设计模式之工厂模式

当我们只用new去创建一个类时,我们就在针对这个具体类编程。如果能够不使用具体类,而是针对接口编程,将会对以后扩展有很大方便。

情景:

你是一个披萨店的老板,披萨有很多种类。

我们可能需要这么做

Pizza orderPizza(String type) {
    Pizza pizza;
    
    if (type == A) {
        pizza = new APizza();
    } else if (type == B) {
        pizza = new BPizza();
    } else if (type == C) {
        pizza = new CPizza();
    }

    ... // 制作披萨订单的其他操作
}

但是以后我们可能会有D,E,F....之列的更多的披萨。我们必须重新修改这份代码。

根据“封装变化”的设计原则,我们需要把创建对象的部分封装起来。

public class SimplePizzaFactory {
    public Pizza createPizza(String type) {
        Pizza pizza = null;
        
        if (type.equals("A")) {
            pizza = new APizza();
        } else if (type.equals("B")) {
            pizza = new BPizza();
        } else if (type.equals("C")) {
            pizza = new CPizza();
        }
        
        return pizza;          
    }
}

这样在制作订单时只需在工厂取一个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;
        
    }
    
}

↑ 简单工厂 不是一种设计模式,反而像是一种编程习惯。

现在有很地区要建立披萨店的分店,你希望所有的店都有相同的流程,但是不同的地区会有不同的口味,比如有些地方只吃DEF口味的披萨,也就是说不同地区要有不同的工厂,没有方法把Pizza的制作活动全部局限于PizzaStore类呢?

方法是把createPizza放进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;
        
    }

    abstract Pizza createPizza(String type);
}

工厂方法用来处理对象的创建,并将这样的行为封装在子类中。这样,客户程序中关于超类的代码就和子类创建对象的代码解耦了。

 abstract Product factoryMethod(String type) 

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

我们最一开始的代码,PizzaStore的实现是依赖于具体Pizza类的,使用工厂模式后,它已经不再依赖于具体的披萨类,也就是减少了对具体类的依赖。

设计原则:依赖倒置原则(Dependency Inverseion Principle)

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

 这个原则说明了,不要让高层组件依赖底层组件,而且,不管高层组件还是底层组件,两者都应该依赖抽象。

使用了工厂方法模式之后,PizzaStore类依赖Pizza这个接口,而具体的APizza,BPizza等具体披萨类也依赖于Pizza这个接口,这就是一种依赖倒置。

………………

现在又有一个新的问题

就是具体Pizza的制作。上面没有写具体披萨的制作过程,默认都是一样的。但是!不同地区用到原料也可能有不同,例如A地产辣椒和B地是不一样的,于是,我们还应该给Pizza写一个原料工厂~~~

首先为工厂定义一个接口

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

为每一个地区创建一个原料工厂,比如纽约的原料工厂

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

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

}

抽象的Pizza类

public abstract class Pizza {

    String name;
    Dough dough;
    Sauce sauce;
    Veggies veggies[];
    Cheese cheese;
    Pepperoni pepperoni;
    Clams clams;
    
    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.");
    }
    
    void setName(String name) {
        this.name = name;
    }
    
    String getName() {
        return name;
    }
    
    public String toString() {
        return name;
    }

}

具体的Pizza类。每个Pizza的制作流程其实是一样的。只有原料不同,同时原料会和地点有关。为具体的类添加一个原料工厂。

public class CheesePizza extends Pizza {
    
    PizzaIngredientFactory ingredientFactory;
    
    public CheesePizza(PizzaIngredientFactory ingredientFactory) {
        this.ingredientFactory = ingredientFactory;
    }

    @Override
    void prepare() {
        System.out.println("Preparing " + name);
        dough = ingredientFactory.createDough();
        sauce = ingredientFactory.createSauce();
        cheese = ingredientFactory.createCheese();
    }

}

纽约的PizzaStore类

public class NYPizzaStore extends PizzaStore {

    @Override
    Pizza createPizza(String item) {
        Pizza pizza = null;
        PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory();
        
        if (item.equals("cheess")) {
            pizza = new ChessPizza(ingredientFactory);
            
        } else if (item.equals("veggie")) {
            pizza = new VeggiePizza();
            pizza.setName("New York Style Veggie Pizza.");
        }
        // else if .... 还有其他种类的Pizza 
        
        return pizza;
    }
    
}

完成~~

我们引入了新类型的工厂,也就是所谓的抽象工厂,来创建披萨家族。

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

抽象工厂允许客户使用抽象类的接口来创建一组相关的产品,而不需要知道(或关心)实际产出的具体产品是什么。这样一来,客户就从具体产品中被解耦。

类图

工厂方法模式通过继承,而抽象工厂模式是通过组合实现。

抽象工厂是用来创建一个产品家族的抽象类型,可以把一群相关产品集合起来。

原文地址:https://www.cnblogs.com/wenruo/p/6529558.html