本文是对面向对象设计模式--工厂方法模式(FactoryMethod)和抽象工厂模式(AbstractFactory)的解析,主要分为定义解析、简单工厂方法讲解、工厂方法模式讲解、抽象工厂模式讲解、多案例练习加深对工厂方法模式与抽象工厂模式的理解、最后总结知识要点。
第一篇:定义解析
工厂方法模式和抽象工厂模式是GoF四人帮整理的《设计模式-可复用面向对象软件基础》一书中23种设计模式中归类为创建型模式中的设计模式,23种设计模式根据它们的用途分为三大类:5种创建型模式、7种结构型模式、11种行为型模式。
引自GoF四人帮对工厂方法模式(Factory Method)的定义与类图:
定义一个用于创建对象的接口,让子类决定将哪个子类实例化。Factory Method使一个类的实例化延迟到其子类。
解析:定义一个用于创建对象的接口,让子类决定将哪个子类实例化;工厂方法模式其作用就是用于创建对象的,我们将要创建的对象称为产品,创建对象的方法称为工厂方法,工厂方法模式就像一个工厂生产产品一样;
一个产品可能会有许多不同的子类产品,如一个产品有不同品牌的产品,在同一个品牌的产品中又会分为不同价格档次的产品,如果一个产品有3个品牌系列,每个品牌内的产品又分为4个不同的价格档次的产品,这样具体的产品子类将会有3*4=12个具体的子产品,如果全部把这些具体的子类的实例化都由一个类或一个方法中决定,这会让系统缺乏弹性,这个方法依赖的子类太多,一旦需要添加新的子类或删除某个子类都需要重新打开该方法,然后进行修改。
工厂方法模式把创建对象的方法工厂方法进行抽象,定义为一个接口,让子工厂类来实现具体创建产品的工厂方法,如每一个品牌由一个对应品牌子工厂类,由其创建不同计费档次的产品对象,如果有3个品牌,每个品牌有4个价格档次的产品,只需要3个子工厂类,每个子工厂类都创建对应品牌的不同价格档次的产品。当有新的品牌加入时,只需要实现一个新的子工厂类即可。
工厂方法模式使得一个类的实例延迟到其子类;通过利用面向接口编程,客户只需要指定具体的子工厂类和具体产品类型标识,即可使用抽象的工厂方法和抽象的产品创建具体的子产品对象,产品对象的实例化工作由指定的具体子工厂类中实现。
抽象工厂模式(Abstract Factory):提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
解析:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类;抽象工厂模式从定义上看,其目的是用于创建一系列相关的对象的,也就是用于创建一个产品族的模式;
如组装一台电脑会遇到不同品牌的主板,需要相应品牌的芯片才能进行匹配,如选择Intel的主板就必须使用Intel的CPU、显卡等,如果选择的是AMD的主板就必须使用AMD的CPU、显卡等。主板、CPU、显卡这些属于不同的产品,它们存在相互依赖的关系,属于同一个产品族。为了方便创建在一个产品族中的产品,把创建这一系列对象的接口放在同一个抽象工厂中,由具体子产品族抽象工厂实现类来决定创建的产品对象,如Intel的抽象工厂实现类创建Intel的主板、CPU、显卡等,AMD的抽象工厂实现类创建AMD的主板、CPU、显卡等。
第二篇:简单工厂方法讲解
基础需求:有一个披萨店,有多种不同的披萨,有CheesePizza(奶酪披萨)、GreekPizza(希腊披萨)、PepperoniPizza(腊肠披萨)等。
每一种披萨的制作过程都有如下步骤:
1)做一些准备工作,如面团、酱料、芝士等。2)烘烤 3)切片4)装盒
现在需要为披萨店实现一个生成披萨的系统。
第一次实现尝试:直接创建披萨的具体实现
Pizza orderPizza(String type){
Pizza pizza;
//根据披萨的类型,实例化具体的类型。
if(type.equals("cheese")){
pizza = new CheesePizza(); //实现Pizza接口
}else if(type.equals("greek")){
pizza = new GreekPizza();
}else if(type.equals("pepperoni")){
pizza = new PepperoniPizza():
}
//烘烤披萨的准备(擀面皮、加佐料)
pizza.prepare();
pizza.bake(); //烘烤
pizza.cut(); //切片
pizza.box(); //装盒
return pizza;
}
第一次需求变更:现在我们需要根据不同类型披萨的销售情况,将销量不好的披萨下架,同时将新风味的披萨上线。我们需要去掉GreekPizza并增加ClamPizza(蛤蜊披萨)和VeggiePizza(素食披萨)。
根据需求变更修改实现:
Pizza orderPizza(String type){
Pizza pizza;
//随着时间的变化,披萨种类会发生变化
if(type.equals("cheese")){
pizza = new CheesePizza(); //实现Pizza接口
} else if(type.equals("pepperoni")){
pizza = new PepperoniPizza():
} else if(type.equals("clam")){
pizza = new ClamPizza();
}else if(type.equals("veggie"){
pizza = new ViggePizza();
}
//下面是我们不想要改变的地方。只有发生这些动作的披萨会改变。
pizza.prepare(); //烘烤披萨的准备(擀面皮、加佐料)
pizza.bake(); //烘烤
pizza.cut(); //切片
pizza.box(); //装盒
return pizza;
}
缺点:1、代码绑着具体类,造成紧耦合,会导致代码更脆弱、更缺乏弹性。
2、一旦有扩展或变化,就需要重新打开这段代码进行检查和修改。通常这样修改过的代码将会造成部分系统更难维护和更新,而且更容易犯错。
优化方法:封装变化原则,找出会变化的部分,把它们从不变化的部分分离出来。
第二次实现尝试:简单工厂方法
public class SimplePizzaFactory{
public static Pizza createPizza(String type){
Pizza pizza;
if(type.equals("cheese")){
pizza = new CheesePizza(); //实现Pizza接口
} else if(type.equals("pepperoni")){
pizza = new PepperoniPizza():
} else if(type.equals("clam")){
pizza = new ClamPizza();
}else if(type.equals("veggie"){
pizza = new ViggePizza();
}
return pizza;
}
}
简单工厂:将创建对象的代码提取到一个单独的类中,把创建对象的逻辑封装起来并提供一个简单的对外接口,称为简单工厂。简单工厂并不是一种设计模式,只是一种编程习惯。
PizzaStore是Pizza工厂的客户,它通过SimplePizzaFactory取得披萨的实例。
SimplePizzaFactory是创建披萨的工厂,createPizza()一般为静态的。
Pizza是我们的产品,定义为抽象类,具有一些有用的实现,这些实现可以被覆盖,如具体的披萨类ClamPizza等。
第三篇:工厂方法模式讲解
第二次需求变更:现在有几个来在不同地方的披萨店想要加盟我们的披萨店,有来自纽约、芝加哥、加州等地的披萨店。1)我们希望让所有披萨店的制作流程是一样的。 2)各地的加盟店想要在制作流程中加入自己的特色。
需求分析:制作流程是一样的,即统一的披萨超类。各地加盟商要有自己的特色,即每一个加盟商都要有各自对应的披萨类。我们希望建立一个框架,把加盟店和创建披萨捆绑在一起的同时又要保持一定的弹性。
采用简单工厂方法实现:
public class SimplePizzaFactory{
public Pizza createPizza(String type){
Pizza pizza;
if(type.equals("NYcheese")){
pizza = new NYCheesePizza(); //实现Pizza接口
} else if(type.equals("NYpepperoni")){
pizza = new NYPepperoniPizza():
} else if(type.equals("NYclam")){
pizza = new NYClamPizza();
}else if(type.equals("NYveggie"){
pizza = new NYViggePizza();
}else if(...){
...
}
return pizza;
}
}
缺点:依赖的具体类太多,难以维护。缺乏弹性。
优化方法:可以把createPizza()抽象成一个工厂接口,再由三个不同的加盟商去实现三个不同的工厂类。具体的创建对象的工作,由子类来实现。
每个区域类型创建一个PizzaStore(NYPizzaStore、ChicagoPizzaStore、CaliforniaPizzaStore),由每个子类各自决定如何制作披萨。
每个子类都会覆盖createPizza()方法,同时使用PizzaStore的orderPizza()方法。
如果加盟店为顾客提供纽约风味的的披萨,就使用NYStylePizzaStore。
采用工厂方法实现:
public abstract class PizzaStore{
public Pizza orderPizza(String type){
Pizza pizza = createPizza(type);
pizza.prepare(); 佐料)
pizza.bake(); //烘烤
pizza.cut(); //切片
pizza.box(); //装盒
return pizza;
}
abstract Pizza createPizza(String type);
}
public class NYStylePizzaStore extend PizzaStore{
public Pizza createPizza(String type){
Pizza pizza;
if(type.equals("NYcheese")){
pizza = new NYCheesePizza();
}else if(type.equals("NYpepperoni")){
pizza = new NYPepperoniPizza():
}else if(type.equals("NYclam")){
pizza = new NYClamPizza();
}else if(type.equals("NYveggie"){
pizza = new NYViggePizza();
return pizza;
}
}
public class ChicagoStylePizzaStore extend PizzaStore{...}
public class CaliforniaStylePizzaStore extend PizzaStore{...}
原本由一个对象负责所有具体类的实例化,现在通过PizzaStore改为由一群子类来负责实例化。
abstract Pizza createPizza(String type);工厂方法用来处理对象的创建,并将这样的行为封装在子类中。这样,客户程序中关于超类的代码就和子类对象创建代码解耦了。1)工厂方法必须是抽象的,所以依赖子类来处理对象的创建。2)工厂方法必须返回一个产品。3)工厂方法将客户和实际创建具体产品代码分隔开。4)具体产品子类都继承自同一个产品抽象类。
工厂方法模式:定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
工厂方法帮我们将产品的实现从产品的使用解耦,如果增加或改变产品的实现,Creator类并不会受影响。简单工厂是把所有工作放在一个地方全部处理完。工厂方法创建的是一个框架,让子类决定如何实现。
工厂方法模式的用意是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类中。
工厂方法抽象类角色:是工厂方法模式的核心,任何在模式中创建对象的工厂类必须实现这个接口。
面向对象原则:依赖倒置原则
当你在实例化一个对象时, 就是在依赖它的具体类。如果直接在PizzaOrder里创建各种不同的具体披萨对象,那它的依赖所有具体的披萨类:
依赖倒置原则:要依赖抽象,不要依赖具体类。
与针对接口编程原则相比,这个原则更强调抽象,不能让高层组件依赖低层组件。而且不管高层组件还是低层组件,两者都应该依赖于抽象。高层组件:PizzaStore,低层组件:具体的披萨实现类如NYClamPizza。
思考倒置:需要实现一个披萨店,首先披萨店进行准备、烘烤、装盒,披萨店需要能够制作不同的口味的披萨:芝士披萨、素食披萨等;先从顶端开始,然后往下到具体类。不想让披萨店理会这些具体的类,要不然披萨店将会全都依赖这些具体类。采用倒置的方法,从披萨Pizza开始,素食披萨、芝士披萨都是披萨,共享同一个Pizza接口。披萨店使用披萨抽象即可,不需要理会具体的披萨类。
所有的披萨类都依赖披萨抽象,披萨店也依赖披萨抽象。我们已经倒置了一个拉萨店依赖具体披萨类的设计。
使用工厂方法之后,PizzaStore和具体的Pizza实现类都依赖于Pizza抽象类。
第四篇:抽象工厂方法模式讲解
第三次需求变更:要如何确保每家加盟店使用高质量的原料?
建造一个生产原料的工厂,并将原料运送到各家加盟店。但是,不同地方的加盟店,使用的原料是不一样的。 所以需要为纽约和芝加哥两个加盟店准备两组不同的原料。每个原料家族都包含了一种面团、一种酱料、一种芝士,以及一种海鲜佐料的类型,每个区域实现了一个完整的原料家族。
需求分析:我们要建造一个工厂来生产原料;这个工厂将负责创建原料家族中的每一种原料。每个原料都有一个对应的方法创建该原料。
public interface PizzaIngredientFactory{
public Dough createDough(); //面团
public Sauce createSauce(); //酱料
public Cheese createCheese();//芝士
public Veggies[] createVeggies();//蔬菜
public Pepperoni createPepperoni();//香肠
public Clams createClam();//蛤蜊
}
处理方法:1)为每个区域建造一个工厂。创建一个继承自PizzaIngredientFactory的子类来实现每一个创建方法。2)实现一组原料类供工厂使用。如ReggianoCheese、RedPeppers等。这些类可以在合适的区域间共享。
1)我们引入新类型的工厂,也就是所谓的抽象工厂,来创建披萨原料家族。
2)通过抽象工厂所提供的接口,可以创建产品的家族,利用这个接口,我们的代码将从实际工厂解耦,以便在不同上下文中实现各式各样的工厂,制造出各种不同的产品。
3)因为代码从实际的产品中解耦了,所以我们可替换不同的工厂来取得不同的行为。
抽象工厂模式:提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
1)抽象工厂允许客户使用抽象的接口来创建一组相关的产品,而不需要知道实际产出的具体产品是什么。这样一来,客户就从具体的产品中解耦。
2)抽象工厂的方法经常以工厂方法的方式实现。抽象工厂的任务是定义一个负责创建一组产品的接口。这个接口内的每个方法都负责创建一个具体产品,同时我们利用实现抽象工厂的子类来提供这些具体的做法。
每一个模式都是针对一定问题的解决方案。
抽象工厂模式与工厂方法模式的最大区别就在于,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则需要面对多个产品等级结构。
产品族,是指位于不同产品等级结构中,功能相关联的产品组成的家族。如AMD的主板、芯片组、CPU组成一个家族,Intel的主板、芯片组、CPU组成一个家族。
而这两个家族都来自于三个产品等级:主板、芯片组、CPU。一个等级结构是由相同的结构的产品组成。
第五篇:案例实践
练习案例源码:https://github.com/chentian114/100-dayaction/tree/master/designpattern-1
简单工厂方法
SimplePizzaFactoryDemo简单披萨工厂案例:
当需要披萨时,就叫披萨工厂创建一个。
工厂方法模式(FactoryMethod)
FactoryMethodPizzaDemo工厂方法模式披萨工厂案例:
每个区域类型创建一个PizzaStore(NYPizzaStore、ChicagoPizzaStore、CaliforniaPizzaStore),每个子类各自决定如何制作披萨。
工厂方法用来处理对象的创建,并将这样的行为封装在子类中。这样,客户程序中关于超类的代码就和子类对象创建代码解耦了。
ExportFileDemo导出报表文件案例:
系统需要支持对数据库中的数据进行导出,
1)并且支持多种导出格式,如:HTML、PDF等,
2)每种格式导出的结构有所不同,如标准报表、财务报表等。
FactoryHumanDemo女娲造人案例:
女娲使用乾坤鼎造出不同肤色的人。
对造人过程进行分析,该过程涉及三个对象:女娲、乾坤鼎、三种不同肤色的人。
女娲可以用客户来表示,乾坤鼎类似于一个工厂,负责制造生产产品(即人类),
三种不同肤色的人,都是同一个接口下的不同实现类, 对于乾坤鼎来说都是它生产出的产品。
抽象工厂模式(AbstractFactory)
AbstractPizzaIngredientDemo披萨原料工厂:
披萨原料有:面团、酱料、奶酪等。
不同地区风味的披萨原料是不同的,纽约风味的披萨使用的原料是:薄饼面团、加番茄酱、雷奇亚干酪奶酪等。
芝加哥风味的披萨使用的原料是:厚饼面团、李子番茄酱、马苏里拉奶酪等
ComputerEngineerDemo电脑工程师组装电脑案例:
我们在组装电脑的时候,通常需要选择一系列的配件,比如CPU、主板等。
在确定装机方案时,需要整体考虑各个配件之间的兼容性。
比如:CPU和主板,Intel的CPU根本就插不到AMD的主板中。CPU对象和主板对象其实是有关系的,需要相互匹配的。
案例分析:对于装机工程师,只需要知道CPU和主板的接口,而需要不知道具体实现。
第六篇:总结
简单工厂模式把核心放在一个具体类上。
工厂方法模式退化后可以变得很像简单工厂模式。设想如果非常确定一个系统只需要一个具体工厂类,那么不妨把抽象工厂类合并到具体工厂类中去。
抽象工厂与工厂方法都是负责创建对象,工厂方法利用的是继承,抽象工厂是通过对象组合。
利用工厂方法创建对象,需要扩展一个类,并覆盖它的的工厂方法。其实整个工厂方法模式,只不过就是通过子类来创建对象。用这种做法,客户只需要知道他们所使用的抽象类型就可以了,而由子类来负责决定具体类型。
抽象工厂用来创建一个产品家族的抽象类型,这个类型的子类定义了产品被产生的方法。抽象工厂如果加入新产品就必须改变接口。其一般使用工厂方法来实现具体工厂。
工厂方法模式:
优点:
1)良好的封装性,代码结构清晰。一个对象创建是有条件约束的,如一个调用者需要一个具体的产品对象,只要知道这个产品的类名就可以了,不用知道创建对象的过程, 降低模块间的耦合。
2)工厂方法模式的扩展性非常优秀。在增加产品类的情况下,只要适当地修改具体的工厂类或扩展一个工厂类,就可以完成“拥抱变化”。
3)屏蔽产品类。调用者只需要关心产品的接口,只要接口保持不变,系统中的上层模块就不需要发生变化。因为产品类的实例化工作是由工厂类负责的,生成具体哪一个产品是由工厂类决定的。
4)工厂方法模式是典型的解耦框架。高层模块值需要知道产品的抽象类,其他的实现类都不用关心。
使用场景:
1)工厂方法模式是new一个对象的替代品,所有需要生成对象的地方都可以使用,但需要慎重地考虑是否要增加一个工厂类进行管理增加代码的复杂度。
2)需要灵活的、可扩展的框架时,可以考虑采用工厂方法模式。
抽象工厂模式
抽象工厂模式的好处:一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象。
抽象工厂的功能是为一系列相关对象或相互依赖的对象创建一个接口。
由于抽象工厂定义的一系列对象通常是相关或相互依赖的,这些产品对象就构成了一个产品族,也就是抽象工厂定义了一个产品族。
优点:
分离接口和实现;客户端使用抽象工厂来创建需要的对象,而客户端根本就不知道具体的实现是谁。
使切换产品族变得容易;
产品族内的约束为非公开状态。
缺点:
不太容易扩展新的产品;如果需要给整个产品族添加一个新的产品,那么就需要修改抽象工厂,这样就会导致修改所有的工厂实现类。
一个应用系统是由多人开发的,导出的功能是你实现的,但是使用者是其他人。
这时候你应该设计的足够灵活并尽可能降低两者之间的耦合度,
当你修改或增加一个新的功能时,使用者不需要修改任何地方。
假如你的设计不够灵活,可能一个小的需求变更,便使得你的代码结构发生改变,并导致使用者都要修改他们的代码。