工厂模式有两种类型:工厂方法模式和抽象工厂模式
工厂方法模式定义:定义了一个创建对象的接口(这里的接口并不是单单是指java中的interface),但由子类决定实例化的类是哪一个。工厂方法让类把实例化推迟到子类中。
抽象工厂模式定义:提供一个接口(这里的接口并不是单单是指java中的interface),用于创建相关或依赖对象的家族,而不需要明确指定具体的类。
不管工厂模式的形式是哪一种,我们要清楚的知道工厂模式的作用,工厂模式是用来封装对象的创建的!也就是说工厂模式用来管理创建对象的,其他的一概和工厂模式无关。
通过封装对象的创建,就可以达到松耦合的目的,因为滥用new关键字会将多个对象绑定起来,从而耦合在一起,而工厂模式就是要将这些耦合解开,然后以最小的耦合来达到相同的目的。
比如说:现在要在多个村庄之间组建电网,第一种方案就是“图方便原则”,我们忽略掉村与村之间的界限,只要两个住户之间相隔近,不管他俩是不是一个村,我们都将这两家住户连在同一根电线上,当扩展到成百上千的住户的时候,我们可以想象,这时的电路网会错综复杂,难以管理,当需要更改电网的时候,也是牵一发而动全身。第二个方案是将住户以村为单位,或者更小的,我们以生产队为单位,一个生产队里面的住户采取“图方便原则”,而生产队之间用一根电线连接起来,这样生产队内部的调整不会影响到其他的生产队,也便于管理,这样就以最小的耦合来达到了相同的目的。实际上这里的每一个电线连接处我们都可以将其视为一个new关键字。
当然上面的例子只是宽泛的类比了一下工厂模式,例子不一定准确,下面通过一个真正的实例来介绍工厂模式中的工厂方法模式:
- 需求说明
- 系统设计
需要使用new的地方,就是对象之间关联的地方,也就是耦合发生的地方,虽然完全消除对象之间的关联是一件不可能的事情,但是我们可以在保证需求的前提下,尽量的减少耦合,或者将耦合全部集中到一个易于管理的地方。
对于 本例子来说,对象之间的关联在于:①具体的店铺和抽象店铺的关联;②具体面食与抽象面食之间的关联。我们可以将抽象店铺与具体店铺之间用一个new关键字来关联,同时将同一类店铺中所有的面食种类全部封装在店铺内部,这样每个店铺就可以管理自己的面食,店铺与店铺之间不相关联。
- 系统实现
1 面食抽象类: 2 /** 3 * 声明为抽象方法,不能直接new,实例化交给工厂来做 4 * @author Apache_xiaochao 5 * 6 */ 7 public abstract class Noodle { 8 9 private String name; // 名称 10 private String dough; // 面团 11 private List<String> spices; // 调料 12 13 /** 14 * 准备食材 15 */ 16 public void prepare() { 17 System.out.println( 18 "正在准备食材,面食名称:" + this.getName() + 19 " 面团名称:" + this.getDough() + 20 " 调料:" + spices.toString()); 21 } 22 23 /** 24 * 烧水 25 */ 26 public void boilWater() { 27 System.out.println("正在烧水..."); 28 } 29 30 /** 31 * 煮面 32 */ 33 public void cook() { 34 System.out.println("正在煮面..."); 35 } 36 37 /** 38 * 装盘 39 */ 40 public void sabot() { 41 System.out.println("正在装盘..."); 42 } 43 //余下的get和set方法,省略... 44 }
1 面食店抽象类: 2 /** 3 * 面食店抽象类,所有的面食店都需要继承这个抽象类 4 * @author Apache_xiaochao 5 * 6 */ 7 public abstract class NoodleStore { 8 9 /** 10 * 根据用户需求制作相应的面食 11 * @param type 12 * @return 13 */ 14 public Noodle orderNoodle(String type){ 15 Noodle noodle = null; 16 if(type != null){ 17 noodle = createNoodle(type); 18 noodle.prepare(); 19 noodle.boilWater(); 20 noodle.cook(); 21 noodle.sabot(); 22 } 23 return noodle; 24 } 25 26 /** 27 * 工厂方法 ,将new全部集中到这个方法里面去,然后将new延迟到子类中实现 28 * @param type 29 * @return 30 */ 31 protected abstract Noodle createNoodle(String type); //这里是核心,工厂方法就在这 32 //上面定义了一个抽象的工厂方法,也可以不是抽象的,我们可以在这里定义一个默认的工厂。 33 //这里讲所有的new全部集中到工厂方法中进行管理,每个子类都可以在该方法中管理自己的对象 34 //这里采用的是“参数化工厂方法”,可以根据传递的参数而创建不同的对象,然而工厂常常只产生一种对象,因此不需要参数化 35 36 }
1 具体的面食店,以山西面食馆为例: 2 /** 3 * 山西面食馆 4 * 5 * @author Apache_xiaochao 6 * 7 */ 8 public class ShanXiNoodleStore extends NoodleStore { 9 10 @Override 11 protected Noodle createNoodle(String type) { 12 Noodle noodle = null; 13 if (type != null) { 14 if ("saozi".equalsIgnoreCase(type)) { 15 noodle = new SXSaoziNoodle(); 16 } else if ("liujian".equalsIgnoreCase(type)) { 17 noodle = new SXLiujianNoodle(); 18 } 19 } 20 return noodle; 21 } 22 23 } 24 在这个具体的面食店里面,我们使用了很多的new,这些new可以轻松被管理,如果以后这家店希望添加新的面食,或者减少不受欢迎的面食,改起来就方便很多。
整个架构中代码执行过程说明:
- 我们先在某个地方开一家山西面食馆:NoodleStore noodleStore = new ShanXiNoodleStore(); //店铺开起来了
- 假设现在有一位顾客走进我们新开的面食馆,点了一份臊子面:noodleStore.orderNoodle("saozi"); //来生意啦
- 点完餐之后,系统开始运行,步骤如下:
因为是在山西面食馆里面点餐,所以是山西面食馆里面的系统在为我们服务,当指定面食类型之后,系统就会为我们准备后相应的食材,然后开始制作面食(这些基本上都是大同小异,如果有不同的地方,可以覆盖父类中的相关方法)
对于工厂方法的总结:
工厂模式都是用来封装对象的创建的。工厂方法模式属于工厂模式,当然也是为封装对象创建而服务的,由上面的例子我们可以看,所有对象的创建都被封装到工厂方法之中,而且这个方法的实现,并没有在NoodleStore这个类中,而是在NoodleStore的子类中,因为子类更加清楚要做什么,这样就在一定程度上达到了松耦合的目的,并且让代码更加清晰、灵活、易于扩展。
再次给出工厂方法模式的定义:定义一个创建对象的接口(这里的接口就是指工厂方法),但由子类决定要实例化的类是哪一个(工厂方法的实现在子类中)。工厂方法让类把实例化推迟到子类。
接下来我们仍然使用这个例子来讲解抽象工厂模式:
- 需求说明
- 系统设计
这一次我们选用抽象工厂,既然也属于工厂模式,那么它的作用还是用来封装对象的创建。(下图为相应UML图,不过已经没有什么可看性了,太复杂)
- 系统实现
1 抽象工厂接口: 2 /** 3 * 抽象工厂 4 * @author Apache_xiaochao 5 * 6 */ 7 public interface NoodleIngredientFactory { 8 9 public abstract Dough orderDough(); //提供面团 10 public abstract Shallot orderShallot(); //提供小葱 11 public abstract Chili orderChili(); //提供辣椒 12 public abstract Egg orderEgg(); //提供鸡蛋 13 14 } 15 抽象工厂中定义了各种各样食材的供应方法,这些都返回了相应的食材对象,肯定需要用到很多new关键字,这里我们用抽象工厂将它们封装起来。
1 具体工厂的实现(以东北食材供应工厂为例): 2 /** 3 * 东北原料工厂,这个工厂为东北全境的东北面食馆提供食材 4 * @author Apache_xiaochao 5 * 6 */ 7 public class DongBeiIngredientFactory implements NoodleIngredientFactory { 8 9 @Override 10 public Dough orderDough() { 11 Dough dough = new Dough(); 12 dough.setName("黑龙江优质小麦"); 13 return dough; 14 } 15 16 @Override 17 public Shallot orderShallot() { 18 Shallot shallot = new Shallot(); 19 shallot.setName("山东大葱"); 20 return shallot; 21 } 22 23 @Override 24 public Chili orderChili() { 25 Chili chili = new Chili(); 26 chili.setName("西北红辣椒"); 27 return chili; 28 } 29 30 @Override 31 public Egg orderEgg() { 32 Egg egg = new Egg(); 33 egg.setName("农家土鸡蛋"); 34 return egg; 35 } 36 37 }
1 具体面食馆的实现(以东北面食馆为例): 2 /** 3 * 东北面食馆 4 * 5 * @author Apache_xiaochao 6 * 7 */ 8 public class DongBeiNoodleStore extends NoodleStore { 9 10 private NoodleIngredientFactory nif; 11 12 //在构造方法中指定由哪个食材工厂来提供食材 13 public DongBeiNoodleStore() { 14 this.nif = new DongBeiIngredientFactory(); 15 } 16 17 @Override 18 protected Noodle createNoodle(String type) { 19 Noodle noodle = null; 20 if (type != null) { 21 if ("yangchun".endsWith(type)) { 22 noodle = new DBYangchunNoodle(nif); 23 } else if ("dawan".equalsIgnoreCase(type)) { 24 noodle = new DBDawanNoodle(nif); 25 } 26 } 27 return noodle; 28 } 29 30 }
对于抽象工厂模式的总结:抽象工厂也是工厂模式的一种,本质上还是封装对象的创建。在本例子中我们可以看到,抽象工厂将食材的创建全部封装到抽象工厂中,不同的地区可以根据当地的特点来创建属于本地区的食材供应市场,而整个程序不需要因为地域之间不同而发生改变,每个区域只要创建一个食材供应市场,然后在自己店铺的系统中指定它既可。
工厂方法与抽象工厂之间的区别:
- 利用工厂方法创建对象,需要扩展一个类,并覆盖对应的工厂方法
- 提供一个封装对象创建的抽象类型,这个类型的子类或者是实现类定义了对象创建的方法,然后实例化这个工厂,并将其传到需要需要使用它的地方
- 工厂方法封装的是某一类对象的创建,每次只提供一个对象
- 抽象工厂封装的是一群相关的产品集合,可以同时提供多个对象
- 工厂方法使用继承:把对象的创建委托给子类,子类实现工厂方法来创建对象
- 抽象工厂使用对象组合:对象的创建被实现在工厂接口所暴露出来的方法中
如有错误,恳请读者指正!