常用设计模式--三种工厂模式
2. 工厂模式
工厂顾名思义就是创建产品,根据产品是具体产品还是具体工厂可分为简单工厂模式和工厂方法模式,根据工厂的抽象程度可分为工厂方法模式和抽象工厂模式。
也就是说,工厂模式可分为简单工厂模式、工厂方法模式、抽象工厂模式三种,这里放在这一起分析。
2.1 简单工厂模式
简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。其UML类图如下:
2.1.1 代码实现
下面我们使用电脑生产来讲解该模式:
Computer类:电脑标准规范类(AbstractProduct)
public abstract class Computer {
/**
* 产品的抽象方法,由具体的产品类去实现
*/
public abstract void make();
}
LenovoComputer类:制造联想电脑(Product1)
public class LenovoComputer extends Computer{
@Override
public void make() {
System.out.println("生产联想电脑");
}
AsusComputer类:制造华硕电脑(Product2)
public class AsusComputer extends Computer {
@Override
public void make() {
System.out.println("生产华硕电脑");
}
}
创建工厂类(Factory)
它提供了一个静态方法createComputer用来生产电脑。你只需要传入你想生产的电脑的品牌,它就会实例化相应品牌的电脑对象:
public class ComputerFactory {
public static Computer createComputer(String type){
Computer mComputer=null;
switch (type) {
case "lenovo":
mComputer=new LenovoComputer();
break;
case "asus":
mComputer=new AsusComputer();
break;
}
return mComputer;
}
}
通常的做法是通过引入配置文件的方式,这里为了减少代码量,所以采用最简便的方式。
客户端调用工厂类
客户端调用工厂类,传入“asus”生产出华硕电脑并调用该电脑对象的make方法:
public class CreatComputer {
public static void main(String[]args){
ComputerFactory.createComputer("asus").make();
}
}
2.1.2 优缺点
优点:
- 使用户根据参数获得对应的类实例,避免了直接实例化类,降低了耦合性。
缺点:
- 可实例化的类型在编译期间已经被确定,如果增加新类型,则需要修改工厂;
- 违背了开放封闭原则(ASD) ;
- 简单工厂需要知道所有要生成的类型,当子类过多或者子类层次过多时不适合使用。
2.1.3 应用场景
JDK类库中广泛使用了简单工厂模式,如工具类java.text.DateFormat,它用于格式化一个本地日期或者时间。
public final static DateFormat getDateInstance();
public final static DateFormat getDateInstance(int style);
public final static DateFormat getDateInstance(int style,Locale locale);
2.2 工厂方法模式
和简单工厂模式中工厂负责生产所有产品相比,工厂方法模式将生成具体产品的任务分发给具体的产品工厂,其UML类图如下:
也就是定义一个抽象工厂,其定义了产品的生产接口,但不负责具体的产品,将生产任务交给不同的派生类工厂,这样不用通过指定类型来创建对象了。
2.2.1 代码实现
接下来使用生产手机的例子来讲解该模式。
产品类
Phone类:手机标准规范类(AbstractProduct)
public interface Phone {
void make();
}
MiPhone类:制造小米手机(Product1)
public class MiPhone implements Phone {
public MiPhone() {
this.make();
}
@Override
public void make() {
// TODO Auto-generated method stub
System.out.println("make xiaomi phone!");
}
}
IPhone类:制造苹果手机(Product2)
public class IPhone implements Phone {
public IPhone() {
this.make();
}
@Override
public void make() {
// TODO Auto-generated method stub
System.out.println("make iphone!");
}
}
工厂类
AbstractFactory类:生产不同产品的工厂的抽象类
public interface AbstractFactory {
Phone makePhone();
}
XiaoMiFactory类:生产小米手机的工厂(ConcreteFactory1)
public class XiaoMiFactory implements AbstractFactory{
@Override
public Phone makePhone() {
return new MiPhone();
}
}
AppleFactory类:生产苹果手机的工厂(ConcreteFactory2)
public class AppleFactory implements AbstractFactory {
@Override
public Phone makePhone() {
return new IPhone();
}
}
客户端演示
public class Demo {
public static void main(String[] arg) {
AbstractFactory miFactory = new XiaoMiFactory();
AbstractFactory appleFactory = new AppleFactory();
miFactory.makePhone(); // make xiaomi phone!
appleFactory.makePhone(); // make iphone!
}
}
2.2.2 优缺点
优点:
- 在工厂方法模式中,工厂方法用来创建客户端所需要的产品,同时还向客户端隐藏了哪种具体产品类将被实例化这一细节,客户端只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名。
- 使用工厂方法模式的另一个优点是在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了。这样,系统的可扩展性也就变得非常好,完全符合“开闭原则”。
缺点:
- 在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。
- 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。
2.3 抽象工厂模式
抽象工厂模式可以说是是工厂方法模式的升级版,当需要创建的产品有多个产品线(产品族)时使用抽象工厂模式是比较好的选择。
上面两种模式不管工厂怎么拆分抽象,都只是针对一类产品Phone(AbstractProduct),如果要生成另一种产品PC,应该怎么表示呢?
最简单的方式是把2中介绍的工厂方法模式完全复制一份,不过这次生产的是PC。但同时也就意味着我们要完全复制和修改Phone生产管理的所有代码,显然这是一个笨办法,并不利于扩展和维护。
抽象工厂模式通过在AbstarctFactory中增加创建产品的接口,并在具体子工厂中实现新加产品的创建,当然前提是子工厂支持生产该产品。其UML类图如下:
2.3.1 代码实现
手机产品类
Phone 抽象产品类:手机标准规范类(AbstractProduct)
public abstract Phone {
void make();
}
MiPhone 具体产品类:制造小米手机(Product1)
public class MiPhone extends Phone {
public MiPhone() {
this.make();
}
@Override
public void make() {
// TODO Auto-generated method stub
System.out.println("make xiaomi phone!");
}
}
IPhone 具体产品类:制造苹果手机(Product2)
public class IPhone extends Phone {
public IPhone() {
this.make();
}
@Override
public void make() {
// TODO Auto-generated method stub
System.out.println("make iphone!");
}
}
电脑产品类
PC 抽象产品类:定义PC产品的抽象类(AbstractPC)
public abstract PC {
void make();
}
MiPC 具体产品类:定义小米电脑产品(MIPC)
public class MiPC extends PC {
public MiPC() {
this.make();
}
@Override
public void make() {
// TODO Auto-generated method stub
System.out.println("make xiaomi PC!");
}
}
MAC 具体产品类:定义苹果电脑产品(MAC)
public class MAC extends PC {
public MAC() {
this.make();
}
@Override
public void make() {
// TODO Auto-generated method stub
System.out.println("make MAC!");
}
}
工厂相关的类
AbstractFactory 抽象工厂类:增加PC产品制造抽象类
public abstract AbstractFactory {
Phone makePhone();
PC makePC();
}
XiaoMiFactory 具体工厂类:增加小米PC的制造(ConcreteFactory1)
public class XiaoMiFactory extends AbstractFactory{
@Override
public Phone makePhone() {
return new MiPhone();
}
@Override
public PC makePC() {
return new MiPC();
}
}
AppleFactory 具体工厂类:增加苹果PC的制造(ConcreteFactory2)
public class AppleFactory extends AbstractFactory {
@Override
public Phone makePhone() {
return new IPhone();
}
@Override
public PC makePC() {
return new MAC();
}
}
demo演示
public class Demo {
public static void main(String[] arg) {
AbstractFactory miFactory = new XiaoMiFactory();
AbstractFactory appleFactory = new AppleFactory();
miFactory.makePhone(); // make xiaomi phone!
miFactory.makePC(); // make xiaomi PC!
appleFactory.makePhone(); // make iphone!
appleFactory.makePC(); // make MAC!
}
}
小总结
看起来抽象工厂模式和工厂方法模式差不多,不过它们的区别在于增加产品上;
工厂方法模式支持新增同一个产品等级的产品,例如已经有Iphone的情况下增加Miphone。
而抽象工厂模式支持新增同一产品族,例如在已经有Iphone,MAC的情况下同时增加MiPhone和MiPC。
前者是每次只增加一个产品,后者是每次增加一组互相有关联的产品(产品族)。
2.3.2 优缺点
优点:
- 抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易。所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。另外,应用抽象工厂模式可以实现高内聚低耦合的设计目的,因此抽象工厂模式得到了广泛的应用;
- 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。这对一些需要根据当前环境来决定其行为的软件系统来说,是一种非常实用的设计模式;
- 增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。
缺点:
-
在添加新的产品对象时,难以扩展抽象工厂来生产新种类的产品,这是因为在抽象工厂角色中规定了所有可能被创建的产品集合,要支持新种类的产品就意味着要对该接口进行扩展,而这将涉及到对抽象工厂角色及其所有子类的修改,显然会带来较大的不便
-
开闭原则的倾斜性
(1) 增加产品族:对于增加新的产品族,抽象工厂模式很好地支持了“开闭原则”,只需要增加具体产品并对应增加一个新的具体工厂,对已有代码无须做任何修改。
(2) 增加新的产品等级结构:对于增加新的产品等级结构,需要修改所有的工厂角色,包括抽象工厂类,在所有的工厂类中都需要增加生产新产品的方法,违背了“开闭原则”。
正因为抽象工厂模式存在“开闭原则”的倾斜性,它以一种倾斜的方式来满足“开闭原则”,为增加新产品族提供方便,但不能为增加新产品结构提供这样的方便,因此要求设计人员在设计之初就能够全面考虑,不会在设计完成之后向系统中增加新的产品等级结构,也不会删除已有的产品等级结构,否则将会导致系统出现较大的修改,为后续维护工作带来诸多麻烦。
2.3.3 三种工厂模式的转换
- 当抽象工厂模式中每一个具体工厂类只创建一个产品对象,也就是只存在一个产品等级结构时,抽象工厂模式退化成工厂方法模式 ;
- 当工厂方法模式中抽象工厂与具体工厂合并,提供一个统一的工厂来创建产品对象,并将创建对象的工厂方法设计为静态方法时,工厂方法模式退化成简单工厂模式。
欢迎访问个人博客:http://www.itle.info/