写在前面
看了一下自己的博客,从15年开通(那时候大三)到现在(刚刚毕业)已经1年半了,大三开始就一直忙着各种比赛,靠熟人朋友接一些小项目,然后去年大四一年就来杭州实习,开发一个新的物流项目,然后从长沙到杭州,也遇到了很多老朋友,然后经常出去玩一玩就把博客放一边了,今天回过头来看,发现当年要写的面向对象设计模式的系列居然还一直没有更新,觉得深感惭愧,虽说这类型的在网上已经不少了,但总归来说对自己未来的发展还是有帮助的,所以打算还是坚持下来,继续写下去了。
?
言归正传,今天要介绍的是设计模式中的抽象工厂模式,属于创建型模式。在工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法也具有唯一性,一般情况下,一个具体工厂中只有一个工厂方法或者一组重载的工厂方法。但是有时候我们需要一个工厂可以提供多个产品对象,而不是单一的产品对象。这个时候工厂方法模式就显得不那么好用了。
?
一、抽象工厂模式动机
为了更清晰地理解抽象工厂模式,需要先引入两个概念:
1.产品等级结构:产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。
2.产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中。?
产品族与产品等级结构的关系可用下面这张图来说明:
?这个时候我们再来看一个例子,还是上一篇工厂方法模式文章中电视机那个例子,假设海尔公司除了生产电视机外,还生产空调,TCL公司也可以生产空调,这个时候如果使用工厂方法模式的话,就不能把就得再来个新的空调产品工厂,而这两个工厂(电视机工厂和空调工厂)彼此间没有联系,而实际情况是,空调和电视都是由海尔生产或者海信生产的,这时候抽象工厂模式的优势就来了。
?
二、抽象工厂模式定义
抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式。
?
三、抽象工厂模式的结构
抽象工厂模式包含如下角色:
AbstractFactory:抽象工厂
ConcreteFactory:具体工厂
AbstractProduct:抽象产品
Product:具体产品
?
?
四、模式分析
抽象工厂模式需要具备以下几点:
1.当系统所提供的工厂所需生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构中属于不同类型的具体产品时需要使用抽象工厂模式。
2.抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形态。
3.抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建 。当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、有效率。
抽象工厂模式示意图如下:
?
五、实例分析
还是拿电器厂的例子来说,一个电器工厂可以产生多种类型的电器,如海尔工厂可以生产海尔电视机、海尔空调等,TCL工厂可以生产TCL电视机、TCL空调等,相同品牌的电器构成一个产品族,而相同类型的电器构成了一个产品等级结构,现使用抽象工厂模式模拟该场景。
分析该场景我们可以知道,海尔和TCL是具体的工厂,所以需要有一个共有的抽象工厂出来,同时每个工厂都可以生产电视和空调,所以电视和空调就是每个工厂的产品族,而海尔的电视和TCL的电视又构成了产品的等级结构,所以需要电视和空调的共有抽象类。
1.抽象产品类——电视(接口)
?
class="Television.java">public interface Television { public void show(); }? 2.抽象产品类——空调(接口)
?
?
public interface AirConditioner { public void changeTemperature(); }? 3.用于生产一个产品族的抽象工厂类(接口)
?
?
public interface EFactory { public Television produceTelevison(); public AirConditioner produceAirConditioner(); }? 4.具体的产品类——海尔电视、海尔空调、TCL电视、TCL空调
?
?
public class HaierTelevision implements Television { @Override public void show() { System.out.println("我是海尔电视"); } }?
public class HaierAirConditioner implements AirConditioner { @Override public void changeTemperature() { System.out.println("海尔空调调整温度了"); } }?
public class TCLTelevision implements Television { @Override public void show() { System.out.println("我是TCL电视"); } }?
public class TCLAirConditioner implements AirConditioner { @Override public void changeTemperature() { System.out.println("TCL空调调整温度了"); } }?
?
5.具体的工厂类——海尔产品族生产车间和TCL产品族生产车间
?
public class HaierFactory implements EFactory { @Override public Television produceTelevison() { return new HaierTelevision(); } @Override public AirConditioner produceAirConditioner() { return new HaierAirConditioner(); } }?
public class TCLFactory implements EFactory { @Override public Television produceTelevison() { return new TCLTelevision(); } @Override public AirConditioner produceAirConditioner() { return new TCLAirConditioner(); } }? 6.创建客户端进行测试
?
?
public class Test { public static void main(String[] args) { // 声明一个抽象工厂 EFactory eFactory; // 声明一个抽象的产品族,即:声明一个电视机抽象类和一个空调抽象类 Television tv; AirConditioner airConditioner; // 创建具体的工厂子类,如海尔生产这个产品族,并向上转型给父类 eFactory = new HaierFactory(); // 调用生产的方法 tv = eFactory.produceTelevison(); airConditioner = eFactory.produceAirConditioner(); // 调用产品自己的方法 tv.show(); airConditioner.changeTemperature(); } }?7.模式优势
?
通过以上6点,我们用抽象工厂模式设计了一个模拟电视和空调产品族的场景,现在假设我们需要更改产品族,不再使用海尔(即第6点中的HaierFactory对象),而是需要改成TCL来生产产品族,此时,我们只需要把具体的工厂类替换成TCLFactory即可,具体可看以下代码:
?
public class Test { public static void main(String[] args) { // 声明一个抽象工厂 EFactory eFactory; // 声明一个抽象的产品族,即:声明一个电视机抽象类和一个空调抽象类 Television tv; AirConditioner airConditioner; // 创建具体的工厂子类,如TCL生产这个产品族,并向上转型给父类 eFactory = new TCLFactory(); // 调用生产的方法 tv = eFactory.produceTelevison(); airConditioner = eFactory.produceAirConditioner(); // 调用产品自己的方法 tv.show(); airConditioner.changeTemperature(); } }可以发现,此时我们只需要改eFactory = new HaierFactory();这一行代码,将HaierFactory()改成TCLFatory()即可。如果使用java的反射机制和XML文档的结合,还可以这么写:
?
tvf =?(EFactory)XMLUtil.getBean(); //getBean()的返回类型
这样一来只需要修改XML中的名字,连java文件都不需要改动,很好的符号了“开闭原则”。
?
六、抽象工厂模式缺点
当一个产品族中的多个对象被设计成一起工作时,抽象工厂模式能够保证客户端始终只使用同一个产品族中的对象。这对一些需要根据当前环境来决定其行为的软件系统来说,是一种非常实用的设计模式。但是,在添加新的产品对象时,难以扩展抽象工厂来生产新种类的产品,这是因为在抽象工厂角色中规定了所有可能被创建的产品集合,要支持新种类的产品就意味着要对该接口进行扩展,而这将涉及到对抽象工厂角色及其所有子类的修改,显然会带来较大的不便。具体如下:
(1) 增加产品族:对于增加新的产品族,工厂方法模式很好的支持了“开闭原则”,对于新增加的产品族,只需要对应增加一个新的具体工厂即可,对已有代码无须做任何修改。
(2) 增加新的产品等级结构:对于增加新的产品等级结构,需要修改所有的工厂角色,包括抽象工厂类,在所有的工厂类中都需要增加生产新产品的方法,不能很好地支持“开闭原则”。
?
抽象工厂模式的这种性质称为“开闭原则”的倾斜性,抽象工厂模式以一种倾斜的方式支持增加新的产品,它为新产品族的增加提供方便,但不能为新的产品等级结构的增加提供这样的方便。
最后,当抽象工厂模式中每一个具体工厂类只创建一个产品对象,也就是只存在一个产品等级结构时,抽象工厂模式退化成工厂方法模式;当工厂方法模式中抽象工厂与具体工厂合并,提供一个统一的工厂来创建产品对象,并将创建对象的工厂方法设计为静态方法时,工厂方法模式退化成简单工厂模式。
?
谢谢您的关注和阅读,文章不当之处还请您不吝赐教~~~