工厂模式(factory Pettern)

"Head First 学习笔记(四)"

Posted by just on February 20, 2017

前言

一看日期吓一跳,已经一周多没有写笔记了。除了懒之外还是懒….其实之前认真思考过,是否应该花大量的时间去排版笔记去写bolg,想想还是应该写下去,除了给予自己反馈增加自己的动力确实能让自己学习到的东西再梳理一遍增加知识在脑袋里的存储时间。

正文

工厂模式无论是在平时开发还是各种源码中都经常出现的,它细分为三种 :

  • 简单工厂
  • 工厂方法
  • 抽象工厂

虽然分类较多,不过具体的作用都是将对象创建的过程封装起来,以便将代码从具体类解耦。重点介绍较为复杂的抽象工厂,简单工厂和工厂发方法我们应该在平时都经常用到,很容易理解。

简单工厂

那么我们先来实现一个小目标吧,比如说,开一个🍕披萨店!

披萨的制作流程呢,先要准备材料然后烤制、分割、装盘一个披萨就制作完成了!(😄就是这么简单)

我们有很多口味的披萨,但所有的披萨都要经过这个流程,唯一的差别就是面饼不同,为了节省店面的成本和方便以后开连锁店我们需要将面饼的制作过程给独立出来,交给代工厂来制作,这样我们的披萨店🍕可以省下很多的精力。

这就是简单工厂,简单工厂其实并不是一种设计模式,反而像是一种编程习惯。它将一个项目中需要变化的部分以接口的形式抽象出来,通过创建不同Factory实例来完成抽象的功能的具体实现从而达到解耦。

简单工厂

工厂方法

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

但是将面饼的制作交给外面的工厂成本太高,且改良工艺沟通起来非常困难,而且连锁店开起来后我们发现那些小店并不愿意承担让别人制作的成本😠,于是我们改进了经营制度。由我们提供披萨的制作方法,而连锁店他们自己来制作🍕Pizza这样仍可以保证双方的利益关系。

工厂方法

工厂方法猛一看与静态工厂类似,都是将具体方法抽象出去,只不过一个是继承,另一个是接口罢了。但其实它们的用法还是挺不同的,工厂方法的子类看起来很像是简单工厂。简单工厂把全部的事情在一个地方都处理完了,然而工厂方法却是创建一个框架,让子类决定要如何实现。这种做法更具备弹性,可以修改正在创建的类,同时也仍具有对扩展的方便性,如果有很多相同的实现也可以提供一个默认的实现类来解决代码重复的问题。

抽象工厂

接下来的就是今天的正餐了,如果你对前面没有看到代码而不爽那么你可以在抽象工厂的代码中看到它们的影子哦。

前不久我们的🍕披萨店已经发展成为和黄焖鸡米饭、沙县小吃以及兰州拉面一个级别的商业帝国了。越来越多连锁店的各种不同的问题冲击着我们的总部,如果要处理这些问题我们总部将变得臃肿不堪。于是我们又准备改革创兴了!

我们先需要一个给我们提供原材料的工厂,全国那么多地方我们不可能都用一个工厂提供,所以我们提供一个规范,如果想要跟我们合作就必须符合这个规范 :

/**
 * Pizza工厂的接口
 * 在接口中每一种原料都有一个对应的方法创建该原料
 */
public interface PizzaIngredientFactory {
   
    public Cheese createCheese();

    public Dough createDough();

    public Sauce createSauce();

    public Clam createClam();
}

然后我们同样要给Pizza连锁店提供规范免得他们瞎搞坏了我们的招牌,毕竟一个差评递上是个好评不是吗!当然也不能太严厉什么都管,那样就会太糟糕了。

/**
*创建一个Pizza商店规范
*/
public abstract class PizzaStore {
    public abstract Pizza createPizza(String name);
}

什么?在规范上太宽松?Native! 这只是🍕店的流程规范,而Pizza的制作规范还没有开始呢,我们制作了一个一个制作Pizza的机器,嘿嘿。店家只需要将机器搬到店里然后把制作好的Pizza面饼放置到机器里制作就可以了,像那啥,正新鸡排不就是这样的吗。

/**
 * 创建一个Pizza的制作机器。
 */
public abstract class Pizza {
    public String name;
    public Dough dough;
    public Cheese cheese;
    public Sauce sauce;
    public Clam clam;

    //搜集原料
    abstract void prepare();

    public void bake() {
        System.err.println("Back 25 minutes 350");
    }

    public void cut() {
        System.err.println("Cutting the pizza into diagonal slices");
    }

    public void box() {
        System.err.println("Place pizza in official PizzaStore box");
    }
}

接下来我们就开始大多阔斧的改革了,先在NY试行,NY是什么地方?谁知道呢。

先找到NY的一家工厂让他们帮我们生产材料 :

public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
    @Override
    public Cheese createCheese() {
        return new Cheese();
    }

    public Clam createClam() {
        return new Clam();
    }

    @Override
    public Dough createDough() {
        return new Dough();
    }

    @Override
    public Sauce createSauce() {
        return new Sauce();
    }
}

然后整一个店出来,只需要在任何地方使用app或网页应用中输入想要点的的Pizza的名称就可以制作并且送到了 :

public class NYPizzaStore extends PizzaStore {

    @Override
    public Pizza createPizza(String name) {
        Pizza pizza = null;
        NYPizzaIngredientFactory factory = new NYPizzaIngredientFactory();
        if (name.equals("cheese")) {
            pizza = new CheesePizza(factory);
        } else if (name.equals("clam")) {
            pizza = new ClamPizza(factory);
        }
        return pizza;
    }
}

机器不是Pizza吗?那些不同种类的Pizza哪里的呢,嗯哼你问的很有道理,我正准备讲的(才没忘!)。Pizza可以制作各种披萨,为了方便店家创新我们设计为只要按照规范就可以制作自己的招牌Pizza。

以ClamPizza来举例:

public class ClamPizza extends Pizza {
    private PizzaIngredientFactory pizzaIngredientFactory;

    public ClamPizza(PizzaIngredientFactory pizzaIngredientFactory) {
        this.pizzaIngredientFactory = pizzaIngredientFactory;
    }

    @Override
    void prepare() {
      //我们可以在这里组合各种原材料 现在用到了特别海鲜Clam
        dough = pizzaIngredientFactory.createDough();
        sauce = pizzaIngredientFactory.createSauce();
        clam = pizzaIngredientFactory.createClam();
    }
}

这样一个商业帝国就搭建成了哈哈。这就是抽象工厂,先来看看抽象工厂的结构图 :

抽象工厂

臭线工厂允许客户使用抽象的接口来创建一组相关的产品,而不需要关心实际产出的具体产品是是什么。这样一来,客户就从具体的产品中被解耦出来,最后在让我们从我们🍕Pizza商业帝国的结构图来看看 :

Pizza抽象工厂

要点

  • 所有的工厂都是用来封装对象的创建
  • 简单工厂,虽然不是真正的设计模式,但仍不失为一个简单的方法,可以将客户程序从具体解耦。
  • 工厂方法使用继承:把对象的创建委托给子类,子类实现工厂方法来创建对象。
  • 抽象工厂使用对象组合:对象的创建被实现在工厂接口所暴露的方法中。
  • 所有工厂模式都通过减少应用程序和具体类之间的依赖促进耦合。
  • 工厂方法允许类将实例化延迟到子类进行。
  • 抽象工厂创建相关的对象家族,而不需要依赖它们的具体类。
  • 依赖导致原则,指导我们避免依赖具体类型,而要尽量依赖抽象。
  • 工厂是很有具威力的技巧,帮助我们针对抽象编程,而不要针对具体类编程。