工厂模式属于类的创建模式,工厂模式专门负责将大量有共同接口的类实例化。工厂模式可以动态决定将哪一个类实例化,而不必事先知道每次要实例化哪一个类。工厂模式有三个基本形态:
1. 简单工厂(Simple Factory)模式,又称静态工厂方法模式(Static Factory Method
Pattern)。
2. 工厂方法(Factory Method)模式,又称多态性工厂(Polymorphic Factory)模式或虚拟构造子(Virtual Constructor)模式。
3. 抽象工厂(Abstract Factory)模式,又称工具箱(Kit 或Toolkit)模式。
我们用图例来理解简单工厂模式的基本语义。(以下所有图示只是理解的代表形式,为了表达清楚,在某些方面可能违背建模图示法中的基本语义)。
图1
简单工厂模式的掌握,对学习单例模式和多例模式起着非常重要的作用。
图1表示农场的水果种植情况,很明显,我们可知:Fruit接口类有三个方法(plant,harvest,grow);由于Apple和Grape类实现了Fruit接口,所以必须实现Fruit类中所有的方法,其中Apple 和Grape各自有相关的方法。至于他们之间的关系,代码我就不列出。
农场的园丁(FruitGardener )也是系统的一部分,自然要由一个合适的类来代表。这个类就是FruitGardener 类,FruitGardener 类会根据客户端的要求,创建出不同的水果对象,比如苹果(Apple)、葡萄(Grape)的实例。而如果接到不合法的要求,FruitGardener 类会抛出BadFruitException 异常。
/*********************以下为农场的园丁类*********************/
public class FruitGardener
{
/**
* 静态工厂方法
*/
public static Fruit factory(String which) throws BadFruitException
{
if (which.equalsIgnoreCase("apple"))
{
return new Apple();
}
else if (which.equalsIgnoreCase("grape"))
{
return new Grape();
}
else
{
throw new BadFruitException("Bad fruit request");
}
}
}
/*********************以下为自定义异常类*********************/
public class BadFruitException extends Exception
{
public BadFruitException(String msg)
{
super(msg);
}
}
/*********************用户调用*********************/
………………
try
{
FruitGardener.factory("grape");
FruitGardener.factory("apple");
FruitGardener.factory("xxx");
}
catch(BadFruitException e)
{
...
}
…………………
|
由此可见, 简单工厂涉及到三个角色:
1. 工厂类角色:负责创建产品的对象。如上面的FruitGardener类。它是工厂方法模式的核心。它往往由一个具体的类实现。
2. 抽象产品角色:实现了产品的共同接品,它可以由抽象类或接口实现。如上面的Fruit
3. 具体的产品:某一具体的产品,如上面的Apple和Grape
注意
1. 厂方法的中的工厂角色也可以由抽象产品角色扮演。即抽象产品角色担任了对象的创建。在java API中如:java.text.DateFormat 此时,抽象产品角色被定义成一个抽象的类,而不是一个接口。(抽象类与接口之间的区别与应用,在后面我会继续列出相关内容)。一个抽象类不能有自己的实例, DateFormat 的工厂方法是静态方法,并不是普通的方法。也就是说,它直属于类本身,不通过类的实例化。
图2
2. 如果抽象产品角色被省略,则工厂角色可以与具体产品角色合并。
public class FruitGardener
{
public FruitGardener (){}
/**
* 静态工厂方法
*/
public static FruitGardener factory()
{
return new FruitGardener ();
}
}
|
以上两点类似单例模式和多类模式,但不等于单例或多例模式。在后面模式设计中,会提到它,先别急。这是简单工厂模式的一种退化现象。如下代码:
简单工厂模式与其它模式的关系
1. 与单例模式的关系
单例模式使用了简单工厂模式。换言之,单例类具有一个静态工厂方法提供自身的实例。一个抽象产品类同时是子类的工厂。(如图2所示)
但是单例模式并不是简单工厂模式的退化情形,单例模式要求单例类的构造方法是私有的,从而客户端不能直接将之实例化,而必须通过这个静态工厂方法将之实例化,而且单例类自身是自己的工厂角色。换言之,单例类自己负责创建自身的实例。单例类使用一个静态的属性存储自己的惟一实例 ,工厂方法永远仅提供这一个实例。(说到这里,我简单有点兴奋,终于知道 Sun提供的javaAPI中为什么会采用这些模式 )
2. 与多例模式的关系
多例模式是对单例模式的推广。多例模式与单例模式的共同之处在于它们都禁止外界直接将之实例化,同时通过静态工厂方法向外界提供循环使用的自身的实例。它们的不同在于单例模式仅有一个实例,而多例模式则可以有多个实例。
多例模式往往具有一个聚集属性,通过向这个聚集属性登记已经创建过的实例达到循环使用实例的目的。一般而言,一个典型的多例类具有某种内部状态,这个内部状态可以用来区分各个实例;而对应于每一个内部状态,都只有一个实例存在。
注 :聚集表示类之间的关系是整体与部分的关系,它分:共享聚集和组成;共享聚集中的部分可以是多个整体的一部分;而组成 仅 表示整体与部分关系,整体消失,则部分也消失。需要注意的是 ,一些面向对象大师对聚集的定义并不一样。
其它理解多例模式,并不困难,我们可以通过javaAPI来理解,尤其是聚集关系。
根据外界传入的参量,工厂方法可以查询自己的登记聚集,如果具有这个状态的实例已经存在,就直接将这个实例提供给外界;反之,就首先创建一个新的满足要求的实例,将之登记到聚集中,然后再提供给客户端。
关于单例模式和多例模式详细的细节,在后面会描述。
3. 备忘录模式
单例和多例模式使用了一个属性或者聚集属性来登记所创建的产品对象, 以便可以通过查询这个属性或者聚集属性找到和共享已经创建了的产品对象。这就是备忘录模式的应用。
4. MVC 模式
MVC 模式并不是严格意义上的设计模式,而是在更高层次上的架构模式。 MVC 模式可以分解成为几个设计模式的组合,包括合成模式、策略模式、观察者模式,也有可能会包括装饰模式、调停者模式、迭代子模式以及工厂方法模式等。
简单工厂模式所创建的对象往往属于一个产品等级结构,这个等级结构可以是MVC模式中的视图(View);而工厂角色本身可以是控制器(Controller)。一个MVC 模式可以有一个控制器和多个视图,如下图所示。
图3
我们结合简单工厂方法,可知上图中的Controller(控制器)也就是工厂角色,(FruitGardener)它负责创建产品View(视图)。如果系统需要有多个控制器参与这个过程的话,简单工厂模式就不适用了,应当考虑使用工厂方法模式,我们以后描述它。
简单工厂方法的应用
使用静态工厂方法是为了将具体子类实例化的工作隐藏起来,从而客户端不必考虑如何将具体子类实例化,因为抽象类DateFormat 会提供它的合适的具体子类的实例。这是一个简单工厂方法模式的绝佳应用。
利用具体产品类的超类类型将它的真实类型隐藏起来,其好处是提供了系统的可扩展性。如果将来有新的具体子类被加入到系统中来,那么工厂类可以将交给客户端的对象换成新的子类的实例,而对客户端没有任何影响。
这种将工厂方法的返还类型设置成抽象产品类型的做法,叫做针对抽象编程,这是依赖倒转原则(DIP)的应用。我们后面会描述“依赖倒转原则(DIP)”的具体应用。
该模式的缺点
大家不难看出,如果新增加产品如:orange,则需要修改工厂类(FruitGardener),从而限制了其灵活性;而对于产品角色类适合,因此简单工厂只在有限的程度上支持“开–闭”原则。
工厂类集成了所有的创建工作,显得工厂类负任比较得,万一出问题,所有的创建工作将不能进行。当产品类有不同的接口种类时,工厂类需要判断在什么时候创建某种产品。这种对时机的判断和对哪一种具体产品的判断逻辑混合在一起,使得系统在将来进行功能扩展时较为困难。这一缺点在工厂方法模式中得到克服。
由于简单工厂模式使用静态方法作为工厂方法,而静态方法无法由子类继承,因此,工厂角色无法形成基于继承的等级结构。这一缺点会在工厂方法模式中得到克服。
分享到:
相关推荐
Android中的LayoutInflater就是一个典型的工厂模式应用,它负责将XML布局文件转换为视图对象。 3. 构造器模式(Builder模式):将复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。在Android...
此资源出自下面的作者,我只是转载,非常实用的设计方法,如果您想成位出色的设计师,那就再复习复习吧!如果您想成为软件设计师,通过学习,您将会站另一个高度看待软件设计. 原始地址:...
设计模式之 Factory(工厂方法和抽象工厂) 使用工厂模式就象使用 new 一样频繁. 设计模式之 Builder 汽车由车轮 方向盘 发动机很多部件组成,同时,将这些部件组装成汽车也是一件复杂的工作,Builder 模式就是将这...
1. 工厂方法模式(Factory Method):定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法把类的实例化推迟到子类中进行。 适用场景:当一个类无法预测它需要创建哪个类的对象时;当一个类希望由...
设计模式在软件工程中扮演着至关重要的角色,它们提供了解决常见设计问题的模板,使得代码更易于理解、维护和扩展。在J2EE领域,以下是一些常用的设计模式及其应用场景: 1. **Session Facade Pattern**:此模式...
浙江天地环保工程有限公司的核心业务是采用EPC(Engineering, Procurement, Construction)总承包模式进行脱硫工程。这种模式是指工程公司在项目设计、采购和施工阶段提供一站式服务,对项目的整体质量、进度和成本...
根据提供的标题“转载 天线原理与设计”以及描述中的简略信息,我们可以推断出这篇文章主要关注的是天线的工作原理及其设计方法。虽然提供的部分内容似乎并没有直接包含具体的知识点,但基于标题和描述,我们可以...
综上所述,桥式起重机的智能转载系统设计技术研究涵盖了多学科领域,包括自动化控制、传感技术、通信技术、数据分析以及机械工程。这样的系统不仅提高了工作效率,也极大地提升了作业安全性,是未来起重机发展的重要...
ARM公司通过授权模式,使得许多半导体厂商能够生产基于ARM架构的芯片,这极大地推动了ARM的普及。在嵌入式系统中,ARM通常与微控制器(如51、AVR、PIC)、DSP(数字信号处理器)和FPGA(现场可编程门阵列)共同存在...
5. **设计模式**:系统可能应用了诸如工厂模式、单例模式、观察者模式等设计模式,以实现良好的代码结构和可扩展性。 6. **权限控制与安全**:考虑到教务管理系统涉及敏感信息,系统可能包含了用户角色、权限分配等...
标题“hook更改硬盘序列号 转载”涉及到的是一个技术话题,主要关于如何通过编程技术来模拟或改变硬盘的物理序列号(SN)。在IT领域,硬盘序列号是硬盘制造商赋予每个硬盘的一个唯一标识符,通常用于追踪设备、验证...
1)精简TCP/IP 协议栈,以减小代码量。ZLIP 目前没有支持UDP 协议,ICMP 协议 也只支持其中的echo 协议(响应ping 数据包)。lwIP 是一个功能全面的TCP/IP 协 议栈,但是相对51 来说代码量较大。...模式下编译。
spi简单仿真,vivado工程,包含rtl文件和tb文件。SPI 接口是 Motorola 首先提出的全双工三线同步串行外围接口,采用主从模式(MasterSlave)架构;支持多 slave 模式应用,一般仅支持单 Master。时钟由 Master 控制...
- 设计模式:如工厂模式、观察者模式等,它们在解决常见问题时提供了一种标准解决方案。 - 无状态业务层:理解其含义并讨论长事务的处理方式。 - 架构图:如用例图、类图、序列图等,用于表示系统组件及其相互...
随着技能的积累,开发者会逐步接触软件工程实践,如需求分析、设计模式、版本控制、项目管理。需求分析要求开发者理解用户需求,转化成可实施的技术规格。设计模式是解决常见问题的标准化解决方案,能提高代码的...
江西农业大学计算机与信息工程学院的毕业论文设计探讨了音乐在网上的盈利模式这一主题,旨在分析当前网络音乐产业面临的挑战与机遇,以及如何通过创新商业模式实现盈利。这篇论文由学生姜大伟撰写,指导教师为杨有亮...
- 《漫谈设计模式:从面向对象开始》:介绍了设计模式,对于软件设计至关重要,尤其是单例、工厂和代理模式。 - 《Spring 3.0就这么简单》:适合初学者快速掌握Spring框架的入门书籍。 - 《Java并发编程实战》:...
良好的设计模式,如单例模式(用于控制播放器实例的数量)和工厂模式(用于创建音频对象)也可能被应用。 最后,考虑到“很实用”,这个音乐播放器可能还包括了一些高级特性,比如音轨转换、歌词同步显示、网络流...
9. **设计模式**:在实现大型网络应用时,设计模式如工厂模式、观察者模式和状态机模式等可以帮助构建可扩展和可维护的代码。 10. **网络库**:为了简化网络编程,有许多优秀的C++库可供使用,如Boost.Asio、Poco库...
- **лMENU+5+1/2**:这可能是一个用于进入某些诺基亚手机工程模式或特殊设置菜单的组合键。 - **T190arempt:20010903** 和 **T191arempt:199807223X8/2X88/998/8088/L2000/7689/T189/C289**:这些序列号或代码...