工厂模式

GitHub代码

场景描述:我们正在设计一个披萨店,因为我们对披萨的操作有一套固有的流程,比如切割,装盒等,但是我们有着非常多的披萨类型,风味不同,所以如何设计针对不同口味创建不同的披萨就成了整个程序的难点。

在这里插入图片描述

简单工厂

为了增加程序的弹性,并且避免频繁修改代码,我们将变化的部分拿出去,创建了一个简单工厂类。

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;
    }
}

利用静态方法定义一个简单的工厂,这是很常见的技巧,常被称为静态工厂。为何使用静态方法?因为不需要使用创建对象的方法来实例化对象。getInstance()。

这样我们的披萨店代码就呼之欲出了。

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;
    }
}

简单工厂其实并不是一个设计模式,反而比较像是一种编程习惯。有些开发人员的确是把这个编程习惯误认为是“工厂模式”。

下面是这个简单工厂的类图:
在这里插入图片描述

工厂方法

新的问题:当我们出现新的加盟店时,如纽约和芝加哥,它们的口味不太相同,这样我们就需要创建不同区域的特有工厂,然后再根据对应的口味生产特定的披萨。不过我们能够看出来,不同的工厂中,通过特定口味生产披萨这个步骤都是相同的,只不过最后生产的具体披萨不同而已,所以我们是否可以将其抽象出来呢?
在这里插入图片描述
我们尝试抽象披萨店的代码:

public abstract class PizzaStore {

    public Pizza orderPizza(String type){
        Pizza pizza;

        pizza = createPizza(type);

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

        return pizza;
    }

    public abstract Pizza createPizza(String type);
}

然后针对不同地域去实现特定的制作方法
在这里插入图片描述
所有工厂模式都用来封装对象的创建。工厂方法模式(Factory Method Pattern)通过让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的。

让我们来看一下通过这种方式来下单的流程如何:
在这里插入图片描述

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

工厂方法模式能够封装具体类型的实例化。而所谓的“决定”,并不是指模式允许子类本身在运行时做决定,而是指编写创建者类时,不需要知道实际创建的产品是哪一个。

我们看一下工厂方法模式的类图:
在这里插入图片描述
再对比我们的披萨店,可以更加直观的感受
在这里插入图片描述
简单工厂把全部的事情,在一个地方都处理完了,然而工厂方法却是创建一个框架,让子类决定要如何实现。简单工厂的做法,可以将对象的创建封装起来,但是简单工厂不具备工厂方法的弹性,因为简单工厂不能变更正在创建的产品。

抽象工厂

让我们再看一个依赖性非常强的实现。
在这里插入图片描述
在这里插入图片描述
上图看出了,依赖过多,所以会导致非常大的弊端,每次新增种类,不仅需要修改代码,还造成了过多的依赖。

设计原则:要依赖抽象,不要依赖具体类。

这个原则说明了:不能让高层组件依赖底层组件,而且,不管高层或底层组件,“两者”都应该依赖于抽象。
这就是“依赖倒置原则”(Dependency Inversion Principle)。
在这里插入图片描述
下图可以更加清晰地看出这句话的含义。
在这里插入图片描述
下面的指导方针,能帮你避免在OO设计中违反依赖倒置原则:

  • 变量不可以持有具体类的引用。
  • 不要让类派生自具体类。
  • 不要覆盖基类中已实现的方法。

当然只是尽量达到这个原则,而不是随时都要遵守这个原则。

新的需求:我们又新增了新的需求,我们拥有不同地域的加盟店,并且它们使用的原料各不相同,而我们想要控制原料,而不是由他们自己提供原料,所以我们就需要一个新的设计来管理整个原料家族。

我们尝试建造一个工厂来创建所有原料。

public interface PizzaIngredientFactory {
    public Dough createDough();
    public Sauce createSauce();
    public Cheese createCheese();
    public Veggies[] createVeggies();
    public Pepperoni createPepperoni();
    public Clams createClams();
}

然后我们创建每个地域的工厂,如纽约的

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()};
        return veggies;
    }

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

    @Override
    public Clams createClams() {
        return new FreshClams();
    }
}

这时我们的披萨变为抽象的了。

public abstract class Pizza {
    public String name;
    public Dough dough;
    public Sauce sauce;
    public Veggies veggies[];
    public Cheese cheese;
    public Pepperoni pepperoni;
    public Clams clams;

    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 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 NYPizzaStore extends PizzaStore {
    public Pizza createPizza(String item){
        Pizza pizza = null;
        PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory();
        if (item.equals("cheese")){
            pizza = new CheesePizza(ingredientFactory);
            pizza.setName("New York Style Cheese Pizza");
        }// 省略其他类型pizza

        return pizza;
    }
}

现在我们看一下一个披萨订单都经历了什么
在这里插入图片描述
在这里插入图片描述
上述模式就是一种新的工厂模式。

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

我们看一下它的类图:
在这里插入图片描述
比对一下我们的披萨店,更直观的理解一下
在这里插入图片描述
总结
整个工厂方法模式,只不过就是通过子类来创建对象。用这种做法,客户只需要知道他们所使用的抽象类型就可以了,而由子类来负责决定具体类型。换句话说,它将客户从具体类型中解耦。
而抽象工厂模式,提供一个用来创建一个产品家族的抽象类型,这个类型的子类定义了产品被产生的方法。要想使用这个工厂,必须先实例化它,然后将它传入一些针对抽象类型所写的代码中。换句话说,它将客户从所使用的实际具体产品中解耦。

当你需要创建产品家族和想让制造的相关产品集合起来时,你可以使用抽象工厂。
而当你需要把你的客户代码从需要实例化的具体类中解耦,或者如果你目前还不知道将来需要实例化哪些具体类时,就可以使用工厂方法。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值