开发之路(设计模式四:工厂模式下)

在之前内容里,介绍了工厂模式中的简单工厂和工厂方法内容,这我们继续工厂模式的学习,今天学习抽象工厂模式。

当直接实例化一个对象时,就是在依赖它的具体类。看一张对象依赖图
《开发之路(设计模式四:工厂模式下)》
很明显PizzaStore依赖所有披萨对象,所有的具体pizza都从PizzaStore当中产生。
当下面各个新增一种披萨种类,就相当于多了一个依赖,上面PizzaStore可能就要修改。

这里引出一个原则:依赖倒置原则。
注:要依赖抽象,不要依赖具体类。感觉有点像面向接口编程,而不面向实现编程。确实这很相似,然而这里更强调“抽象”.这个原则说明:不能让“高层组件”依赖“底层组件”,换句话说好比人要吃鸡鸭鱼肉等食物摄取营养,但是人不吃鸡鸭鱼肉也能从别的许多食物的摄取营养物质的把。所以二者不应该是依赖的关系,回过来说,“高层组件”和“底层组件”都要依赖抽象。

PizzaStore是“高层组件”,而各个披萨具体实现PizzaStore的具体实现类,就为“底层组件”,很明白,PizzaStore依赖这些具体比萨类。
《开发之路(设计模式四:工厂模式下)》

我们把依赖倒置原则,放在我们目前的披萨店项目中。类图如下
《开发之路(设计模式四:工厂模式下)》
想要遵循依赖倒置原则,(依赖抽象)工厂方式并不一定是最好的方法,但却是最有效方法之一。

何为“倒置”?
前面说“依赖”,现在来解释解释倒置原则,在上面当中我们看到各个pizza具体类(低层组件)向上指向了Pizza(高层组件),底层组件现在竟然依赖高层组件,对比图一当中对象(向下指向)依赖具体实现类情况,是不是指向都反过来了。所以说倒置指的就是和一般OO设计的思考方式完全相反。而现在我们的设计就让高层和底层组件都依赖Pizza这个抽象。

所以说若要遵循依赖倒置原则的话必须遵守下面几点要求。
1:变量不可以持有具体类的引用。
注:如果使用new,就会持有具体类的引用。你可以使用工厂模式避免这个问题。

2:不要让类派生自具体类。
注:如果派生自具体类,就会依赖这个具体类。请派生自一个抽象

3:不要覆盖基类中已经实现的方法。

编者注:包括我之前做的几期模式文章,当学习到工厂模式的时候我也有脑浆糊时候,倘若真的写项目程序之前都要想如何使用什么设计模式的话,恐怕我什么程序都写不出来,在设计模式中,无论有多少原则,其实是要尽量去达到,而不是说随时都要遵守原则或模式。其实很多时候任何Java程序在某种程度上来说都做不到百分之百遵守原则,都有违反方针地方,但设计模式可以作为一个项目的灵魂。
但是,如果你深入体验模式,将这些模式内化成你思考的一部分,那么在设计时,你将知道何时有足够的理由违反这些原则。就比方说当你实例化一个字符串的时候,你是不是直接实例化了?其实我们平时在程序中也都不假思索的实例化字符串对象吗?有违反这个原则吗?有!能这样做吗?能!为什么?因为字符串不能改变。包括我自己目前学习设计模式都还只是学“形”,我离设计模式的“心”还有不少的距离。

回到我们披萨店,现在看看我们披萨店“生意”是蒸蒸日上,具体弹性的框架,维护拓展起来也比最初设计的强很多,虽然花的功夫不少,但往往是值得的。
某一天你接到了好几个顾客的投诉电话,投诉的内容大体相同,某某加盟店里的食材不新鲜,甚至有媒体曝光你的某些加盟店为了降低成本,使用劣质原料。身为老板得你,必须采取一些办法,阻止这样的情况再次发生。
往后几天,你亲自考察了位于纽约和芝加哥的两家加盟店,经过研究决定为确保加盟店使用高质量的原料,你打算建造一家生产原料的工厂,并将原料运到各个加盟店专供,下面是你写的两份菜单
《开发之路(设计模式四:工厂模式下)》

现在我们建造一个工厂,来生产原料,
定义工厂接口,负责创建原料
这里有很多新类,每个原料都是一个类(接口),每个原料都有个create方法,到时候我们都要具体展开。
《开发之路(设计模式四:工厂模式下)》

下面具体创建纽约原料工厂,
每个原料都重载接口,根据不同风味定制添加不同的调料,对于蔬菜(Veggies),用一个数组做返回值,这里我就自己写死了数组,芝加哥工厂暂时不放出。
《开发之路(设计模式四:工厂模式下)》

这里Pizza就需要修改了,好让它能使用工厂生产出来的原料。
《开发之路(设计模式四:工厂模式下)》

下面是创建披萨,下面以芝士披萨为例,不在像之前那样做法了,写出区域性话,就单单创建某某味道的披萨
《开发之路(设计模式四:工厂模式下)》

下面图将给你更好的说明(我就直接引用书中笔记了)
《开发之路(设计模式四:工厂模式下)》

下面是修改加盟店代码以纽约店为例,纽约店会用到纽约披萨原料工厂,把工厂对象传入每个披萨里就能“获取”对应原料。
相比于之前的createPizza方法,这个版本两者之间有何异同呢?
《开发之路(设计模式四:工厂模式下)》

我们目前做了这些。
《开发之路(设计模式四:工厂模式下)》

这样的工厂形式就是抽象工厂模式
抽象工厂模式:提供一个接口,用于创建相关或依赖对象,而不需要明确指定具体类。
下面是抽象工厂类图
《开发之路(设计模式四:工厂模式下)》
抽象方法,给我感觉是它更多运用抽象形式,简化编码当中复杂性。

下面是工厂方法和抽象工厂两种模式的对比
工厂方法:
《开发之路(设计模式四:工厂模式下)》

抽象工厂:
《开发之路(设计模式四:工厂模式下)》

其实,无论简单工厂,工厂方法还是抽象工厂方法,都对我们提升代码质量有很大帮助。

要点:
1:所有的工厂都是用来封装对象的创建
2:简单工厂,虽然不算是设计模式,但却是一个很好的编码习惯,可以使客户和具体类解耦
3:工厂方法使用继承:把对象的创建放在子类,子类实现工厂方法。
4:抽象工厂模式使用对象组合:对象的创建被是现在工厂的接口中。
5:所有的工厂模式都是减少程序中类之间依赖,松耦合。
6:第一次了解原来实例化可以延迟到子类中进行。
7:学习了依赖倒置原则。
8:面向抽象编程,不面向具体类编辑,尽管自己还是会犯老毛病。

下面放出实例的全部源码,由于源码类文件较多,先放出了项目class图
《开发之路(设计模式四:工厂模式下)》

Material_Implements包下内容
《开发之路(设计模式四:工厂模式下)》

披萨工厂接口

package Factory_Interface;

import Material_Interface.Cheese;
import Material_Interface.Clams;
import Material_Interface.Dough;
import Material_Interface.Pepperoni;
import Material_Interface.Sauce;
import Material_Interface.Veggies;

/**
 * 这时我们需要给披萨店送原料了 建立一家生产原料的工厂 添加各种原料
 * 
 * @author Joy
 * 
 */
public interface PizzaIngredientFactory {
    // 面饼
    public Dough createDough();

    // 酱料
    public Sauce createSauce();

    // 芝士奶酪
    public Cheese createCheese();

    // 各种蔬菜,数组
    public Veggies[] createVeggies();

    // 香肠
    public Pepperoni createPepperoni();

    // 海鲜
    public Clams createClam();
}

两个具体工厂实现

package Factory_Implements;

import Factory_Interface.PizzaIngredientFactory;
import Material_Implements.FreshClams;
import Material_Implements.Garlic;
import Material_Implements.MarinaraSauce;
import Material_Implements.Mushroom;
import Material_Implements.Onion;
import Material_Implements.RedPepper;
import Material_Implements.ReggianoCheese;
import Material_Implements.SlicedPepperoni;
import Material_Implements.ThinCrustDough;
import Material_Interface.Cheese;
import Material_Interface.Clams;
import Material_Interface.Dough;
import Material_Interface.Pepperoni;
import Material_Interface.Sauce;
import Material_Interface.Veggies;

/**
 * 纽约披萨店原料工厂实现,工厂专精大蒜番茄酱料,干酪,鲜蛤蛎
 * 
 * @author Joy
 * 
 */
// 具体的原料工厂都要实现PizzaIngredientFactory类
public class NYPizzaIngredientFactory implements PizzaIngredientFactory {

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

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

    @Override
    public Cheese createCheese() {
        return new ReggianoCheese();
    }

    @Override
    public Veggies[] createVeggies() {
        Veggies veggies[] = { new Garlic(), new Onion(), new Mushroom(),
                new RedPepper() };
        return veggies;
    }

    @Override
    public Pepperoni createPepperoni() {
        return new SlicedPepperoni();
    }

    @Override
    public Clams createClam() {
        return new FreshClams();
    }
}
package Factory_Implements;

import Factory_Interface.PizzaIngredientFactory;
import Material_Implements.BlackOlives;
import Material_Implements.Eggplant;
import Material_Implements.FrozenClams;
import Material_Implements.MozzarellaCheese;
import Material_Implements.PlumTomatoSauce;
import Material_Implements.SlicedPepperoni;
import Material_Implements.Spinach;
import Material_Implements.ThickCrustDough;
import Material_Interface.Cheese;
import Material_Interface.Clams;
import Material_Interface.Dough;
import Material_Interface.Pepperoni;
import Material_Interface.Sauce;
import Material_Interface.Veggies;

/**
 * 加州披萨店原料工厂
 * 
 * @author Joy
 * 
 */
public class ChicagoPizzaIngredientFactory implements PizzaIngredientFactory {
    @Override
    public Dough createDough() {
        return new ThickCrustDough();
    }

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

    @Override
    public Cheese createCheese() {
        return new MozzarellaCheese();
    }

    @Override
    public Veggies[] createVeggies() {
        Veggies veggies[] = { new BlackOlives(), new Spinach(), new Eggplant() };
        return veggies;
    }

    @Override
    public Pepperoni createPepperoni() {
        return new SlicedPepperoni();
    }

    @Override
    public Clams createClam() {
        return new FrozenClams();
    }
}

各个原料接口

package Material_Interface;
/**
 * 芝士奶酪类
 * @author Joy
 *
 */
public interface Cheese {
    //只需定义接口,具体实现让实现类搞定
    public String toString();

}
package Material_Interface;
/**
 * 海蛎类
 * @author Joy
 *
 */
public interface Clams {
    public String toString();
}
package Material_Interface;
/**
 * 面饼类
 * @author Joy
 *
 */
public interface Dough {
    public String toString();
}
package Material_Interface;
/**
 * 香肠类
 * @author Joy
 *
 */
public interface Pepperoni {
    public String toString();
}
package Material_Interface;
/**
 * 酱料类
 * @author Joy
 *
 */
public interface Sauce {
    public String toString();
}
package Material_Interface;
/**
 * 蔬菜类
 * @author Joy
 *
 */
public interface Veggies {
    public String toString();
}

下面就是各个原料的具体实现

package Material_Implements;

import Material_Interface.Veggies;

public class BlackOlives implements Veggies {

    public String toString() {
        return "黑橄榄";
    }
}
package Material_Implements;

import Material_Interface.Veggies;

public class Eggplant implements Veggies {

    public String toString() {
        return "茄子";
    }
}
package Material_Implements;

import Material_Interface.Clams;

public class FreshClams implements Clams {

    public String toString() {
        return "鲜海蛎";
    }
}
package Material_Implements;

import Material_Interface.Clams;

public class FrozenClams implements Clams {

    public String toString() {
        return "海湾的鲜海蛎";
    }
}
package Material_Implements;

import Material_Interface.Veggies;

public class Garlic implements Veggies {

    public String toString() {
        return "大蒜";
    }
}
package Material_Implements;

import Material_Interface.Sauce;

public class MarinaraSauce implements Sauce {
    @Override
    public String toString() {
        return "番茄酱";
    }
}
package Material_Implements;

import Material_Interface.Cheese;

public class MozzarellaCheese implements Cheese {

    @Override
    public String toString() {
        return "碎芝士";
    }
}
package Material_Implements;

import Material_Interface.Veggies;

public class Mushroom implements Veggies {

    public String toString() {
        return "香菇";
    }
}
package Material_Implements;

import Material_Interface.Veggies;

public class Onion implements Veggies {

    public String toString() {
        return "洋葱";
    }
}
package Material_Implements;

import Material_Interface.Sauce;

public class PlumTomatoSauce implements Sauce {
    public String toString() {
        return "梅子番茄酱";
    }
}
package Material_Implements;

import Material_Interface.Veggies;

public class RedPepper implements Veggies {

    public String toString() {
        return "红辣椒";
    }
}
package Material_Implements;

import Material_Interface.Cheese;

public class ReggianoCheese implements Cheese {
    
    public String toString() {
    
        return "干奶酪";
    }

}
package Material_Implements;

import Material_Interface.Pepperoni;

public class SlicedPepperoni implements Pepperoni {

    public String toString() {
        return "香肠切片";
    }
}
package Material_Implements;

import Material_Interface.Veggies;

public class Spinach implements Veggies {

    public String toString() {
        return "菠菜";
    }
}
package Material_Implements;

import Material_Interface.Dough;

public class ThickCrustDough implements Dough {
    public String toString() {
        return "厚面饼";
    }
}
package Material_Implements;

import Material_Interface.Dough;

public class ThinCrustDough implements Dough {
    public String toString() {
        return "薄面饼";
    }
}

下面是Pizza“工厂”(抽象)方法

package Pizza;

import Material_Interface.Cheese;
import Material_Interface.Clams;
import Material_Interface.Dough;
import Material_Interface.Pepperoni;
import Material_Interface.Sauce;
import Material_Interface.Veggies;

public abstract class Pizza {
    String name;// 披萨名称
    Dough dough;// 面团类型
    Sauce sauce;// 酱料
    // ArrayList toppings = new ArrayList();// 一套佐料(下面原料代替了list)

    // 每个披萨都持有一组需用到的原料
    Veggies veggies[];
    Cheese cheese;
    Pepperoni pepperoni;
    Clams clams;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    /**
     * //把prepare方法抽象化,获取来自原料工厂的原料 Pizza类完全不关心具体原料是什么,只知道如何制作披萨,
     * 比萨和原料之间被解耦,往后拓展不同口味披萨,建立不同的披萨工厂也很方便 Pizza类被复用
     */
    // 准备
    public abstract void prepare();

    // 烘烤
    public void bake() {
        System.out.println("\n" + "披萨正在烘烤,需烘烤25分钟。。。");
    }

    // 切片
    public void cut() {
        System.out.println("制作完成,给披萨切片。。。");
    }

    // 装盒
    public void box() {
        System.out.println("给披萨打包装盒。。。");
    }

    // 输出客人点的披萨信息
    @Override
    public String toString() {
        StringBuffer display = new StringBuffer();
        display.append(name + "\n");
        if (dough != null) {
            display.append(dough + "\t");
        }
        if (sauce != null) {
            display.append(sauce + "\n");
        }
        if (cheese != null) {
            display.append(cheese + "\t");
        }
        if (veggies != null) {
            for (int i = 0; i < veggies.length; i++) {
                display.append(veggies[i] + ", ");
            }
        }
        if (pepperoni != null) {
            display.append(pepperoni + "\n");
        }
        if (clams != null) {
            display.append(clams + "\t");
        }
        return display.toString();
    }

}

下面是具体Pizza实现

package Pizza;

import Factory_Interface.PizzaIngredientFactory;
//芝士披萨
public class CheesePizza extends Pizza {
    PizzaIngredientFactory ingredientFactory;

    // 要制作披萨,要先从对于工厂获取原料,所以构造器初始化数据
    public CheesePizza(PizzaIngredientFactory ingredientFactory) {
        this.ingredientFactory = ingredientFactory;
    }

    // 这里就不在以前Pizza类直接写死,而是更加灵活使用
    // prepare()方法一步步创建芝士披萨,需要的原料全部从“工厂”中获取
    @Override
    public void prepare() {
        System.out.println("准备 " + name);
        dough = ingredientFactory.createDough();
        sauce = ingredientFactory.createSauce();
        cheese = ingredientFactory.createCheese();
    }
}
package Pizza;

import Factory_Interface.PizzaIngredientFactory;
//海蛎披萨
public class ClamPizza extends Pizza {
    PizzaIngredientFactory ingredientFactory;

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

    public void prepare() {
        System.out.println("准备 " + name);
        dough = ingredientFactory.createDough();
        sauce = ingredientFactory.createSauce();
        cheese = ingredientFactory.createCheese();
        clams = ingredientFactory.createClam();
    }
}
package Pizza;

import Factory_Interface.PizzaIngredientFactory;
//香肠披萨
public class PepperoniPizza extends Pizza {
    PizzaIngredientFactory ingredientFactory;
 
    public PepperoniPizza(PizzaIngredientFactory ingredientFactory) {
        this.ingredientFactory = ingredientFactory;
    }
 
    public void prepare() {
        System.out.println("准备 " + name);
        dough = ingredientFactory.createDough();
        sauce = ingredientFactory.createSauce();
        cheese = ingredientFactory.createCheese();
        veggies = ingredientFactory.createVeggies();
        pepperoni = ingredientFactory.createPepperoni();
    }
}
package Pizza;

import Factory_Interface.PizzaIngredientFactory;

//素食披萨
public class VeggiePizza extends Pizza {
    PizzaIngredientFactory ingredientFactory;

    public VeggiePizza(PizzaIngredientFactory ingredientFactory) {
        this.ingredientFactory = ingredientFactory;
    }

    public void prepare() {
        System.out.println("准备 " + name);
        dough = ingredientFactory.createDough();
        sauce = ingredientFactory.createSauce();
        cheese = ingredientFactory.createCheese();
        veggies = ingredientFactory.createVeggies();
    }
}

下面是PizzaStore接口

package Store;

import Pizza.Pizza;

/**
 * PizzaStore相当于客户, 往后会有很多类似此类进行拓展
 * 
 * 在这里orderPizza()方法对pizz对象做了很多事(createPizza,prepare,bake,cut,box)
 * 但是由于Pizza对象是抽象的(接口),
 * orderPizza()并不知道具体哪些实际类参与进来了(相当于实现Pizza接口的实现类有哪些orderPizza()方法并不知道)
 * 换句话说,这就是解耦,减少相互依赖
 * 
 * 同理createPizza()方法中也不知道创建的具体是什么披萨 只知道披萨可以被准备,被烘烤,被切片,被装盒行为而已
 * 
 */
// 披萨店
public abstract class PizzaStore {

    // 点单
    public Pizza orderPizza(String type) {
        // createPizza()方法从工厂对象中取出,
        // 并且方法也不再这里实现,定义为抽象类,谁需要在拿走方法去具体实现
        Pizza pizza = createPizza(type);
        System.out.print("----制作一个" + pizza);
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }

    // 把工厂对象移到这个方法里,并封装
    // 现在工厂方法为抽象的,子类必须实现这个抽象方法
    public abstract Pizza createPizza(String item);
}

下面是两个加盟店的具体实现

package Store;

import Factory_Implements.NYPizzaIngredientFactory;
import Factory_Interface.PizzaIngredientFactory;
import Pizza.CheesePizza;
import Pizza.ClamPizza;
import Pizza.PepperoniPizza;
import Pizza.Pizza;
import Pizza.VeggiePizza;

/**
 * 此时NYStylePizzaStore所封装的知识是关于如何制作纽约风格的比萨
 * 
 * @author Joy
 * 
 */

// 纽约风格披萨店(分店)
public class NYStylePizzaStore extends PizzaStore {
    /**
     * 相比之前写的这里添加了工厂类
     */
    // 根据披萨的不同,创建不同披萨(具体实现类)
    @Override
    public Pizza createPizza(String item) {
        Pizza pizza = null;
        // 这里多态创建纽约原料工厂,该工厂负责纽约风味的披萨
        PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory();
        if (item.equals("芝士")) {
            // 实例化具体披萨类型,并传进该比萨所需要的工厂,以便取得原料
            pizza = new CheesePizza(ingredientFactory);
            pizza.setName("纽约芝士披萨");
        } else if (item.equals("素食")) {
            pizza = new VeggiePizza(ingredientFactory);
            pizza.setName("纽约素食披萨");
        } else if (item.equals("海蛎")) {
            pizza = new ClamPizza(ingredientFactory);
            pizza.setName("纽约海蛎披萨");
        } else if (item.equals("香肠")) {
            pizza = new PepperoniPizza(ingredientFactory);
            pizza.setName("纽约香肠披萨");
        }
        return pizza;
    }
}
package Store;


import Factory_Implements.ChicagoPizzaIngredientFactory;
import Factory_Interface.PizzaIngredientFactory;
import Pizza.CheesePizza;
import Pizza.ClamPizza;
import Pizza.PepperoniPizza;
import Pizza.Pizza;
import Pizza.VeggiePizza;

/**
 * 此时ChicagoStylePizzaStore所封装的知识是关于如何制作芝加哥风格的比萨
 * 
 * @author Joy
 * 
 */
// 芝加哥披萨店(分店)
public class ChicagoStylePizzaStore extends PizzaStore {
    Pizza pizza = null;
    // 这里多态创建纽约原料工厂,该工厂负责纽约风味的披萨
    PizzaIngredientFactory ingredientFactory = new ChicagoPizzaIngredientFactory();
    @Override
    public Pizza createPizza(String item) {
        if (item.equals("芝士")) {
            pizza = new CheesePizza(ingredientFactory);
            pizza.setName("芝加哥芝士披萨");
        } else if (item.equals("素食")) {
            pizza = new VeggiePizza(ingredientFactory);
            pizza.setName("芝加哥素食披萨");
        } else if (item.equals("海鲜")) {
            pizza = new ClamPizza(ingredientFactory);
            pizza.setName("芝加哥海蛎披萨");
        } else if (item.equals("香肠")) {
            pizza = new PepperoniPizza(ingredientFactory);
            pizza.setName("芝加哥香肠披萨");
        }
        return pizza;
    }
}

测试类

package TestMain;

import Pizza.Pizza;
import Store.ChicagoStylePizzaStore;
import Store.NYStylePizzaStore;
import Store.PizzaStore;

public class TestMain {
    public static void main(String[] args) {
        PizzaStore nyStore = new NYStylePizzaStore();
        PizzaStore chicagoStore = new ChicagoStylePizzaStore();
        System.out.println("====================================");
        Pizza pizza3 = nyStore.orderPizza("香肠");
        System.out.println("Joy点了一个" + pizza3);
        System.out.println("====================================");
        Pizza pizza4=chicagoStore.orderPizza("海鲜");
        System.out.println("Joy点了一个" + pizza4);

    }
}

效果图:
《开发之路(设计模式四:工厂模式下)》

工厂模式给我最大感受就是对编写好的代码的理解程度加深,想法也更加考虑拓展了,当然还需反复多练习,模拟代码已经全部放出,注释也写的比较清楚,若有不理解欢迎留言

感谢你看到这里,至此工厂模式内容结束,本人文笔随便,若有不足或错误之处望给予指点,90度弯腰~~~很快我会发布下一个设计模式内容,生命不息,编程不止!

参考书籍:《Head First 设计模式》
    原文作者:设计模式
    原文地址: https://segmentfault.com/a/1190000010755704
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞