HeadFirst设计模式读书笔记之工厂模式
1. 简单工厂
1. 你开了一家披萨店,点披萨的方法可能是这样:
public pizza orderpizza(string type) { pizza pizza; if (type.equals("芒果披萨")){ pizza = new mangopizza(); }else if (type.equals("核桃披萨")){ pizza = new walnutpizza(); }else if (type.equals("橙皮披萨")){ pizza = new flavedopizza(); }else { pizza = new starpizza(); } pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; }
可以看到,每当你想增加一种披萨类型,就要修改代码,添加一种if else条件.当有多个系统存在orderpizza的需求时,每个系统都要同时修改他们的代码.因此,需要将这种实例化具体对象的代码封装起来.
public class pizzastore { simplepizzafactory factory; public pizzastore(simplepizzafactory factory){ this.factory = factory; } public pizza orderpizza(string type) { pizza pizza; pizza = factory.createpizza(type); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } }
public class simplepizzafactory { public pizza createpizza(string type){ pizza pizza = null; if (type.equals("芒果披萨")){ pizza = new mangopizza(); }else if (type.equals("核桃披萨")){ pizza = new walnutpizza(); }else if (type.equals("橙皮披萨")){ pizza = new flavedopizza(); }else { pizza = new starpizza(); } return pizza; } }
这就是简单工厂方法,他不算一种设计模式,而是一种编程习惯.其要点有二:
1. 将产品定义为抽象类,可以被覆盖
2. 将具体的产品放到工厂类中来实例化,由工厂创建并返回给客户,这样便使得产品的创建逻辑可以解耦合,并增加了复用性
2. 工厂方法
1. 假设你现在有了很多加盟店,每种加盟店根据地区的差异有自己的工厂,但加盟店虽然采用工厂方法创建披萨,但其他部分却有所不同:烘烤的做法,不切片,使用杂七杂八的盒子...
这时,你想把创建披萨的方法与每家披萨店绑定在一起,让每家pizzastore中都使用createpizza()创建pizza,并且这些店还可以拥有一定的制作披萨的自主权.因此,我们需要把pizzastore和createpizza()定义成抽象的
/** * 抽象的pizzastore */ public abstract class pizzastore { public pizza orderpizza(string type) { pizza pizza; // 使用自己的方法做披萨 pizza = createpizza(type); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } // 将做披萨的工厂方法放到pizzastore中,工厂方法是抽象的,所以依赖子类来处理对象的创建 public abstract pizza createpizza(string type); }
2. 现在由pizzastore的子类来具体卖披萨
public class beijingpizzastore extends pizzastore { @override public pizza createpizza(string item) { if (item.equals("橙皮披萨")){ return new flavedopizza(); }else { return new pekingduckpizza(); } } }
public class chengdupizza extends pizzastore { @override public pizza createpizza(string type) { if (type.equals("芒果披萨")){ return new mangopizza(); }else { return new starpizza(); } } }
现在,orderpizza()与具体的pizza实现了一定的解耦合,决定最终pizza对象的变成了某个具体的pizza店,而pizza店则由顾客来决定
3. 一些对工厂方法的理解:
1. 工厂方法是抽象的,所以依赖子类来创建对象,这样便将超类的代码与具体创建产品的代码分割开了
2. 工厂方法必须返回一个产品,超类通常中会用到这个产品
3. 工厂方法将产品与创建者抽象为接口,使他们成为平级的存在,具体的产品创建则使用子类来交互
4. 工厂方法将产品实例化的操作推迟到子类,子类的选择则由创建者来决定
5. 简单工厂方法更趋向组合,工厂方法则利用抽象和继承
6. 简单工厂封装了对象的创建,一个工厂处理所有的产品,但对同一个产品不可变更其做法,工厂方法则更有弹性,子类通过重写可以更改产品的创建
3. 依赖倒置原则
下面是一个简单粗暴的披萨店:
/** * 依赖具体对象的pizzastore */ public class pizzastore { public pizza createpizza(string style, string type){ pizza pizza = null; if (style.equals("一元档")){ if (type.equals("芒果披萨")){ pizza = new mangopizza(); }else if (type.equals("核桃披萨")){ pizza = new walnutpizza(); } }else if (style.equals("二元档")){ if (type.equals("北京烤鸭披萨")){ pizza = new pekingduckpizza(); }else if (type.equals("橙皮披萨")){ pizza = new flavedopizza(); } }else { system.out.println("披萨卖完了"); } pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } }
可以看到,这个披萨店依赖于每一个具体的披萨对象,每增加一个披萨就要修改一次代码,增加一个依赖,可以说是非常不好用了
从图中看到,高层组件直接依赖于低层组件而依赖倒置原则的的内容就是:
要依赖抽象,不要依赖具体类
这个原则说明不管高层还是低层组件都不应该依赖具体类,而应该依赖于抽象.如图所示:
与之前相比高层组件pizzastore不在直接依赖于具体的pizza对象,而是依赖于pizza接口,而具体的pizza产品则由被高层组件依赖颠倒成了依赖pizza接口,这就是一种依赖倒置.总体来看,pizzastore和pizza都抽象化了,不用再依赖于具体的类型,符合依赖倒置的设计原则.
有三个指导方针可以让你更好地遵守依赖倒置原则:
- 变量不可以持有具体类的引用
- 不要让类派生自具体类
- 不要覆盖基类中已覆盖的方法
4. 抽象工厂方法:创建产品家族
与普通的工厂方法比较,抽象工厂方法要显得重型得多: 工厂方法只创建一种产品,抽象工厂方法则创建一个家族的产品
1. 典型做法是创建一个工厂的接口,这个接口负责创建所有的产品家族成员,如创建一个生产调料家族的工厂:
/** * 原料工厂的接口,实现这样的接口,可以随意组合产出各式各样的调料 * 每个原料都是一个类 */ public interface seasoningfactory { chili createchili(); douban createdouban(); pepper createpepper(); salt createsalt(); }
/** * 郫县调料生产厂 */ public class pixianseasoningfactory implements seasoningfactory { /** * 虎皮青椒 * @return */ @override public chili createchili() { return new hupiqingjiao(); } /** * 郫县豆瓣 * @return */ @override public douban createdouban() { return new pixiandouban(); } /** * 花椒油 * @return */ @override public pepper createpepper() { return new huajiaoyou(); } /** * 食用盐 * @return */ @override public salt createsalt() { return new shiyongyan(); } }
/** * 灌县调料生产厂 */ public class guanxianseasoningfactory implements seasoningfactory { /** * 虎皮青椒 * @return */ @override public chili createchili() { return new hupiqingjiao(); } /** * 老干妈豆瓣酱 * @return */ @override public douban createdouban() { return new laoganmadoubanjiang(); } /** * 花椒油 * @return */ @override public pepper createpepper() { return new huajiaoyou(); } /** * 食用盐 * @return */ @override public salt createsalt() { return new shiyongyan(); } }
下面是抽象工厂方法的类图:
从类图中可以看出来,抽象工厂方法有一个缺点,就是当添加了一个新产品接口时,要去抽象工厂接口中添加一个方法,这会造成一些麻烦
2. 工厂方法与抽象工厂的一些对比
相同点:
- 都是负责创建对象
- 都能使程序解耦,将程序从创建大量具体对象的泥潭中拉出来
不同点:
- 工厂方法用子类来创建具体对象,利用抽象和继承.抽象工厂包含了一大堆接口,利用组合的思想来创建对象
- 工厂方法一般用来创建一种产品,抽象工厂则创建一族产品
-
工厂方法功能简单,使用轻便.抽象工厂功能强大,使用时会创建大量的类
3. 下面使用抽象工厂生产的原料来制作披萨
/** * 原料工厂的接口,实现这样的接口,可以随意组合产出各式各样的调料 * 每个原料都是一个类 */ public interface seasoningfactory { /** * 辣椒 * @return */ chili createchili(); /** * 豆瓣 * @return */ douban createdouban(); /** * 花椒 * @return */ pepper createpepper(); /** * 盐 * @return */ salt createsalt(); }
/** * 豆瓣 */ public abstract class douban { public abstract string saymyname(); }
public class pixiandouban extends douban { @override public string saymyname() { return "郫县豆瓣"; } }
public class laoganmadoubanjiang extends douban { @override public string saymyname() { return "老干妈豆瓣酱"; } }
/** * 郫县调料生产厂 */ public class pixianseasoningfactory implements seasoningfactory { /** * 虎皮青椒 * @return */ @override public chili createchili() { return new hupiqingjiao(); } /** * 郫县豆瓣 * @return */ @override public douban createdouban() { return new pixiandouban(); } /** * 花椒油 * @return */ @override public pepper createpepper() { return new huajiaoyou(); } /** * 食用盐 * @return */ @override public salt createsalt() { return new shiyongyan(); } }
/** * 灌县调料生产厂 */ public class guanxianseasoningfactory implements seasoningfactory { /** * 虎皮青椒 * @return */ @override public chili createchili() { return new hupiqingjiao(); } /** * 老干妈豆瓣酱 * @return */ @override public douban createdouban() { return new laoganmadoubanjiang(); } /** * 花椒油 * @return */ @override public pepper createpepper() { return new huajiaoyou(); } /** * 食用盐 * @return */ @override public salt createsalt() { return new shiyongyan(); } }
/** * 每一个披萨都有一组抽象工厂生产的原料 * */ public abstract class pizza { protected string name; /** * 辣椒 */ protected chili chili; /** * 豆瓣 */ protected douban douban; /** * 花椒 */ protected pepper pepper; /** * 盐 */ protected salt salt; /** * 在这个方法中,收集抽象工厂生产的原料 */ public abstract void prepare(); public void bake(){ system.out.println("烘烤..."); } public void cut(){ system.out.println("切片..."); } public void box() { system.out.println("包装..."); } public string getname() { return name; } public void setname(string name) { this.name = name; } @override public string tostring(){ return "披萨制作完毕:"+name +"\n生产原料:"+chili.saymyname() +"\t"+douban.saymyname() +"\t"+pepper.saymyname() +"\t"+salt.saymyname(); } }
public class chengdupizza extends pizza{ // 调料工厂 seasoningfactory seasoningfactory; public chengdupizza(seasoningfactory seasoningfactory) { this.name = "旧成都披萨"; this.seasoningfactory = seasoningfactory; } @override public void prepare() { system.out.println("开始准备 "+getname()+":"); chili = seasoningfactory.createchili(); douban = seasoningfactory.createdouban(); pepper = seasoningfactory.createpepper(); salt = seasoningfactory.createsalt(); system.out.println("以下原料准备完毕:"+chili.saymyname() +","+douban.saymyname()+","+pepper.saymyname()+","+salt.saymyname()); } }
public abstract class pizzastore { public abstract pizza createpizza(string type); }
public class starboundpizzastore extends pizzastore { @override public pizza createpizza(string type) { pizza pizza; seasoningfactory factorya = new pixianseasoningfactory(); seasoningfactory factoryb = new guanxianseasoningfactory(); if (type.equals("旧成都披萨")){ pizza = new chengdupizza(factorya);; }else if (type.equals("新北京披萨")){ pizza = new pekingpizza(factoryb); }else { pizza = new chengdupizza(factoryb); } pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } public static void main(string[] args) { pizzastore pizzastore = new starboundpizzastore(); pizza pizza = pizzastore.createpizza("旧成都披萨"); system.out.println(pizza.tostring()); pizza pizza1 = pizzastore.createpizza("新北京披萨"); system.out.println(pizza1.tostring()); } }
结果: 开始准备 旧成都披萨: 以下原料准备完毕:虎皮青椒,郫县豆瓣,花椒油,食用盐 烘烤... 切片... 包装... 披萨制作完毕:旧成都披萨 生产原料:虎皮青椒 郫县豆瓣 花椒油 食用盐 开始准备 新北京披萨: 以下原料准备完毕:虎皮青椒,老干妈豆瓣酱,花椒油,食用盐 烘烤... 切片... 包装... 披萨制作完毕:新北京披萨 生产原料:虎皮青椒 老干妈豆瓣酱 花椒油 食用盐