工厂设计模式

类型:
工厂设计模式分为简单工厂、工厂方法、抽象工厂。

作用:
封装对象的创建。

一、简单工厂

以披萨店售卖披萨为例。
一家披萨店肯定不止一种披萨,所以建一个披萨超类,所有披萨都继承它。

abstract class Pizza {

    String name;

    public void prepare() {
        System.out.println("通用: 准备");
    }

    public void bake() {
        System.out.println("通用:烘烤");
    }

    public void cut() {
        System.out.println("通用:切片");
    }

    public void box() {
        System.out.println("通用:打包");
    }
}

所有的披萨继承Pizza超类,超类中设置了一些制作披萨通用的方法。
我们需要:辣条披萨、老干妈披萨和奶酪披萨。

class LatiaoPizza extends Pizza {

    public LatiaoPizza() {
        name = "辣条披萨";
        System.out.println("name: " + name);
    }

    /**
     * 子类可以根据实际需求覆盖父类方法
     */
    @Override
    public void prepare() {
        System.out.println(name + ":准备一些辣条");
    }
    
    @Override
    public void cut() {
        System.out.println(name + ":切成辣条形状");
    }
}

class LaoganmaPizza extends Pizza {

    public LaoganmaPizza(){
        name = "老干妈披萨";
        System.out.println("name: " + name);
    }

    @Override
    public void prepare() {
        System.out.println(name + ":准备一些老干妈");
    }
}

class CheesePizza extends Pizza {

    public CheesePizza() {
        name = "奶酪披萨";
        System.out.println("name: " + name);
    }

}

披萨超类里面设置一些披萨通用的方法,子类披萨可以根据的自己的需求决定是否覆盖。

披萨店里有很多披萨,我们可以用一个工厂类来管理披萨种类,工厂是一种称呼。
根据披萨类型创建相应的披萨。

class SimplePizzaFactory {

    public Pizza createPizza(String type){
        Pizza pizza = null;
        if (type.equals("cheese")){
            pizza = new CheesePizza();
        }else if (type.equals("辣条")){
            pizza = new LatiaoPizza();
        }else if (type.equals("老干妈")){
            pizza = new LaoganmaPizza();
        }
        return pizza;
    }
}

最后再创建一个我们的披萨店制作披萨的流程类。PizzaStore

class PizzaStore {

    private SimplePizzaFactory mSimplePizzaFactory = null;

    public PizzaStore(SimplePizzaFactory factory) {
        this.mSimplePizzaFactory = factory;
    }

    public Pizza orderPizza(String type) {
        Pizza pizza;
        //使用简单工厂创建披萨
        pizza = mSimplePizzaFactory.createPizza(type);
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
}

到这里我们的披萨店就可以开业卖披萨了,可以尝试点上一份披萨了。

  public static void main(String[] args) {

        PizzaStore pizzaStore = new PizzaStore(new SimplePizzaFactory());

        //奶酪披萨
        Pizza pizza = pizzaStore.orderPizza("cheese");
        //老干妈披萨
        pizza = pizzaStore.orderPizza("老干妈");
        //辣条披萨
        pizza = pizzaStore.orderPizza("辣条");


    }

运行结果如下:

name: 奶酪披萨
通用: 准备
通用:烘烤
通用:切片
通用:打包

name: 老干妈披萨
老干妈披萨:准备一些老干妈
通用:烘烤
通用:切片
通用:打包

name: 辣条披萨
辣条披萨:准备一些辣条
通用:烘烤
辣条披萨:切成辣条形状
通用:打包

因为披萨的类型有很多种,有可能会增加或者减少,所以使用简单工厂方法将披萨类的实例进行单独封装,用户类PizzaStore不用再new对象,将对象的创建交给工厂来做。
简单工厂严格来说不算工厂模式,更像是一种良好的编程习惯 – 将变化的地方进行封装。(封装变化)。

使用静态方式定义简单工厂称为静态工厂

静态工厂不需要使用创建对象的方法来实例化对象。

二、工厂方法模式

如果我们的披萨店经营的很好,需要到四川、浙江、上海等其他地方开分店。并且每个地方的披萨可能会有每个地方的口味。这样仅用一个SimplePizzaFactory无法满足我们的需求。我们可以根据SimplePizzaFactory写出各地的工厂方法。比如四川的SCPizzaStore,浙江的ZJPizzaStore

如果需要实现不同地区创建不同口味的披萨,此时我们就要把createPizza拿回PizzaStore了,并且把它设置为抽象,每个子类都必须实现它,创建不同口味的披萨。

abstract class PizzaStore {

    /**
     * 订购披萨
     */
    public Pizza orderPizza(String type){
        Pizza pizza;
        //将CreatePizza方法从工厂对象中移回PizzaStore
        pizza = createPizza(type);

        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();

        return pizza;
    }

    /**
     * 工厂方法:
     * 用来处理对象的创建,并将创建对象的行为封装在子类中。
     * 这样超类的代码就和子类对象创建代码解耦了
     */
    abstract Pizza createPizza(String type);
}

再来创建我们的四川SCPizzaStore和浙江ZJPizzaStore口味的PizzaStore

//四川风格
class SCPizzaStore extends PizzaStore {

    /**
     * 实现createPizza()方法,子类决定实例化哪一种具体Pizza,实现与父类解耦
     */
    @Override
    Pizza createPizza(String type) {
        if (type.equals("麻辣")) {
            return new SCStyle2Pizza();
        } else if (type.equals("香辣")){
            return new SCStyle1Pizza();
        }
        return null;
    }
}
//浙江风格
class ZJPizzaStore extends PizzaStore {
    @Override
    Pizza createPizza(String type) {
        if (type.equals("水煮")) {
            return new ZJStyle1Pizza();
        } else if (type.equals("清蒸")) {
            return new ZJStyle2Pizza();
        }
        return null;
    }
}

SCPizzaStore 和ZJPizzaStore 都继承自超类PizzaStore ,实现其createPizza()方法。

这样做的好处是:将对象的创建延迟到子类中,父类不在关心子类如何创建对象,实现了解耦。

我们的披萨也可能需要根据不同的地区来做出调整,此时我们超类Pizza调整为:

abstract class Pizza {

    String name;    //名字
    String dough;   //面团类型
    String sauce;   //调料
    ArrayList toppings = new ArrayList();


    public void prepare() {
        System.out.println("准备:" + name);
        System.out.print("上面添加调料:");
        for (int i = 0; i < toppings.size(); i++) {
            System.out.println("    " + toppings.get(i));
        }
    }

    //烘焙
    public void bake() {
        System.out.println("通用:烘烤方式");
    }

    //切片
    public void cut() {
        System.out.println("通用:切片方式");
    }

    //打包
    public void box() {
        System.out.println("通用:打包方式");
    }

    public String getName() {
        return name;
    }
}

创建四川口味的披萨和浙江口味的披萨。都继承自Pizza超类。

//香辣披萨
class SCStyle1Pizza extends Pizza {

    public SCStyle1Pizza(){
        name = "四川:香辣披萨";
        dough = "厚饼";
        sauce = "配料:香精,红辣椒,辣椒油";

        toppings.add("椒盐");
    }

    /**
     * 覆盖父类切法
     */
    @Override
    public void cut() {
        System.out.println("四川香辣披萨专属切法");
    }
}
//麻辣披萨
class SCStyle2Pizza extends Pizza {

    public SCStyle2Pizza(){
        name = "四川:麻辣披萨";
        dough = "厚饼";
        sauce = "配料:麻,辣椒油";

        toppings.add("上番茄酱");
    }

    @Override
    public void cut() {
        System.out.println("四川麻辣披萨专属切法");
    }
}

//水煮披萨
class ZJStyle1Pizza extends Pizza {

    public ZJStyle1Pizza(){
        name = "浙江:水煮披萨";
        dough = "薄饼";
        sauce = "配料:蛤蜊";

        toppings.add("鱼油");
    }
    //水煮披萨不用切,所以覆盖父类cut方法
    @Override
    public void cut() {
    }
}
//清蒸披萨
class ZJStyle2Pizza extends Pizza {
    public ZJStyle2Pizza(){
        name = "浙江:清蒸披萨";
        dough = "薄饼";
        sauce = "配料:虾仁";
        toppings.add("小葱");
    }
}

到这里我们就可以点上一份香辣披萨和水煮披萨了

    public static void main(String[] args) {

        //订购一个四川口味的香辣披萨和一个浙江口味的水煮披萨
        PizzaStore pizzaStore = new SCPizzaStore();
        pizzaStore.orderPizza("香辣");

        pizzaStore = new ZJPizzaStore();
        pizzaStore.orderPizza("水煮");

    }

运行结果:

准备:四川:香辣披萨
上面添加调料:    椒盐
通用:烘烤方式
四川香辣披萨专属切法
通用:打包方式

准备:浙江:水煮披萨
上面添加调料:    鱼油
通用:烘烤方式
通用:打包方式

总结

工厂方法是用来处理对象的创建,并将这样的行为封装到子类中。这样超类的代码就和子类的创建解耦了

工厂方法模式定义:

定义了一个创建对象的接口,但由子类决定要实例化哪一个。工厂方法让类把实例化推迟到子类。

:定义参照Head First。在Head First中超类也被称作接口。

说明:在此例中PizzaStore为创建对象的接口,createPizza()为工厂方法,定义为抽象类型,每个子类都必须实现createPizza()方法,这样就达到了将类的实例推迟到子类中实现的效果。

问:PizzaStore为什么也定义成abstract抽象类型的呢?
符合设计原则 – 依赖倒置原则:依赖抽象,不依赖具体类。

问:简单工厂和工厂方法模式的区别?
简单工厂把类的创建都封装在一个地方创建,而工厂方法则是创建一个框架,由子类决定如何实现,更有弹性。

三、抽象工厂

加入我们的披萨店已经开了很多分店了,为了早日上市捞钱我们需要做到从原料到制作一条龙服务。因此我们现在需要解决原料的问题了。

1、创建原料工厂,负责生产披萨所需要的原料。
interface PizzaIngredientFactory {

     //创建面团
     Dough createDough();
     //创建调料
     Sauce createSauce();
     //创建蔬菜
     Veggies[] createVeggies();
     //创建火腿
     Pepperoni createPepperoni();
}
2、创建各地区的原料工厂

创建四川原料工厂

//四川原料工厂,返回四川口味披萨所需要的材料
class SCPizzaIngredientFactory implements PizzaIngredientFactory{

    @Override
    public Dough createDough() {
        //四川面
        return new SCDough();
    }

    @Override
    public Sauce createSauce() {
        //四川调料
        return new SCSauce();
    }

    @Override
    public Veggies[] createVeggies() {
        //蔬菜
        Veggies veggies[] = {new TomatoVeggies(), new CukeVeggies()};
        return veggies;
    }

    @Override
    public Pepperoni createPepperoni() {
        //鸡肉火腿
        return new ChickenPepperoni();
    }
}

创建浙江原料工厂

//浙江原料工厂,返回浙江口味披萨所需要的材料
class ZJPizzaIngredientFactory implements PizzaIngredientFactory{

    @Override
    public Dough createDough() {
        //浙江和四川都是用一种面
        return new SCDough();
    }

    @Override
    public Sauce createSauce() {
        //浙江调料
        return new ZJSauce();
    }

    @Override
    public Veggies[] createVeggies() {
        //蔬菜
        Veggies veggies[] = {new TomatoVeggies(), new CukeVeggies()};
        return veggies;
    }

    @Override
    public Pepperoni createPepperoni() {
        //鱼肉火腿
        return new FishPepperoni();
    }
}

3、重做Pizza
abstract class Pizza {
    //每个披萨都会持有一组准备时会用到的原料
    String name;
    Dough mDough;
    Sauce mSauce;
    Veggies mVeggies[];
    Pepperoni mPepperoni;

    /**
     * 把prepare()声明成抽象
     * 因为不同地区准备的原料可能不同,所以由子类从原料工厂中自己索取
     */
    abstract void prepare();

    void bake(){
        System.out.println("通用: 烘烤30分钟");
    }

    void cut(){
        System.out.println("通用:切成三角形");
    }

    void box(){
        System.out.println("通用:使用正方盒子打包");
    }

    public String getName() {
        return name;
    }

}

继续重做香辣披萨

class XLPizza extends Pizza {

    PizzaIngredientFactory mIngredientFactory;

    /**
     * 构造方法初始化一个抽象工厂向pizza提供准备所需要的原料
     * pizza只需要向工厂索取原料就行,不关心具体是什么地区的工厂。
     * 实现pizza和区域原料之间的解耦
     */
    public XLPizza(PizzaIngredientFactory pizzaIngredientFactory){
        this.mIngredientFactory = pizzaIngredientFactory;
        name = "香辣披萨";
    }

    @Override
    void prepare() {
        System.out.println("准备:" + name);
        //初始化原料,从原料工厂中获取原料
        mDough = mIngredientFactory.createDough();
        mSauce = mIngredientFactory.createSauce();
        mVeggies = mIngredientFactory.createVeggies();
        mPepperoni = mIngredientFactory.createPepperoni();
        System.out.println("准备成功: 从原料工厂获得原料");
    }
}

清蒸披萨

class QZPizza extends Pizza {

    PizzaIngredientFactory mIngredientFactory;

    /**
     * 使用抽象工厂向pizza提供准备所需要的原料
     * pizza只需要向工厂索取原料就行,不关心具体是什么地区的工厂。
     * 实现pizza和区域原料之间的解耦
     */
    public QZPizza(PizzaIngredientFactory ingredientFactory){
        this.mIngredientFactory = ingredientFactory;
        name = "清蒸披萨";
    }

    /**
     * 从工厂中获取原料
     */
    @Override
    void prepare() {
        System.out.println("准备:" + name);
        mDough = mIngredientFactory.createDough();
        mSauce = mIngredientFactory.createSauce();
        mVeggies = mIngredientFactory.createVeggies();
        mPepperoni = mIngredientFactory.createPepperoni();
        System.out.println("准备成功: 从原料工厂获得原料");
    }
}

披萨代码利用相关工厂生产原料,所生产工的原料依赖所使用的工厂,Pizza不关心这些原料。Pizza和区域原料之间被解耦。

4、回到披萨店

四川披萨店

class SCPizzaStore extends PizzaStore {

    @Override
    Pizza createPizza(String type) {
        Pizza pizza = null;
        //四川披萨店原料由四川原料工厂提供
        PizzaIngredientFactory ingredientFactory = new SCPizzaIngredientFactory();
        System.out.println("获得四川原料工厂");
        if (type.equals("香辣")){
            //将工厂传递给披萨
            pizza = new XLPizza(ingredientFactory);
        }else if (type.equals("麻辣")){
            pizza = new MLPizza(ingredientFactory);
        }
        return pizza;
    }

}

浙江的披萨店:

class ZJPizzaStore extends PizzaStore {

    @Override
    Pizza createPizza(String type) {
        Pizza pizza = null;
        //浙江披萨店原料由浙江原料工厂提供
        PizzaIngredientFactory ingredientFactory = new ZJPizzaIngredientFactory();
        System.out.println("获得浙江原料工厂");
        if (type.equals("清蒸")){
            //将工厂传递给披萨
            pizza = new QZPizza(ingredientFactory);
        }else if (type.equals("水煮")){
            pizza = new SZPizza(ingredientFactory);
        }
        return pizza;
    }

到这里我们已经完工了,可以点一份披萨了

    public static void main(String[] args) {

        //订购一个香辣披萨一个清蒸披萨
        PizzaStore pizzaStore = new SCPizzaStore();
        pizzaStore.orderPizza("香辣");

        pizzaStore = new ZJPizzaStore();
        pizzaStore.orderPizza("清蒸");
    }

运行结果:

获得四川原料工厂
准备:香辣披萨
准备成功: 从原料工厂获得原料
通用: 烘烤30分钟
通用:切成三角形
通用:使用正方盒子打包

获得浙江原料工厂
准备:清蒸披萨
准备成功: 从原料工厂获得原料
通用: 烘烤30分钟
通用:切成三角形
通用:使用正方盒子打包

经过上面的改造,我们做了什么?
引入了新类型的工厂,也就是抽象工厂来创建披萨的原料家族。
通过抽象工厂所提供的接口,可以创建产品的家族,利用这个接口书写代码,代码可以从实际工厂解耦,以便在不同的上下文中使用各式各样的工厂,制造出各种不同的产品。例如:不同区域,不同外观,不同操作系统。

抽象工厂模式定义:

提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。

抽象工厂允许客户使用抽象的接口来创建一组相关的产品,而不需要知道(或关心)实际产出的具体产品是什么。这样一来,客户就从具体的产品中被解耦

本例中原料工厂PizzaIngredientFactory就是一个被抽象出来的工厂,Pizza只需要向抽象的原料工厂PizzaIngredientFactory获取原料就行,而不需要关心是四川的原料工厂还是浙江的原料工厂。

同时可以看到抽象工厂里的每个创建对象的方法都被声明为抽象的,并且子类覆盖并创建对象,这其实是以工厂方法的方式实现的。

问:抽象工厂和工厂方法的区别?
1、工厂方法使用的是继承:把对象的创建委托给子类,子类实现工厂方法来创建对象。
抽象工厂使用对象组合:对象的创建被实现在工厂接口所暴露出来的方法中

2、抽象工厂适合创建产品家族和想让制造的相关产品集合起来。
工厂方法可以将客户代码从需要实例化的具体类中解耦。

依赖倒置原则:依赖抽象,不依赖具体类

参考:<<Head First 设计模式>>

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值