设计模式——工厂方法 Java源代码

最后更新于:2022-04-01 20:01:54

本程序改编自《Head First Design Patterns》中的Pizza例子,我本学期早上经常吃包子。 总共有11个类: - 一个工厂父类 ( Creator) - 两个工厂子类 ( Concrete Creator) - 一个包子父类 ( Product ) - 六个包子子类 ( Concrete Product ) - 一个Main类 ( 程序的入口 ) ![工厂模式](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cab10c0f37f.jpg "") 图:ConcreteProduct 继承 Product,ConcreteCreator 继承 Creator,ConcreateProduct 依赖 ConcreateProduct。其实,有一条线没有显示出来:Creator 依赖 Product。 *Head First* 说,这体现了依赖倒置原则:因为高层模块(Creator)依赖抽象类(Product),底层模块(ConcreteProduct)也依赖抽象类(Product)。 补充说明:我个人感觉,严格的说:ConcreteProduct 和Product 之间不是依赖,而是继承关系。勉强说的话,算作一种”非常非常强的依赖“吧。*Head First* : The ConcreteProduct class depends on the Product abstraction too, because they implement the Product interface (we’re using the “interface” in the general sense) in the Product abstraction class. ### 一个工厂父类 ~~~ package factoryMethod; public abstract class BaoZiFactory { // 把具体的 new操作 “下放”到子类中。 abstract BaoZi createBaoZi(String baoZiName); // 这个方法不改变,即无论包子里面的馅是什么,制作包子的流程是一样的(都是准备馅,然后蒸30分钟) final public BaoZi makeBaoZi(String baoZiName) { BaoZi baoZi = createBaoZi(baoZiName); baoZi.prepare(); baoZi.steam(); return baoZi; } } ~~~ ### 两个工厂子类 ~~~ package factoryMethod; public class BaoZiFactoryChangsha extends BaoZiFactory { public BaoZiFactoryChangsha() { System.out.println("Constructor of BaoZiFactory in Changsha\n"); } @Override // 依据传进来的参数,决定new什么包子 BaoZi createBaoZi(String baoZiName) { BaoZi baoZi = null; if(baoZiName.equals("酱肉")) { baoZi = new ChangshaJiangRouBaoZi(); } else if(baoZiName.equals("青菜")) { baoZi = new ChangshaQingCaiBaoZi(); } else if(baoZiName.equals("鲜肉")) { baoZi = new ChangshaXianRouBaoZi(); } return baoZi; } } ~~~ ~~~ package factoryMethod; public class BaoZiFactoryWuhan extends BaoZiFactory { public BaoZiFactoryWuhan() { System.out.println("Constructor of BaoZiFactory in Wuhan\n"); } @Override // 依据传进来的参数,决定new什么包子 BaoZi createBaoZi(String baoZiName) { BaoZi baoZi = null; if(baoZiName.equals("酱肉")) { baoZi = new WuhanJiangRouBaoZi(); } else if(baoZiName.equals("青菜")) { baoZi = new WuhanQingCaiBaoZi(); } else if(baoZiName.equals("鲜肉")) { baoZi = new WuhanXianRouBaoZi(); } return baoZi; } } ~~~ ### 一个包子父类 ~~~ package factoryMethod; import java.util.ArrayList; // 父类:包子,抽象出包子共有的特性 // 有道词典:steamed stuffed bun (蒸的,填充的,小圆面包) // 由于这三个单词加起来过长,我命名放弃采纳英文命名法,直接使用汉语拼音命名法BaoZi public abstract class BaoZi { private String name; ArrayList stuffings = new ArrayList (); public void setName(String n) { this.name = n; } public String getName() { return name; } void prepare() { System.out.println("Prepare " + name); System.out.println("Stuffings are:"); for(String stuff: stuffings) { System.out.println(stuff); } } void steam() { System.out.println("Steam for 30 minutes"); } //覆盖toString (这个方法继承自java.lang.Object) public String toString() { StringBuffer display = new StringBuffer(); display.append("---- " + name + " ----\n"); for(String stuff : stuffings) { display.append(stuff + "\n"); } return display.toString(); } } ~~~ ### 六个包子子类 ~~~ package factoryMethod; public class ChangshaJiangRouBaoZi extends BaoZi { public ChangshaJiangRouBaoZi() { setName("长沙酱肉包子"); stuffings.add("辣椒"); stuffings.add("炸酱"); stuffings.add("肉末"); stuffings.add("干子"); } } ~~~ ~~~ package factoryMethod; public class ChangshaQingCaiBaoZi extends BaoZi { public ChangshaQingCaiBaoZi() { setName("长沙青菜包子"); stuffings.add("辣椒"); stuffings.add("包菜"); stuffings.add("茄子"); } } ~~~ ~~~ package factoryMethod; public class ChangshaXianRouBaoZi extends BaoZi { public ChangshaXianRouBaoZi() { setName("长沙鲜肉包子"); stuffings.add("辣椒"); stuffings.add("鲜肉"); } } ~~~ ~~~ package factoryMethod; public class WuhanJiangRouBaoZi extends BaoZi { public WuhanJiangRouBaoZi() { setName("武汉酱肉包子"); stuffings.add("炸酱"); stuffings.add("肉末"); stuffings.add("干子"); } } ~~~ ~~~ package factoryMethod; public class WuhanQingCaiBaoZi extends BaoZi { public WuhanQingCaiBaoZi() { setName("武汉青菜包子"); stuffings.add("包菜"); stuffings.add("茄子"); } } ~~~ ~~~ package factoryMethod; public class WuhanXianRouBaoZi extends BaoZi { public WuhanXianRouBaoZi() { setName("武汉鲜肉包子"); stuffings.add("鲜肉"); } } ~~~ ### 一个Main类 ~~~ package factoryMethod; public class Main { public static void main(String[] args) { BaoZiFactory wuhanFactory = new BaoZiFactoryWuhan(); BaoZiFactory changshaFactory = new BaoZiFactoryChangsha(); BaoZi baoZi = null; baoZi = wuhanFactory.makeBaoZi("酱肉"); System.out.println("Caitao made a " + baoZi.getName() + "\n"); baoZi = wuhanFactory.makeBaoZi("青菜"); System.out.println("Caitao made a " + baoZi.getName() + "\n"); baoZi = changshaFactory.makeBaoZi("鲜肉"); System.out.println("Lucy made a " + baoZi.getName() + "\n"); baoZi = changshaFactory.makeBaoZi("青菜"); System.out.println("Lucy made a " + baoZi.getName() + "\n"); } } ~~~ ### 运行结果 直接从eclipse复制过来的 ~~~ Constructor of BaoZiFactory in Wuhan Constructor of BaoZiFactory in Changsha Prepare 武汉酱肉包子 Stuffings are: 炸酱 肉末 干子 Steam for 30 minutes Caitao made a 武汉酱肉包子 Prepare 武汉青菜包子 Stuffings are: 包菜 茄子 Steam for 30 minutes Caitao made a 武汉青菜包子 Prepare 长沙鲜肉包子 Stuffings are: 辣椒 鲜肉 Steam for 30 minutes Lucy made a 长沙鲜肉包子 Prepare 长沙青菜包子 Stuffings are: 辣椒 包菜 茄子 Steam for 30 minutes Lucy made a 长沙青菜包子 ~~~ ### 如果没有工厂方法模式 又要实现同样的功能怎么破?代码如下(理想输入条件,没有异常处理) ~~~ public BaoZi makeBaoZi(String place, String type) { BaoZi baoZi = null; if (place.equals("武汉")) { if (type.equals("酱肉")) { baoZi = new WuhanJiangRouBaoZi(); } else if (type.equals("青菜")) { baoZi = new WuhanQingCaiBaoZi(); } else if (type.equals("鲜肉")) { baoZi = new WuhanXianRouBaoZi(); } } else if (place.equals("长沙")) { if (type.equals("酱肉")) { baoZi = new ChangshaJiangRouBaoZi(); } else if (type.equals("青菜")) { baoZi = new ChangshaQingCaiBaoZi(); } else if (type.equals("鲜肉")) { baoZi = new ChangshaXianRouBaoZi(); } } baoZi.prepare(); baoZi.steam(); return baoZi; } ~~~ ~~~ 优劣之分立马体现出来了!我们可以看到代码变短了(当然了,需要增加一些类作为“额外工作”,这是值得的) 创建包子的new操作“隐藏了”,取而代之的是一个factory对象调用createBaoZi方法 public BaoZi makeBaoZi(String place, String type) { BaoZiFactory factory = null; if(place.equals("武汉")) { factory = new BaoZiFactoryWuhan(); } else if(place.equals("长沙")) { factory = new BaoZiFactoryChangsha(); } BaoZi baoZi = factory.createBaoZi(type); baoZi.prepare(); baoZi.steam(); return baoZi; } ~~~ **1. 工厂方法模式的优势**:如果位于武汉的工厂需要增加一种口味的包子(这种类似的事情经常发生),比如”热干包子“,那么工厂方法模式只用增加一个包子子类,然后修改武汉工厂子类就行了。 ~~~ 关键是下面的代码不变,放到哪里都一样,以不变应万变! BaoZiFactory factory = null; if(place.equals("武汉")) { factory = new BaoZiFactoryWuhan(); } else if(place.equals("长沙")) { factory = new BaoZiFactoryChangsha(); } BaoZi baoZi = factory.createBaoZi(type); ~~~ **2. 没有工厂方法模式的劣势**:如果位于武汉的工厂需要增加一种口味的包子(这种类似的事情经常发生),比如”热干包子“,那么所有用于在武汉创建包子的 if( … ) { new … } else( ) { … } 的代码都在后面增加一个 if( 热干 ) {new ReGanBaoZiWuhan() } else( ) { … } ~~~ BaoZi baoZi = null; if (place.equals("武汉")) { if (type.equals("酱肉")) { baoZi = new WuhanJiangRouBaoZi(); } else if (type.equals("青菜")) { baoZi = new WuhanQingCaiBaoZi(); } else if (type.equals("鲜肉")) { baoZi = new WuhanXianRouBaoZi(); } *********增加的代码***************** else if(type.equals("热干")) { baoZi = new WuhanReGanBaoZi(); } ************************************ } else if (place.equals("长沙")) { if (type.equals("酱肉")) { baoZi = new ChangshaJiangRouBaoZi(); } else if (type.equals("青菜")) { baoZi = new ChangshaQingCaiBaoZi(); } else if (type.equals("鲜肉")) { baoZi = new ChangshaXianRouBaoZi(); } } ~~~ 改一个地方还比较轻松,**但是关键在于很有可能在很多地方都要用到包子**。假设100个地方都要创建包子,那么100个地方的if( … )else( )“的代码都要修改!这完全是反人类,违反了开闭原则。 [更多深入分析,查看我的这一篇博客,进入目录->第三个标题](http://blog.csdn.net/u013390476/article/details/50333763)
';