在“如何生成实例”这一方面,工厂模式的核心内容是通过构建工厂来获取实例,根据“工厂”生产的“产品”不同,可以细分为简单工厂、工厂方法、抽象工厂。
简单工厂讲的是,通过构建一个生成指定类实例的工厂,来获取该类的实例。
获取一个实例时,可能要同时进行初始化、打日志等附加操作,当这些附加操作需要修改时,只需要在工厂类的create方法中修改一次即可,不需要在每个用到产品实例的地方修改。
工厂方法模式构建一个生成实例的工厂,父类工厂决定实例的生成方式,但不决定所要生成的具体的类,具体的处理全部交给子类工厂。目的是将生成实例的框架(framework)和实际负责生成实例的类解耦。
简单工厂是工厂方法在生成的类只有一个时的特例。
使用TeaFactory就能制作茶,如果想制作咖啡,只需要把TeaFactory换成CoffeeFactory即可。
FactoryMethod模式中的角色有四个,分为两方。
Product(产品)
属于“框架”这一方,是一个抽象类,定义了在Factory Method模式中生成的实例所必须持有的接口,但具体的处理由子类Concrete Product角色决定。代码中Drink类扮演此角色。
Creator(创建者)
属于“框架”这一方,是负责创建Product角色的抽象类,但具体的处理由子类ConcreteCreator角色决定。代码中由DrinkFactory类扮演此角色。
Creator角色对具体负责生产实例的角色和具体的产品角色一无所知,它唯一知道的是只要调用创建实例的方法,就能生成Product角色的实例。不用new关键字生成实例,而是调用方法生成实例,这样做可以防止父类和其他具体类耦合。
Concrete Product(具体的产品)
属于“具体加工”这一方,它决定了具体的产品。代码中由Coffee、Tea类扮演此角色。
Concrete Creator(具体的创建者)
属于“具体加工”这一方,负责创建具体的产品,代码中由CoffeeFactory、TeaFactory类扮演此角色。
如果我们想用相同的框架得到其他的产品和工厂,那么只需要添加“具体加工”,而无需对“框架”做任何改动,从而降低了代码耦合。举个例子,如果我们想用相同的框架创建可乐实例,只需要在cola包中编写Cola类和ColaFactory类即可,不需要修改framework包中的任何内容,即framework包不依赖于cola包。
抽象工厂模式中,不仅有“抽象工厂”,还有“抽象零件”和“抽象产品”,或者可以理解为“抽象产品”和“抽象产品族”。换言之,与前面两个工厂只获取一种产品不同,抽象工厂模式会获取多种产品并把他们组装成一个产品族(或者说把多种零件组装成一个产品)。
工厂方法是抽象工厂的产品族只有一种产品时的特例。
和工厂方法类似的是,抽象工厂模式的目的是将组装零件的框架(framework)和实际的零件类解耦。即,抽象工厂把抽象零件组装成抽象产品,具体工厂把具体零件组装成具体产品。在抽象工厂中,我们不关注零件的具体实现,而是只关心接口,我们仅使用接口获取零件并将零件组装成产品。
另一个fastmeal包和westmeal包类似。
使用WestMealFactory可以制作咖啡、曲奇并组装成西餐,只需要把WestmealFactory换成FastMealFactory即可制作可乐、炸鸡并包装成快餐。
易于增加新的产品族
在抽象工厂模式中增加新的产品族是非常容易的,这里说的“容易”指的是需要编写哪些类和实现哪些方法是非常清晰的:新增一个包,编写Drink、Food、Meal、MealFactory的子类,并实现其抽象方法。也就是说,只要把framework包中的抽象部分全部具体化即可。
这样一来,无论要增加多少个产品族,都无需修改抽象工厂,调用工厂的时候也只需要更换一下具体的工厂类即可。
难以增加新的产品
在抽象模式种增加新的产品(或者说新的零件)是非常困难的,加入我们要在framework包中引入表示赠品的Gift产品,那么必须在每个具体产品族中做一下修改:新增Gift实现类;MealFactory实现类中实现createGift方法。已经编写玩的产品族越多,修改的工作量就越大。
简单工厂通过一个具体的工厂获取一个具体的实例,目的是让获取实例的附属操作与实例的调用解耦;
工厂方法通过父类工厂获取父类实例,通过具体工厂获取具体实例,目的是让生成实例的框架与具体生成的类解耦;
抽象工厂通过抽象工厂把抽象零件组装成抽象产品,通过具体工厂把具体零件组装成具体产品,目的是让组装零件的框架与具体组装的零件类解耦。