设计模式学习笔记(四)——工厂模式

“new”一个实例

在技术上,用new来实例化一个具体类并没有错,但是当我们的代码涉及到“改变”的时候,问题出现了。

当我们实例化一些相关的具体类时,我们通常用“if/else"条件来决定在运行时究竟实例化哪一个类,然而当有变化或扩展时,我们必须打开这部分代码进行修改,这违背了”开-闭“原则,而且造成系统更难维护和更新,更容易犯错。
所以,我们提供了一种新的方式来实例化类。—— 工厂模式

工厂模式有三种形式:

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

简单工厂模式

我们用一个购买pizza的例子来看一下这个模式

抽象产品

public interface Pizza {
    void prepare();
    void bake();
    void cut();
    void box();
}

具体产品类

public class CheesePizza implements Pizza {
    @Override
    public void prepare() {
        System.out.println("cheesePizza--准备中...");
    }

    @Override
    public void bake() {
        System.out.println("cheesePizza--烤制中...");
    }

    @Override
    public void cut() {
        System.out.println("cheesePizza--切片");
    }

    @Override
    public void box() {
        System.out.println("cheesePizza--装盒");
    }
}
public class GreekPizza implements Pizza {
    @Override
    public void prepare() {
        System.out.println("GreekPizza--准备中...");
    }

    @Override
    public void bake() {
        System.out.println("GreekPizza--烤制中...");
    }

    @Override
    public void cut() {
        System.out.println("GreekPizza--切片");
    }

    @Override
    public void box() {
        System.out.println("GreekPizza--装盒");
    }
}
public class PepperoniPizza implements Pizza {
    @Override
    public void prepare() {
        System.out.println("PepperoniPizza--准备中...");
    }

    @Override
    public void bake() {
        System.out.println("PepperoniPizza--烤制中...");
    }

    @Override
    public void cut() {
        System.out.println("PepperoniPizza--切片");
    }

    @Override
    public void box() {
        System.out.println("PepperoniPizza--装盒");
    }
}
public class VeggiePizza implements Pizza {
    @Override
    public void prepare() {
        System.out.println("VeggiePizza--准备中...");
    }

    @Override
    public void bake() {
        System.out.println("VeggiePizza--烤制中...");
    }

    @Override
    public void cut() {
        System.out.println("VeggiePizza--切片");
    }

    @Override
    public void box() {
        System.out.println("VeggiePizza--装盒");
    }
}

简单工厂

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

        if(type.equals("cheese")){
            pizza = new CheesePizza();
        } else if (type.equals("greek")){
            pizza = new GreekPizza();
        } else if (type.equals("pepperoni")){
            pizza = new PepperoniPizza();
        } else if (type.equals("veggie")){
            pizza = new VeggiePizza();
        } else {
            System.out.println("没有该种类");
        }
        return pizza;
    }
}

客户

public class PizzaStore {

    private SimplePizzaFactory simplePizzaFactory;

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

    public Pizza orderPizza(String type){
        Pizza pizza = simplePizzaFactory.createPizza(type);
        if(pizza != null) {
            pizza.prepare();
            pizza.bake();
            pizza.cut();
            pizza.box();
        }
        return pizza;
    }
}

测试

public class Test {
    public static void main(String[] args) {
        SimplePizzaFactory simplePizzaFactory = new SimplePizzaFactory();
        PizzaStore pizzaStore = new PizzaStore(simplePizzaFactory);

        pizzaStore.orderPizza("cheese");
        pizzaStore.orderPizza("veggie");
        pizzaStore.orderPizza("pineapple");
    }
}

在这里插入图片描述

我们确实完成了由客户决定生产什么产品,同时生产细节和客户解耦,客户不必关心具体的实现,只需要提出需求。
缺点: 我们可以看出,简单工厂包含了所有的生产逻辑,当我们新增或删减一个产品时,需要更改工厂里的代码, 这里就违背了我们的”开-闭“原则,由此,下面我们介绍一下工厂模式。

另: 其实简单工厂并不是一个设计模式,反而像是一种编程习惯,但是由于我们经常使用的缘故,很多开发人员将其误认为”工厂模式“。
同时,我们可以用一个静态方法定义一个简单工厂,使得不需要用new的方式来实例化工厂对象,但这也造成不能通过继承来改变创建方法的行为。

工厂方法模式

定义: 定义了一个创建对象的接口,子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
分析: 接口定义了创建对象的方法,子类重写方法具体创建对象,定义中的决定不是说允许子类在运行时做决定,而是在我们的创建者类中,选择了哪个子类,自然就决定了实际创建的产品是什么

UML类图

在这里插入图片描述

Product:抽象产品接口
ConcreteProduct:具体产品类
Creator:抽象工厂接口,实现了所有操作产品的方法,只有抽象的工厂方法factoryMethod必须由子类实现
ConcreteCreator:具体工厂子类,实现了factoryMethod方法,创造实际的产品

示例
现在,披萨店要扩大规模在全国开加盟店,各个地区的披萨口味都不相同,如果现在有三家加盟店NYPizzaStore,ChicagoPizzaStore,CaliforniaPizzaStore,如何实现?我们现在使用工厂模式来试一下

抽象工厂

public abstract class PizzaStore {

    public Pizza orderPizza(String type){
        Pizza pizza = createPizza(type);
        if(pizza != null) {
            pizza.prepare();
            pizza.bake();
            pizza.cut();
            pizza.box();
        }
        return pizza;
    }

    //工厂方法,必须由子类实现
    protected abstract Pizza createPizza(String type);
}

抽象产品

public abstract class Pizza {
    String name;
    String dough;
    String sauce;
    ArrayList toppings = new ArrayList();

    void prepare(){
        System.out.println("准备:"+name);
        System.out.println("揉面饼...");
        System.out.println("添加酱汁...");
        System.out.println("添加配料:");
        for (int i = 0;i < toppings.size();i++){
            System.out.println(" "+toppings.get(i));
        }
    }

    void bake(){
        System.out.println("烘烤25分钟");
    }

    void cut(){
        System.out.println("切片");
    }

    void box(){
        System.out.println("装盒");
    }

    public String getName(){
        return name;
    }
}

具体工厂

public class NYPizzaStore extends PizzaStore {
    @Override
    protected Pizza createPizza(String type) {
        Pizza pizza = null;
        if(type.equals("cheese")) {
            pizza = new NYStyleCheesePizza();
        }
        return pizza;
    }
}

public class ChicagoPizzaStore extends PizzaStore {
    @Override
    protected Pizza createPizza(String type) {
        Pizza pizza = null;
        if(type.equals("cheese")) {
            pizza = new ChicagoStyleCheesePizza();
        }
        return pizza;
    }
}

具体产品

public class NYStyleCheesePizza extends Pizza {
    public NYStyleCheesePizza() {
        name = "纽约芝士披萨";
        dough = "薄面饼";
        sauce = "意式红酱";
        toppings.add("干奶酪");
    }
}

public class ChicagoStyleCheesePizza extends Pizza {
    public ChicagoStyleCheesePizza() {
        name = "芝加哥芝士披萨";
        dough = "厚面饼";
        sauce = "番茄酱";
        toppings.add("白奶酪");
    }
}

测试

public class Test {
    public static void main(String[] args) {
        PizzaStore nyStore = new NYPizzaStore();
        PizzaStore chicagoStore = new ChicagoPizzaStore();

        Pizza pizza = nyStore.orderPizza("cheese");
        System.out.println("订单:"+pizza.getName()+"\n");

        pizza = chicagoStore.orderPizza("cheese");
        System.out.println("订单:"+pizza.getName()+"\n");
    }
}

结果
在这里插入图片描述

简单工厂和工厂方法模式的区别
简单工厂将所有的事情在一个地方做完了,而工厂方法则是创建一个框架,让子类决定要如何实现。

设计原则: 依赖倒置原则:要依赖抽象,不要依赖具体。
这个和”针对接口编程,不针对实现编程“看起来有点像,但这里更强调”抽象“。这个原则说明了不能让高层组件依赖底层组件,且两者都应该依赖于抽象。例如,示例中我们的PizzaStore类是高层,披萨子类是低层,而如果PizzaStore依赖于披萨子类,这就违背了这个原则,我们应该依赖与抽象类而不是具体类。那我们的PizzaStore类中,我们依赖的是Pizza这个抽象类,说明工厂方法模式是符合这个设计原则的。
几个指导方针帮你不违背此原则:

  • 变量不可以持有具体类的引用——即不使用new来实例化
  • 不要让类派生自具体类——请派生自抽象类或接口
  • 不要覆盖基类中已经实现的方法——基类中已实现的方法应用于子类共享而不是用来重写的

想要完全遵守以上指导方针是不可能的,我们写代码时能尽量遵守即可

抽象工厂模式

定义: 提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
分析: 允许客户使用抽象的接口来创建一组产品,而不需要知道产出的具体的产品是什么,这样一来,客户就和具体的产品解耦

UML类图
在这里插入图片描述

AbstractFactory:抽象工厂,提供一组用于创建产品的接口
ConcreteFactory:实际工厂子类,创建一组产品
AbstractProduct:抽象产品
ConcreteProduct:实际产品子类

示例
现在,要确保每家加盟店使用的原料,我们要在每个地区创建一家原料工厂,这些原料工厂的产品是一样的,区别在于使用不同地域的原料或口味。

抽象工厂

public interface PizzaIngredientFactory {
    Dough createDough();
    Sauce createSauce();
    Cheese createCheese();
    Pepperoni createPepperoni();
}

具体工厂

public class ChicagoPizzaIngredientFactory implements PizzaIngredientFactory {
    @Override
    public Dough createDough() {
        return new ThickDough();
    }

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

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

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

public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
    @Override
    public Dough createDough() {
        return new ThinDough();
    }

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

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

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

抽象产品族接口

public interface Cheese {
}
public interface Dough {
}
public interface Pepperoni {
}
public interface Sauce {
}

具体产品族

public class FancyCheese implements Cheese {
    public FancyCheese() {
        System.out.println("高级芝士");
    }
}
public class OrdinaryCheese implements Cheese {
    public OrdinaryCheese() {
        System.out.println("普通芝士");
    }
}
public class CreamSauce implements Sauce {
    public CreamSauce() {
        System.out.println("奶油酱汁");
    }
}
public class TomatoSauce implements Sauce {
    public TomatoSauce() {
        System.out.println("番茄酱汁");
    }
}
public class ItalianPepperoni implements Pepperoni {
    public ItalianPepperoni() {
        System.out.println("意大利香肠");
    }
}
public class DongbeiPepperoni implements Pepperoni {
    public DongbeiPepperoni() {
        System.out.println("东北红肠");
    }
}
public class ThickDough implements Dough {
    public ThickDough() {
        System.out.println("厚面饼");
    }
}
public class ThinDough implements Dough {
    public ThinDough() {
        System.out.println("薄面饼");
    }
}

客户接口

public abstract class Pizza {
    String name;
    Dough dough;
    Cheese cheese;
    Sauce sauce;
    Pepperoni pepperoni;

    abstract void prepare();
    public void bake(){
        System.out.println("烘烤25分钟");
    }
    public void cut(){
        System.out.println("切片");
    }
    public void box(){
        System.out.println("装盒");
    }

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

}

具体客户

public class CheesePizza extends Pizza {

    private PizzaIngredientFactory pizzaIngredientFactory;

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

    @Override
    void prepare() {
        System.out.println("准备中...");
        dough = pizzaIngredientFactory.createDough();
        cheese = pizzaIngredientFactory.createCheese();
        sauce = pizzaIngredientFactory.createSauce();
        pepperoni = pizzaIngredientFactory.createPepperoni();
    }
}

public class VeggiePizza extends Pizza {

    private PizzaIngredientFactory pizzaIngredientFactory;

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

    @Override
    void prepare() {
        System.out.println("准备中...");
        dough = pizzaIngredientFactory.createDough();
        cheese = pizzaIngredientFactory.createCheese();
        sauce = pizzaIngredientFactory.createSauce();
    }
}

测试

public class Test {
    public static void main(String[] args) {
        PizzaStore pizzaStore = new NYPizzaStore();
        PizzaStore pizzaStore2 = new ChicagoPizzaStore();
        Pizza pizza = pizzaStore.orderPizza("cheese");
        System.out.println("---------"+pizza.getName()+"----------");
        pizza = pizzaStore.orderPizza("veggie");
        System.out.println("---------"+pizza.getName()+"----------");

        pizza = pizzaStore2.orderPizza("cheese");
        System.out.println("---------"+pizza.getName()+"----------");
        pizza = pizzaStore2.orderPizza("veggie");
        System.out.println("---------"+pizza.getName()+"----------");

    }
}

在这里插入图片描述

注意: 抽象工厂的方法实际上是使用工厂方法模式的形式,方法声明为抽象,由子类负责具体实例化对象。这样做的原因是,抽象工厂的任务是定义一个负责创造一组接产品的接口,接口内每个方法负责创建一个产品。

比较工厂方法和抽象工厂

工厂方法:继承工厂方法接口,重写工厂方法。通过子类来创建对象,客户只需要知道所使用的抽象类型,由子类来决定具体类型,也就是客户从具体类型中解耦。
抽象工厂:实现了一个创建一组产品的接口,子类重写方法,要使用工厂,我们使用组合的方式,客户持有一个抽象工厂引用,实际工厂类型由子类决定,所以,和工厂方法一样,达到将客户和具体类型解耦的目的,只不过前者用继承,后者用组合。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值