《Head First 设计模式》 读书笔记04 工厂模式(一)

《Head First 设计模式》读书笔记04 工厂模式

 

问题的提出

  如何将实例化具体类的代码从应用中抽离,或者封装起来,使它们不会干扰应用的其他部分?

  假设你有一个披萨店,那么代码最初可能是这样:

最初的披萨店代码
Pizza oderPizza(String type)
{
    //Pizza是一个接口
    Pizza pizza;

    //随着时间改变的地方
    if(type.equals("cheese"))
    {
        pizza = new CheesePizza();
    }
    else if(type.equals("greel"))
    {
        pizza = new GreekPizza();
    }
    else if(type.equals("pepperoni"))
    {
        pizza = new PepperoniPizza();
    }

    //不改变的地方
    pizza.prepare();
    pizza.bake();
    pizza.cut();
    pizza.box();
    return pizza;
}

  披萨类型可能随着时间各种改变,那么找到了改变的部分和不变的部分之后,使用封装将创建对象的部分移到外面去。

  封装创建对象的代码

  

 

 

新的实现——简单工厂

   利用工厂封装后,新的实现代码如下:

  

简单工厂实现披萨店
public class SimplePizzaFactory
{
    public Pizza createPizza(String type)
    {
        Pizza pizza = null;        
        if(type.equals("cheese"))
        {
            pizza = new CheesePizza();
        }
        else if(type.equals("greel"))
        {
            pizza = new GreekPizza();
        }
        else if(type.equals("pepperoni"))
        {
            pizza = new PepperoniPizza();
        }
        return pizza;
    }
}
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;
    }
}

 

   类图:

  

 

 

重要提醒

  在设计模式中,所谓的“实现一个接口”并不一定表示“写一个类,并用implements关键字来实现某个Java接口”。

  “实现一个接口”泛指“实现某个超类型(可以是类或接口)的某个方法”。

 

有更多不同地区的加盟披萨店

  一种做法:设计出不同的工厂类

  每个地区用自己的工厂类加工披萨,每个地区也有自己的PizzaStore对象,建立对象时将自己的工厂类对象加入。框架不变。

  想要更多的质量控制

  把createPizza()方法放回到PizzaStore中,设置成抽象方法,然后为每个区域风味创建一个PizzaStore的子类。

  这种方法实现起来代码如下:

 

工厂方法实现披萨店
public abstract class PizzaStore
{

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

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

        return pizza;
    }
    protected abstract Pizza createPizza(String type);
}

工厂方法

  上面的例子中,实例化披萨的责任被移到一个方法中,这个方法就如同是一个“工厂”。

  工厂方法用来处理对象的创建,并将这样的行为封装在子类中。

  这样,客户程序中关于超类的代码就和子类对象创建代码解耦了。

  abstract Product factoryMethod(String type);

  工厂方法是抽象的,所以依赖子类来处理对象的创建。

  工厂方法必须返回一个产品。超类中定义的方法,通常使用到工厂方法的返回值。

  工厂方法将客户(也就是超类中的代码,如orderPizza())和实际创建具体产品的代码分隔开来。

  工厂方法可能需要参数(也可能不需要)来指定所需要的产品。

 

工厂方法模式

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

  工厂方法让类把实例化推迟到子类。

  见类图:

  

 

  ConcreteCreator可以有一个或多个,选择了子类,则选择了实际创建的工厂方法。

  这个具体的工厂可以只创建一种对象,也可以根据传入的参数创建不同的对象。

  工厂方法和Creator并不总是抽象的,也可以提供默认的具体实现。

 

设计原则——依赖倒置原则Dependency Inversion Principle

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

  不能让高层组件依赖底层组件。

  前面所说的做法就体现了这个设计原则,倒置依赖后:让高层组件PizzaStore依赖于一个抽象的Pizza,而各种具体的披萨也依赖于这个抽象的Pizza。

  几个指导方针帮助你遵循此原则:

  变量不可以持有具体类的引用。

  如果使用new,就会持有具体类的引用,可以使用工厂来避开这样的做法。

  不要让类派生自具体类。

  如果派生自具体类,就会依赖具体类。要派生自抽象类或接口。

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

  如果覆盖基类已实现的方法,那么基类不是一个真正适合被继承的抽象。基类中已实现的方法应该由所有的子类共享。

  这些原则应尽量达到,而不是随时都要遵循,有时候有足够的理由,就可以违反这些方针。(Orz)。

原文地址:https://www.cnblogs.com/mengdd/p/2844868.html