您的位置:首页 > 编程语言 > Java开发

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

2015-12-22 12:52 344 查看
本程序改编自《Head First Design Patterns》中的Pizza例子,我本学期早上经常吃包子。

总共有11个类:

一个工厂父类 ( Creator)

两个工厂子类 ( Concrete Creator)

一个包子父类 ( Product )

六个包子子类 ( Concrete Product )

一个Main类 ( 程序的入口 )




图: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.

一个工厂父类

[code]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;
    }
}


两个工厂子类

[code]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;
    }

}


[code]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;
    }

}


一个包子父类

[code]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();
    }

}


六个包子子类

[code]package factoryMethod;

public class ChangshaJiangRouBaoZi extends BaoZi {

    public ChangshaJiangRouBaoZi() 
    {
        setName("长沙酱肉包子");
        stuffings.add("辣椒");
        stuffings.add("炸酱");
        stuffings.add("肉末");
        stuffings.add("干子");
    }
}


[code]package factoryMethod;

public class ChangshaQingCaiBaoZi extends BaoZi {

    public ChangshaQingCaiBaoZi()
    {
        setName("长沙青菜包子");
        stuffings.add("辣椒");
        stuffings.add("包菜");
        stuffings.add("茄子");
    }

}


[code]package factoryMethod;

public class ChangshaXianRouBaoZi extends BaoZi {

    public ChangshaXianRouBaoZi()
    {
        setName("长沙鲜肉包子");
        stuffings.add("辣椒");
        stuffings.add("鲜肉");
    }

}


[code]package factoryMethod;

public class WuhanJiangRouBaoZi extends BaoZi {

    public WuhanJiangRouBaoZi() 
    {
        setName("武汉酱肉包子");
        stuffings.add("炸酱");
        stuffings.add("肉末");
        stuffings.add("干子");
    }

}


[code]package factoryMethod;

public class WuhanQingCaiBaoZi extends BaoZi {

    public WuhanQingCaiBaoZi() 
    {
        setName("武汉青菜包子");
        stuffings.add("包菜");
        stuffings.add("茄子");
    }

}


[code]package factoryMethod;

public class WuhanXianRouBaoZi extends BaoZi {

    public WuhanXianRouBaoZi()
    {
        setName("武汉鲜肉包子");
        stuffings.add("鲜肉");
    }

}


一个Main类

[code]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复制过来的

[code]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 长沙青菜包子


如果没有工厂方法模式

又要实现同样的功能怎么破?代码如下(理想输入条件,没有异常处理)

[code]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;
    }


[code]优劣之分立马体现出来了!我们可以看到代码变短了(当然了,需要增加一些类作为“额外工作”,这是值得的)
创建包子的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. 工厂方法模式的优势:如果位于武汉的工厂需要增加一种口味的包子(这种类似的事情经常发生),比如”热干包子“,那么工厂方法模式只用增加一个包子子类,然后修改武汉工厂子类就行了。

[code]关键是下面的代码不变,放到哪里都一样,以不变应万变!

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( ) { … }

[code]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( )“的代码都要修改!这完全是反人类,违反了开闭原则。

更多深入分析,查看我的这一篇博客,进入目录->第三个标题
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: