第十四章 汽车组装——生成器模式
14.1 汽车组装
时间:12月30日 地点:大B房间 人物:大B,小A
小A:“师兄,汽车是由哪些部件组装成的啊?”
大B:“假定汽车由三个部件组装而成:车身、引擎和轮胎,每个部件的制造以及最后的组装过程都很复杂。一个品牌汽车销售商一般不会自己来完成这些繁琐的过程,而是把它交给汽车零部件制造商。实际上有许多汽车零部件制造商,可以完成不间部件的生产和组装过程。”
小A:“那么,怎么获得一辆汽车的呢?”
大B:“首先,客户需要选定一个汽车制造商(比如某品牌),然后在选择一个汽车销售商(经纪人)为我们服务。然后,我们只需要告诉销售商我们需要哪个制造商的汽车,接下来,由销售商来代替我们监督汽车制造商一步一步为我们生产所需要的汽车(可以考虑订单式生产方式或对某部分特殊定制,如加长车身)。汽车生产完成后,由汽车制造厂提车就可以了。”
14.2 生成器模式
汽车,还有很多部件:车轮、方向盘、发动机还有各种小零件等等,部件很多,但远不止这些,如何将这些部件装配成一辆汽车,这个装配过程也很复杂(需要很好的组装技术),Builder模式就是为了将部件和组装过程分开。
小A:“什么是生成器模式啊?”
大B:“1、当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。2、当构造过程必须允许被构造的对象有不同的表示时。”
小A:“能不能说得简单一点?”
大B:“简单的说,它有点像工厂模式,但是最终生成‘产品’的是Director而非Factory,Director可以使用的builder来生成产品。而builder——生成器则遵循统一的接口,实现不同的内容,从而达到将一个复杂对象的构建与它的表示分离的目标。”
14.3 房屋
大B:“这样吧,我给你举个使用构建房屋的场景来说明‘生成器’吧!首先,这是我们最终需要生成的产品——房屋,它具有房间数量和门数量的属性。”
大B:“接下来就是房屋的真正构建者——‘生成器’的接口定义,以及它的一个实现。”
大B:“这就是所谓的Director——最终构建房屋的‘表示者’。我们需要给它提供‘生成器’,然后由它来构建房屋。”
大B:“最后,当然是我们的测试启动类了,可以看到,使用生成器模式的简单过程就是:1、创建生成器对象。2、表示者使用此生成器对象构建最终产品。”
* * Builder(生成器模式) *
* 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 */ public class Test { public static void main(String[] args) { ConcreteHouseBuilderA myHouseBuilder = new ConcreteHouseBuilderA(); House myHouse = HouseDirector.CreateHouse(myHouseBuilder); System.out.println("My house has room: " + myHouse.getRoomNumber()); System.out.println("My house has door: " + myHouse.getDoorNumber()); } }
14.4 为何使用生成器模式?
小A:“为什么要使用生成器模式?”
大B:“是为了将构建复杂对象的过程和它的部件解耦。注意:是解耦过程和部件。因为一个复杂的对象,不但有很多大量组成部分,如汽车,有很多部件。车轮、方向盘、发动机还有各种小零件等等,部件很多,但远不止这些,如何将这些部件装配成一辆汽车,这个装配过程也很复杂(需要很好的组装技术),Builder模式就是为了将部件和组装过程分开。”
14.5 如何使用生成器模式?
小A:“那应该如何使用生成器模式?”
大B:“首先假设一个复杂对象是由多个部件组成的,Builder模式是把复杂对象的创建和部件的创建分别开来,分别用Builder类和Director类来表示。”
首先,需要一个接口,它定义如何创建复杂对象的各个部件:
用Director构建最后的复杂对象,而在上面Builder接口中封装的是如何创建一个个部件(复杂对象是由这些部件组成的),也就是说Director的内容是如何将部件最后组装成成品:
Builder的具体实现ConcreteBuilder,通过具体完成接口Builder来构建或装配产品的部件,定义并明确它所要创建的是什么具体东西,提供一个可以重新获取产品的接口。
复杂对象:产品Product:
public interface Product { }(www.xing528.com)
复杂对象的部件:
public interface Part { }
我们看看如何调用Builder模式:
14.6 生成器模式的应用
小A:“生成器模式的应如何去应用?”
大B:“在Java实际使用中,我们经常用到‘池’(Pool)的概念,当资源提供者无法提供足够的资源,并且这些资源需要被很多用户反复共享时,就需要使用池。‘池’实际是一段内存,当池中有一些复杂的资源的‘断肢’(比如数据库的连接池,也许有时一个连接会中断),如果循环再利用这些‘断肢’,将提高内存使用效率,提高池的性能。修改Builder模式中Director类使之能诊断"断肢"断在哪个部件上,再修复这个部件。”
14.7 汽车制造
大B:“我给你举个汽车制造作为例子,你应该就可以明白生成器模式的应如何去应用了。以汽车制造作为例子。假定汽车由三个部件组装而成:车身、引擎和轮胎,每个部件的制造以及最后的组装过程都很复杂。一个品牌汽车销售商一般不会自己来完成这些繁琐的过程,而是把它交给汽车零部件制造商。实际上有许多汽车零部件制造商,可以完成不间部件的生产和组装过程。”
小A:“那么,客户是怎么获得一辆汽车的呢?”
大B:“首先,客户需要选定一个汽车制造商(比如某品牌),然后在选择一个汽车销售商(经纪人)为我们服务。然后,我们只需要告诉销售商我们需要哪个制造商的汽车,接下来,由销售商来代替我们监督汽车制造商一步一步为我们生产所需要的汽车(可以考虑订单式生产方式或对某部分特殊定制,如加长车身)。汽车生产完成后,由汽车制造厂提车就可以了。”
如图14-1汽车制造建模
图14-1 汽车制造建模
用面向对象的方法对这个问题建模,得到的类图如图14-2汽车制造类图:
图14-2 汽车制造类图
程序代码如下:
小A:“这种类结构的设计,有什么好处呢?假设我们现在不想要刚才订的奔驰汽车了,而是想换成一辆路虎越野车怎么办?”
大B:“我们只需要告诉经销商,我们想改变汽车制造商就可以了!”
在上面的程序结构中,我们需要做的是:
1、 定义一个路虎汽车及其制造商,实现制造车身、引擎、轮胎和组装的方法:
2、修改客户端调用,把LandRoverBuilder提供给Producer:
OK,你就得到一辆路虎了!这就是GoF说的“将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示”的含义!用同样的方式,你甚至可以得到一辆拖拉机!
大B:“让我们来稍微扩展一下思路,讨论一下扩展问题。我们注意到,在产品这个层次上,每个制造厂直接依赖于具体的汽车,如BenzBuilder生成Benz,在客户端使用时也必须依赖于具体的汽车制造厂BenzBuilder和具体汽车Benz,在抽象制造厂CarBuilder定义中也没有包含GetCar的定义。”
小A:“这是为什么呢?”
大B:“我们想想把Benz这个汽车这个产品也抽象化,定义为Car,具体汽车Benz和LandRover从Car继承,在CarBuider中定义GetCar方法依赖于抽象Car,从而在客户端也可以依赖于抽象。”
大B:“这种做法没错,你把这个结构图画出来就可以看出,这就成了一个典型的工厂模式!实际上,工厂模式和生成器模式是经常引起混淆和困扰的模式,不过仔细体会两种模式的意图就可以发现,他们关注的重点不同。工厂模式的重点在于产品的系列化的生成问题:工厂方法模式解决一种产品细节差异,抽象工厂模式解决多种产品的系列化创建——这些产品可以很简单,也可以比较复杂。但是,这些产品都是整体创建的。而生成器模式关注的是复杂产品的创建细节——如何一步一步的完成复杂产品部件的创建和最后的组装过程。这个创建复杂产品的过程可以差距巨大,所装配出来的产品也可以差距巨大。比如,上面的CarBuilder定义的抽象过程,只要进行合适的扩充,Producer通过使用具体的生成器就可以生产出小汽车、越野车、赛车、拖拉机,甚至任何由车身、引擎和轮胎可以组合出来的产品!正是由于这些产品之间的差异如此巨大,因此无法在抽象的CarBuilder中定义一个GetProduct之类的抽象方法。
我们再来看Producer这个类。它起到的是指导者的作用,指导生成器的使用方法,也就是利用生成器一步步建造出产品的过程。这个过程一般来说是固定的。通过修改Construct()方法,就可以改变产品的建造过程。当然,如果一个产品的建造过程也是系统的变化因素,当然也可以利用类似工厂模式的方法对Producer进行抽象和封装。
最后我们来讨论一下CarBuilder提供给Producer的方式。生成器模式的核心是给督导者一个生成器,但是具体方式并没有限定。可以像本例这样使用聚合的方式,也可以直接把CarBuilder作为参数提供给Construct方法——这并没有什么本质的区别。”
14.8 生成器模式适用场景
小A:“师兄,生成器模式适用什么场景?”
大B:“1、当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。2、当构造过程必须允许被构造的对象有不同的表示时。”
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。