本程序改编自《Head First Design Patterns》中的Pizza例子,我本学期早上经常吃包子。
总共有11个类:
图: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<String> stuffings = new ArrayList<String> ();
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("鲜肉");
}
}
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( )“的代码都要修改!这完全是反人类,违反了开闭原则。
更多深入分析,查看我的这一篇博客,进入目录->第三个标题