工厂模式

需求

开披萨店,披萨有很多种,披萨店也可以有很多个分店

目的

对修改关闭,针对接口编程,不针对实现编程

简单工厂

披萨

public abstract class Pizza
{
    String name;
    String dough;// 面团
    String sauce;// 酱
    ArrayList<String> toppings = new ArrayList<String>();// 浇在披萨上的

    public String getName()
    {
        return name;
    }

    // 准备
    public void prepare()
    {
        System.out.println("Prepare " + name);
        System.out.println("Tossing dough...");
        System.out.println("Adding sauce...");
        System.out.println("Adding toppings: ");
        for (String topping : toppings)
        {
            System.out.println("   " + topping);
        }
    }

    // 烘烤
    public void bake()
    {
        System.out.println("Baking " + name);
    }

    // 切片
    public void cut()
    {
        System.out.println("Cutting " + name);
    }

    // 打包
    public void box()
    {
        System.out.println("Boxing " + name);
    }

    public String toString()
    {
        // 显示披萨名称和配料
        StringBuffer display = new StringBuffer();
        display.append("---- " + name + " ----\n");
        display.append(dough + "\n");
        display.append(sauce + "\n");
        for (String topping : toppings)
        {
            display.append(topping + "\n");
        }
        return display.toString();
    }
}

// ------------------------------------------------------------------------

// 蛤蜊披萨
public class ClamPizza extends Pizza
{
    public ClamPizza()
    {
        name = "Clam Pizza";
        dough = "Thin crust";
        sauce = "White garlic sauce";
        toppings.add("Clams");
        toppings.add("Grated parmesan cheese");
    }
}

<br />


<br />

披萨店

public class PizzaStore
{
    SimplePizzaFactory factory; // 披萨店接收一个披萨制造工厂

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

    // 客户可以订披萨
    public Pizza orderPizza(String type)
    {
        Pizza pizza;

        pizza = factory.createPizza(type); // 交给工厂来创建披萨

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

        return pizza;
    }
}

<br />


<br />

披萨工厂

public class SimplePizzaFactory
{

    public Pizza createPizza(String type)
    {
        Pizza pizza = null;

        if (type.equals("cheese"))
        {
            pizza = new CheesePizza();
        } else if (type.equals("pepperoni"))
        {
            pizza = new PepperoniPizza();
        } else if (type.equals("clam"))
        {
            pizza = new ClamPizza();
        } else if (type.equals("veggie"))
        {
            pizza = new VeggiePizza();
        }
        return pizza;
    }
}

<br />


<br />

测试

public static void main(String[] args)
{
    // 创建个工厂
    SimplePizzaFactory factory = new SimplePizzaFactory();
    // 创建个披萨店
    PizzaStore store = new PizzaStore(factory);

    // 预订披萨,披萨店使用工厂创建披萨,然后披萨店自己处理披萨的烘烤、切片、装盒
    Pizza pizza = store.orderPizza("cheese");
    System.out.println("We ordered a " + pizza.getName() + "\n");
    System.out.println(pizza);

    pizza = store.orderPizza("veggie");
    System.out.println("We ordered a " + pizza.getName() + "\n");
    System.out.println(pizza);
}

简单工厂的问题和好处

  • 把创建披萨的代码放到了一个地方,SimplePizzaFactory的客户可以有很多,不只是orderPizza,可以是菜单什么的,通过工厂获取价格
  • 可以使用静态方法来替代工厂,成为静态工厂,但是缺点是没法通过继承来改变行为

<br />


<br />

工厂方法,重做披萨店

由于很多加盟商,在不同的地区开店,因此需要配合当地的口味,所以需要定制披萨店

披萨店重做

public abstract class PizzaStore
{
    // 这个方法就像一个工厂,由子类决定如何制造披萨
    public abstract Pizza createPizza(String item);

    public Pizza orderPizza(String type)
    {
        // 创建披萨,由子类来决定,你要创建的是什么样的披萨
        Pizza pizza = createPizza(type);
        System.out.println("--- Making a " + pizza.getName() + " ---");
        // 加工,由于披萨的风味和制作方法各有不同,因此也交给子类来决定,从而达到解耦的目的
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
}

// 开店
public class ChicagoPizzaStore extends PizzaStore
{

    public Pizza createPizza(String item)
    {
        if (item.equals("cheese"))
        {
            return new ChicagoStyleCheesePizza();
        } else if (item.equals("veggie"))
        {
            return new ChicagoStyleVeggiePizza();
        } else if (item.equals("clam"))
        {
            return new ChicagoStyleClamPizza();
        } else if (item.equals("pepperoni"))
        {
            return new ChicagoStylePepperoniPizza();
        } else
            return null;
    }
}

披萨可以自己定义加工自己的方式

public class ChicagoStyleClamPizza extends Pizza
{
    public ChicagoStyleClamPizza()
    {
        name = "Chicago Style Clam Pizza";
        dough = "Extra Thick Crust Dough";
        sauce = "Plum Tomato Sauce";

        toppings.add("Shredded Mozzarella Cheese");
        toppings.add("Frozen Clams from Chesapeake Bay");
    }

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

工厂方法模式的定义

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

设计原则 6 :依赖倒置原则

要依赖抽象,不要依赖具体类。

不能让高层组件依赖于具体的低层组件,在这个例子中,披萨店就是高层,披萨就是低层,而不管对于高层还是低层,都应该依赖于抽象,而不要依赖于具体的抽象类。

比如上面的PizzaStore依赖的就是Pizza这个抽象,而不是依赖于具体的某个Pizza子类

如何应用此原则

  • 变量不可以持有具体类的引用,比如使用new得到具体类的实例,可以使用工厂方法
  • 类不可以派生于具体类,需要派生于一个抽象,就是接口或抽象类
  • 不要覆盖父类中已实现的方法,父类中的方法应该被所有的子类共享

<br />


<br />

抽象工厂

现在不仅披萨店要定制,制作披萨的原料也要定制,因此需要不同的原料工厂

原料工厂

// 原料工厂,每种原料都对应一种创建的方法,抽象化,交给子类决定使用什么原料
public interface PizzaIngredientFactory
{
    public Dough createDough();

    public Sauce createSauce();

    public Cheese createCheese();

    public Veggies[] createVeggies();

    public Pepperoni createPepperoni();

    public Clams createClam();
}


// 芝加哥原料工厂,指定要使用哪些原料
public class ChicagoPizzaIngredientFactory implements PizzaIngredientFactory
{
    public Dough createDough()
    {
        return new ThickCrustDough();
    }

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

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

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

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

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

原料接口,同一种原料的生产之类的可能每个地区都不一样

public interface Cheese
{
    public String toString();
}

public class MozzarellaCheese implements Cheese
{
    public String toString()
    {
        return "Shredded Mozzarella";
    }
}

// 省略其他原料定义

披萨

public abstract class Pizza
{
    public String name;

    public Dough dough;
    public Sauce sauce;
    public Veggies veggies[];
    public Cheese cheese;
    public Pepperoni pepperoni;
    public Clams clam;

    public abstract void prepare();

    public void bake()
    {
        System.out.println("Bake for 25 minutes at 350");
    }

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

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

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

    public String getName()
    {
        return name;
    }

    public String toString()
    {
        StringBuffer result = new StringBuffer();
        result.append("---- " + name + " ----\n");
        if (dough != null)
        {
            result.append(dough);
            result.append("\n");
        }
        if (sauce != null)
        {
            result.append(sauce);
            result.append("\n");
        }
        if (cheese != null)
        {
            result.append(cheese);
            result.append("\n");
        }
        if (veggies != null)
        {
            for (int i = 0; i < veggies.length; i++)
            {
                result.append(veggies[i]);
                if (i < veggies.length - 1)
                {
                    result.append(", ");
                }
            }
            result.append("\n");
        }
        if (clam != null)
        {
            result.append(clam);
            result.append("\n");
        }
        if (pepperoni != null)
        {
            result.append(pepperoni);
            result.append("\n");
        }
        return result.toString();
    }
}


public class CheesePizza extends Pizza
{
    PizzaIngredientFactory ingredientFactory;

    // 由商店决定要用哪个工厂的原料
    public CheesePizza(PizzaIngredientFactory ingredientFactory)
    {
        this.ingredientFactory = ingredientFactory;
    }

    public void prepare()
    {
        System.out.println("Preparing " + name);
        // 只要是工厂就可以,不在乎具体是谁
        dough = ingredientFactory.createDough();
        sauce = ingredientFactory.createSauce();
        cheese = ingredientFactory.createCheese();
    }
}

披萨店

父类不需要更改了,由子类自己决定自己的店需要的原料工厂

public class ChicagoPizzaStore extends PizzaStore
{

    protected Pizza createPizza(String item)
    {
        Pizza pizza = null;
        // 指定工厂
        PizzaIngredientFactory ingredientFactory = new ChicagoPizzaIngredientFactory();

        if (item.equals("cheese"))
        {
            pizza = new CheesePizza(ingredientFactory);
            pizza.setName("Chicago Style Cheese Pizza");

        } else if (item.equals("veggie"))
        {
            pizza = new VeggiePizza(ingredientFactory);
            pizza.setName("Chicago Style Veggie Pizza");

        } else if (item.equals("clam"))
        {
            pizza = new ClamPizza(ingredientFactory);
            pizza.setName("Chicago Style Clam Pizza");

        } else if (item.equals("pepperoni"))
        {
            pizza = new PepperoniPizza(ingredientFactory);
            pizza.setName("Chicago Style Pepperoni Pizza");

        }
        return pizza;
    }
}

抽象工厂的定义

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

工厂方法和抽象工厂的比较

  • 抽象工厂的方法,经常以工厂方法的方式呈现

  • 工厂方法使用的是继承,继承一个工厂方法去创建对象,客户只需要知道它的父抽象类型即可。抽象工厂使用的是组合,在继承到方法创建对象后,然后将相关的产品组合起来.

  • 抽象工厂由于是大的产品家族,后续要是加入新的类型,就需要更改接口,工作量会很大

  • 总之,需要产品族的用抽象工厂,如果只是单一的,可以用工厂方法

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容

  • 设计原则: 要依赖抽象,不要依赖具体类 目录 本文的结构如下: 什么是抽象工厂模式 为什么要用该模式 模式的结构 ...
    w1992wishes阅读 1,097评论 0 6
  • 工厂模式包含三种模式:简单工厂模式、工厂方法模式和抽象工厂模式。 简单工厂模式 定义简单工厂模式:由一个工厂类根据...
    RickGe阅读 344评论 0 0
  • 今天学习下最常见的工厂模式,工厂模式细分下来有三大类: 他们的目标都是一样的:封装对象的创建。但是实现手段和使用场...
    西木柚子阅读 2,136评论 7 28
  • 该文章属于刘小壮原创,转载请注明:刘小壮[https://www.jianshu.com/u/2de707c93d...
    刘小壮阅读 12,694评论 29 59
  • 客户需求 程序设计 1、PizzaStore是用来给客户下订单买pizza的,所以每个PizzaStore都会有一...
    BlainPeng阅读 546评论 0 2