前言
在软件设计过程中,我们总是需要创建很多对象,而且系统越庞大,创建的对象越复杂。而今天我们将讨论的就是解决对象创建时的难题--工厂类模式。为了贴近工厂这个词,我们采用工厂建造汽车这个例子来阐明工厂类模式的演变和什么场景下使用什么模式。
场景1、:一位顾客要开车从上海到苏州,他需要一辆汽车,于是他自己组装汽车,给车装轮胎、导航仪、车灯等。
问题:1、显然,顾客只是想拥有一辆汽车,他不想知道怎么去买汽车,更不想知道怎么组装,然后还要给汽车上漆。
2、如果他想换个型号的汽车,他得重新来遍组装汽车。
用代码可以表示为:
首先我们需要一个汽车类
然后是客户端参与制作汽车了
如果事实真的是这样,估计顾客要吐血了,他只是想有辆车去苏州,自己制作车太变态了,所以,他断然不会这样。如果他想要一辆其他型号的汽车,他需要改整个代码。
这违反了设计模式的两个原则:迪米特法则和开闭原则。客户端只需要一辆车,秉承最小知道原则,顾客只需要得到一辆已经喷好漆、装好空调的汽车。同时,对修改关闭也无法做到,如果顾客想换个型号的汽车,他需要修改大量的代码。
这个场景现实肯定不会出现,现实是顾客会从一个工厂(公司)获得一辆汽车。
场景2、上面那位顾客学聪明了,他从一个生产宝马汽车的工厂购买了一辆汽车。
解决问题:屏蔽了汽车具体的建造细节,顾客再也不需要去学喷漆、装发动机啦。
用代码表示如下:
Car类还是不变,只是新增了个工厂类,把以前客户端做的什么装发动机的代码让工厂来完成。
客户端代码直接调用工厂类,获得汽车,然后开始旅行就行了
由上面的代码可以清晰的看到,客户端对对象构建的具体细节,比如喷漆、安装发动机都不知道,只是拿到了汽车,并可以驾驶她。这已经满足了它的需求。而且如果想换一辆汽车,客户端代码不需要任何修改。
此场景中,运用到的设计模式是简单工厂模式。
意图:屏蔽客户端对象实例化的具体细节;提供一个普通的创建对象的接口。
场景3、突然有一天,顾客赚了1个亿,他告诉厂长他需要买很多汽车,可能是奔驰、或者是宝马,也可能是法拉利。
面对问题:1、如何在保证原来生产线的正常运转的前提下,生产奔驰和法拉利.
解决方法:1、厂长想,如果把3种汽车都放在一条生产线生产,那肯定不好,万一新增的改动影响了以前的生产宝马的生产线,就完蛋了(我们在写代码的时候,是不是经常简单的修改原来的代码,却是冒着影响原来功能的风险呢?)。所以他新增了2个工厂,分别生产奔驰和法拉利。而且,如果以后需要生产新的车型,也直接新增一个工厂,只要遵守“抽象工厂”和顾客的约定即可,而不用担心影响原来汽车的生产。
面对问题:2、现在有3个工厂,有3个厂长,如果顾客想买宝马,就要找宝马厂的厂长,买奔驰就要找奔驰厂的厂长。顾客肯定不愿意,没准一气之下不买了。
解决方法:提供一个“抽象厂长”,他只和顾客协商,具体汽车的建造,由具体厂长去负责。
用代码表示为:
定义了一个汽车的接口ICar,定义了汽车可以提供的服务,包括喷漆,装空调,驾驶等。
上面的Car类稍微改动了下,就是实现了ICar接口。
客户端代码为:
然后再定义一个“抽象厂长”,负责和顾客约定购车协议。
再定义两个工厂类具体生产奔驰和宝马,他们遵循“抽象厂长”和顾客的约定,就是造车。
客户端类如下:
现在可以看到,顾客代码已经变得很简洁了,而且工厂能快速新建各个工厂来生产汽车而不影响原来的汽车生产,这在快速变化的汽车行业中是很好的,而且提供给顾客的服务也越来越简洁,这样的企业,自然竞争力很强。
这里用到的设计模式为工厂方法模式:
意图:定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类。
使用场景:1、当客户程序不需要知道要使用对象的创建过程。
2、当一个类不知道它所必须创建的对象的类的时候。
3、当一个类希望由它的子类来指定它所创建的对象的时候。
4、客户程序使用的对象存在变动的可能,或者根本就不知道使用哪一个具体的对象。
场景4、随着汽车行业的蓬勃发展,竞争也越来越激烈,为了在竞争中生存,厂长决定为各个型号的汽车安装一个智能导航系统,但宝马只能安装宝马导航,奔驰只能安装奔驰自己的导航。
解决方法:厂长决定把导航仪的生产放到各个汽车工厂一起生产。
具体类如下:
新增一个导航仪的接口和实现类
新增一个抽象工厂和实现类
客户端类为
这里用到的设计模式是抽象工厂模式:
意图:为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类。
使用场景: 1、当需要创建的对象是一系列相互关联或相互依赖的产品族时,便可以使用抽象工厂模式。
2、一个系统要由多个产品系列中的一个来配置时。
3、当你要强调一系列相关的产品对象的设计以便进行联合使用时。
场景5、随着汽车的组装越来越复杂,工厂已经不能适应外界快速的变化了,所以厂长决定工厂只是生产汽车需要的部件,由一个专门的工厂负责组装。通常汽车部件是比较固定的,但组装流程较易变,把易变的分离出去,把不变的保留,这样能更好的应对外界的变化。而且,还能使用不同的部件,建造各样的汽车。
代码为:
相对于抽象工厂,我们只增加了一个director类,负责汽车制造的复杂逻辑。
客户端只需要和导演类通信即可。
这里用到的模式为建造者模式
意图:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
总结
上面介绍的几个模式都是秉承了“把易变的部分抽离出来,并用抽象代替具体实现”,因为具体是易变的,抽象是不变的。所以我们在代码设计的过程中,如何抽象,如何化易变为不变是很重要的。同时,要记住“最小知道原则”,如果客户端知道的越多,那代码的约束越多,面对变化的能力也越弱。
参考文献
设计模式(一)工厂模式Factory(创建型)
《设计模式-可复用面向对象软件的基础》