码迷,mamicode.com
首页 > 其他好文 > 详细

工厂模式

时间:2016-06-24 16:16:17      阅读:323      评论:0      收藏:0      [点我收藏+]

标签:

小例子

如今鼓励大众创业万众创新,身在吃货之都成都,我打算投入餐饮行业,可惜穷鬼一个,手头没多少银子,那就从小面馆开启我的创业之路吧。说干就干,简单筹备后我的面馆就开张接客啦!

public class Constant {
    //牛肉面
    public static String BEEF_NOODLES = "BeefNoodles";
    //担担面
    public static String DAN_DAN_NOODLES = "DanDanNoodles";
    //肥肠面
    public static String INTESTINES_NOODLES = "IntestinesNoodles";
    //甜水面
    public static String SWEET_NOODLES = "SweetNoodles";
    //燃面
    public static String BURNING_NOODLES = "BurningNoodles";
}
public class NoodleRestaurant {

    //点餐
    public Noodles orderNoodles(String type) {
        Noodles noodles;
        if (Constant.BEEF_NOODLES.equals(type)) {
            noodles = new BeefNoodles();
        } else if(Constant.DAN_DAN_NOODLES.equals(type)){
            noodles = new DanDanNoodles();
        } else if(Constant.INTESTINES_NOODLES.equals(type)) {
            noodles = new IntestinesNoodles();
        } else if (Constant.SWEET_NOODLES.equals(type)) {
            noodles = new SweetNoodles();
        } else {
            noodles = new BurningNoodles();
        }
        noodles.prepare();
        noodles.cook();
        noodles.bowl();

        return noodles;
    }
}

OK,店铺搞定,开门营业,随着吃客越来越多,我想增加新的品种:杂酱面、豌豆面…夏天到了,我还想增加凉面。发现每增加一个品种,我都得去NoodleRestaurant改动一番,这跟咱们之前一直强调的对扩展开放,对修改关闭的原则相矛盾。而且NoodleRestaurant中new了太多具体类,而代码绑着具体类会导致代码更脆弱,更缺乏弹性。想想我们的NoodleRestaurant依赖了多少具体类?
技术分享(图a)
来数一数,NoodleRestaurant依赖了多少个具体类?而且这还只是暂时的,随着菜单品种的不断增加,这个依赖将变得庞大无比。生意越来越好,我打算在重庆开一家分店,重庆人的口味跟成都不同,我又需要增加重庆小面、豌豆面,同样是牛肉面,重庆口味偏麻偏辣,成都口味偏香,天呐,NoodleRestaurant里的依赖越来越乱了,别慌,接下来要讲的工厂模式将帮你从复杂的依赖中脱困。

工厂方法模式

原本是由一个对象负责所有具体类的实例化,我们现在对NoodleRestaurant做一些改变,让一群子类来负责实例化。

public abstract class NoodleRestaurant {
    public Noodles orderNoodles(String type) {
        Noodles noodles;
        noodles = createNoodles(type);
        noodles.prepare();
        noodles.cook();
        noodles.bowl();
        return noodles;
    }
    //创建Noodles对象
    protected abstract Noodles createNoodles(String type);
}

这里的createNoodles 方法就是工厂方法,工厂方法用来处理对象的创建,并将创建行为封装在子类中,这样,NoodleRestaurant就和具体Noodles类解耦了。

/**
 * 成都面馆
 * Created by sarahzhou on 16/6/21.
 */
public class CDNoodleRestaurant extends NoodleRestaurant {
    @Override
    protected Noodles createNoodles(String type) {
        Noodles noodles;
        if (Constant.BEEF_NOODLES.equals(type)) {
            noodles = new CDBeefNoodles();
        } else if(Constant.DAN_DAN_NOODLES.equals(type)){
            noodles = new DanDanNoodles();
        } else if(Constant.INTESTINES_NOODLES.equals(type)) {
            noodles = new IntestinesNoodles();
        } else if (Constant.SWEET_NOODLES.equals(type)) {
            noodles = new SweetNoodles();
        } else if (Constant.BURNING_NOODLES.equals(type)) {
            noodles = new BurningNoodles();
        } else {
            noodles = new CDColdNoodles();
        }
        return noodles;
    }
}
/**
 * 重庆面馆
 * Created by sarahzhou on 16/6/21.
 */
public class CQNoodleRestaurant extends NoodleRestaurant {
    @Override
    protected Noodles createNoodles(String type) {
        Noodles noodles;
        if (Constant.BEEF_NOODLES.equals(type)) {
            noodles = new CQBeefNoodles();
        } else if (Constant.SMALL_NOODLES.equals(type)) {
            noodles = new SmallNoodles();
        } else if (Constant.LAOMA_DUMPLINGS.equals(type)) {
            noodles = new LaomaDumplings();
        } else if (Constant.PEAS_NOODLES.equals(type)) {
            noodles = new PeasNoodles();
        } else {
            noodles = new CQColdNoodles();
        }
        return noodles;
    }
}

这样我就可以在面馆子类中决定Noodles的种类和口味啦。对于牛肉面Constant.BEEF_NOODLES ,在成都面馆中我做成都口味的牛肉面CDBeefNoodles,在重庆面馆中我做适合重庆人口味的牛肉面CQBeefNoodles。超类从来不管细节,子类会自行负责这一切。工厂方法模式通过让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的。
技术分享(图b)
技术分享(图c)
工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个,工厂方法让类把实例化推迟到了子类。我要新开店,只需创建restaurant子类继承NoodleRestaurant即可,我要改变成都店的菜单,只需更改成都店即可。

依赖倒置原则

不能让高层组件依赖低层组件,而且,不管高层或底层组件,都应该依赖于抽象。图a中的依赖是自上而下的,高层组件NoodleRestaurant依赖具体的低层组件。使用了工厂方法模式之后,依赖关系变成了这样:
技术分享
高层组件NoodleRestaurant依赖同层次的抽象组件Noodles,而CDBeefNoodles等低层组件转而依赖高层组件Noodles,这跟一开始的高层组件依赖低层组件是不是相反了呢?因此称为依赖倒置。依赖倒置可以使我们的设计松耦合,更有弹性,更容易扩展。在设计中遵循依赖倒置,应该遵守以下原则:
1. 变量不可以持有具体类的引用
2. 不要让类派生自具体类,请派生自一个抽象
3. 不要覆盖基类中已实现的方法;如果覆盖基类已实现的方法,那么你的基类就不是一个真正适合被继承的抽象,基类中已实现的方法应该由所有的子类共享。

抽象工厂模式

抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。抽象工厂模式是用来创建整个产品家族的,而工厂方法模式单单是创建一个产品,抽象工厂模式中利用的就是工厂方法模式。
工厂方法使用继承,把对象的创建委托给子类,子类实现工厂方法来创建对象。抽象工厂使用对象组合,对象的创建被实现在工厂接口所暴露出来的方法中。它们都是通过减少应用程序和具体类之间的依赖促进松耦合。
每种面食的制作流程无非都是:准备食材、煮面、盛碗。差别就在准备食材,毕竟每种面食所需的具体材料是不同的嘛:面条、肉、酱料、蔬菜等都是不一样的。

public abstract class Noodles {
    //面条
    Flour flour;
    //肉
    Meat meat;
    //酱料
    Sauce sauce;
    //蔬菜
    Vegetables vegetables;

    //准备食材
    public abstract void prepare();

    //煮面
    public void cook() {
        System.out.println("将原料加工成熟食.");
    }

    //盛碗
    public void bowl() {
        System.out.println("将食物装入碗中.");
    }
}

准备4种食材的工作由prepare() 完成,具体逻辑由子类实现,那么又有新的问题了,食材从哪里来?谁提供呢?我们打算在成都和重庆各建一所原料工厂,先把工厂建起来吧。

/**
 * 原料工厂
 * Created by sarahzhou on 16/6/21.
 */
public interface IngredientFactory {

    //面粉
    Flour createFlour();

    //肉
    Meat createMeat();

    //酱料
    Sauce createSauce();

    //蔬菜
    Vegetables createVegetables();
}
/**
 * 成都原料工厂
 * Created by sarahzhou on 16/6/21.
 */
public class CDIngredientFactory implements IngredientFactory {

    //成都产的面粉
    @Override
    public Flour createFlour() {
        return new CDFlour();
    }

    @Override
    public Meat createMeat() {
        return new BeefMeat();
    }

    //成都口味的酱料;
    @Override
    public Sauce createSauce() {
        return new CDSauce();
    }

    //不同地区的人喜欢吃的蔬菜也不一样,成都人喜欢吃豌豆尖;
    @Override
    public Vegetables createVegetables() {
        return new CDVegetables();
    }
}
/**
 * 重庆原料工厂
 * Created by sarahzhou on 16/6/21.
 */
public class CQIngredientFactory implements IngredientFactory {

    //重庆产的面粉;
    @Override
    public Flour createFlour() {
        return new CQFlour();
    }

    //猪肉;
    @Override
    public Meat createMeat() {
        return new PorkMeat();
    }

    //重庆口味的酱料;
    @Override
    public Sauce createSauce() {
        return new CQSauce();
    }

    //重庆人喜欢吃生菜;
    @Override
    public Vegetables createVegetables() {
        return new CQVegetables();
    }
}

原料工厂建造完毕了,面馆开始进货营业吧。

public abstract class NoodleRestaurant {
    public Noodles orderNoodles(String type) {
        Noodles noodles;
        //调用子类实现的createNoodles方法;
        noodles = createNoodles(type);
        noodles.prepare();
        noodles.cook();
        noodles.bowl();
        return noodles;
    }

    protected abstract Noodles createNoodles(String type);
}
/**
 * 成都面馆
 * Created by sarahzhou on 16/6/21.
 */
public class CDNoodleRestaurant extends NoodleRestaurant {
    IngredientFactory ingredientFactory;

    //绑定原料工厂
    public CDNoodleRestaurant(IngredientFactory ingredientFactory) {
        this.ingredientFactory = ingredientFactory;
    }

    @Override
    protected Noodles createNoodles(String type) {
        Noodles noodles;
        if (Constant.BEEF_NOODLES.equals(type)) {
            noodles = new CDBeefNoodles(ingredientFactory);
        } else if (Constant.DAN_DAN_NOODLES.equals(type)) {
            noodles = new DanDanNoodles(ingredientFactory);
        } else if (Constant.INTESTINES_NOODLES.equals(type)) {
            noodles = new IntestinesNoodles(ingredientFactory);
        } else if (Constant.SWEET_NOODLES.equals(type)) {
            noodles = new SweetNoodles(ingredientFactory);
        } else if (Constant.BURNING_NOODLES.equals(type)) {
            noodles = new BurningNoodles(ingredientFactory);
        } else {
            noodles = new CDColdNoodles(ingredientFactory);
        }
        return noodles;
    }
}
/**
 * 重庆面馆
 * Created by sarahzhou on 16/6/21.
 */
public class CQNoodleRestaurant extends NoodleRestaurant {
    IngredientFactory ingredientFactory;

    //绑定原料工厂
    public CQNoodleRestaurant(IngredientFactory ingredientFactory) {
        this.ingredientFactory = ingredientFactory;
    }

    @Override
    protected Noodles createNoodles(String type) {
        Noodles noodles;
        if (Constant.BEEF_NOODLES.equals(type)) {
            noodles = new CQBeefNoodles(ingredientFactory);
        } else if (Constant.SMALL_NOODLES.equals(type)) {
            noodles = new SmallNoodles(ingredientFactory);
        } else if (Constant.LAOMA_DUMPLINGS.equals(type)) {
            noodles = new LaomaDumplings(ingredientFactory);
        } else if (Constant.PEAS_NOODLES.equals(type)) {
            noodles = new PeasNoodles(ingredientFactory);
        } else {
            noodles = new CQColdNoodles(ingredientFactory);
        }
        return noodles;
    }
}

相应地,我们的Noodles实现类就变成了以下这样,把工厂传给每个面食,以便面食能从工厂中取得原料:

public class BurningNoodles extends Noodles {
    IngredientFactory ingredientFactory;

    public BurningNoodles(IngredientFactory ingredientFactory) {
        this.ingredientFactory = ingredientFactory;
    }

    @Override
    public void prepare() {
        System.out.println("Preparing burning noodles.");
        flour = ingredientFactory.createFlour();
        meat = ingredientFactory.createMeat();
        sauce = ingredientFactory.createSauce();
        vegetables = ingredientFactory.createVegetables();
    }
}

好了,回头看看我们的代码,有底层组件依赖高层组件的吗?显然没有了,咦,有客人来了,担担面一碗:

public class FactoryTestDrive {
    @Test
    public void test1() {
        //创建成都原料工厂
        IngredientFactory ingredientFactory = new CDIngredientFactory();
        //创建成都面馆,并绑定原料工厂
        NoodleRestaurant noodleRestaurant = new CDNoodleRestaurant(ingredientFactory);
        //担担面一碗
        noodleRestaurant.orderNoodles(Constant.DAN_DAN_NOODLES);
    }

}

技术分享
看到没,具体类型都是在运行时确定的。

Spring中的控制反转、依赖注入

提到Spring,不得不提控制反转(Inversion of Control)和依赖注入(Dependency Injection)。传统的创建对象的方式是new,这是个主动的行为,控制权在使用者,我需要哪个对象,我去new就完事儿了。Spring是个大容器,也是个大工厂,我只需要在Spring的配置文件中配置我的依赖,告诉Spring我需要什么,Spring工厂会解析这个配置文件,把我的依赖对象创建好,注入到我的实例中。这样子,控制权就移交给了Spring工厂,这就叫做控制反转、依赖注入。

工厂模式

标签:

原文地址:http://blog.csdn.net/chi_wawa/article/details/51728468

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!