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

本程序改编自《Head First Design Patterns》中的Pizza例子,我本学期早上经常吃包子。

总共有11个类:

  • 一个工厂父类 ( Creator)
  • 两个工厂子类 ( Concrete Creator)
  • 一个包子父类 ( Product )
  • 六个包子子类 ( Concrete Product )
  • 一个Main类 ( 程序的入口 )

设计模式——工厂方法 Java源代码_第1张图片
图: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( )“的代码都要修改!这完全是反人类,违反了开闭原则。

更多深入分析,查看我的这一篇博客,进入目录->第三个标题

你可能感兴趣的:(Java,学习设计模式)