04工厂模式

发布时间 2023-04-17 18:10:47作者: lee_ing

制造对象,不仅只有使用new操作符。实例化不应该总是公开进行,因为经常会导致耦合问题。

01例子

假设你有一个披萨店,身为披萨店的主人,代码可能是这样:

Pizza orderPizza(){
//为了让系统有弹性,我们很希望这是一个抽象类或接口。但如果这样,这些类或接口就无法直接实例化。
        Pizza pizza =new Pizza();
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
}

需要更多披萨类型时:

Pizza orderPizza(String type){
        Pizza pizza ;
        if(type.equals("cheese")){
               pizza=new CheesePizza();
        }else if(type.equals("greek")){
               pizza=new GreekPizza();
        }else if(type.equals("pepperoni")){
               pizza=new PepperoniPizza();
        }
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }

缺点:如果要增加或者删除某个type,需要对这份代码进行修改。
关于哪个具体类被实例化才是搞乱orderPizza()方法的祸首:
它无法让orderPizza()对修改关闭;但是,现在我们已经知道哪些会改变,哪些不会改变,该是封装的时候了。

02简单工厂

建立简单工厂

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

重写PizzaStore类

依赖工厂来创建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;
    }
}

类图


简单工厂不是设计模式,是一种编程习惯!

03工厂方法

加盟披萨店

如果利用SimplePizzaFactory,写出三种不同的工厂,分别是
NYPizzaFactory,ChicagoPizzaFactory,CaliforniaPizzaFactory,各地加盟店都有适合的工厂可以使用,这是一种做法。

NYPizzaFactory nyFactory=new NYPizzaFactory();//创建纽约风味pizza的工厂
PizzaStore nyStore=new PizzaStore(nyFactory);//创建pizzastore
nyFactory.orderPizza("Veggie");//制作pizza,得到纽约风味的;

缺点:不能质量控制;目的:只想创建pizza不同,其他操作相同;

框架

02简单工厂中,在SimplePizzaFactory前,把制作pizza绑到了store,失去了弹性。【需要】:创建框架,把加盟店和pizza创建绑定在一起,但允许保持弹性

有个做法可让披萨制作轰动局限于PizzaStore类,而同时又能让这些加盟店依然可以自由地制作该区域的风味。
把createPizza()方法放回到PizzaStore中,不过要把它设置成“抽象方法”,然后为每个区域风味创建一个PizzaStore的子类。

public abstract class PizzaStore(){
    public Pizza orderPizza(String type){
        Pizza pizza;

        pizza=createPizza(type);//从工厂对象移回pizzastore;【不是factory.createPizza()】
        
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
    public abstract Pizza createPizza(String type);//把工厂对象移到这个方法中
    //作为抽象方法,为每个区域风味创建一个pizzastore子类
}

子类:

orderPizza()方法在抽象的pizzastore中定义,所以,该方法不知道哪个子类实际运行代码并制作pizza;【即不知道哪个实际的具体类【解耦了】】

实现

加盟店只需要继承pizzastore,并提供自己的pizza风味的creatPizza()方法。

纽约风味店:

class NYPizzaStore extends PizzaStore{
    Pizza createPizza(String type){
         if(type.equals("cheese")){
               pizza=new NYStyleCheesePizza();//创建具体类
        }else if(type.equals("pepperoni")){
               pizza=new NYStylePepperoniPizza();
        }else if(type.equals("clam")){
               pizza=new NYStyleClamPizza();
        }else if(type.equals("veggie")){
               pizza=new NYStyleVeggiePizza();
        }
    }
}

工厂方法(以上框架)

对pizzastore类做一些转换,从由一个对象处理具体类的实例化,到由一个子类集合来承担责任。

public abstract class PizzaStore(){
    public Pizza orderPizza(String type){
        Pizza pizza;

        pizza=createPizza(type);//从工厂对象移回pizzastore;【不是factory.createPizza()】
       //pizzastore的子类在creatpizza()中处理对象的实例化
        
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
    public abstract Pizza createPizza(String type);//把工厂对象移到这个方法中
    //作为抽象方法,为每个区域风味创建一个pizzastore子类
    //实例化pizza的责任被转到了方法中(该方法扮演工厂的角色)
}

Pizza类

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

    public void prepare(){
        System.out.println("Preparing"+name);
        System.out.println("Tossing dough");
        System.out.println("Adding sauce");
        System.out.println("Adding toppings:");
        for(int i=0;i<toppings.size();i++){
            System.out.println(""+toppings.get(i));
        }
    }
    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 String getName(){
        return name;
    }
}

Pizza的一些具体子类

public class NYStyleCheesePizza extends Pizza{
    public NYStyleCheesePizza(){
        name="NY Style Sauce and Cheese Pizza";
        dough="Thin Crust Dough";
        sauce="Marinara Sauce";
        toppings.add("Grated Reggiano Cheese");
    }
}

测试:

public class PizzaTestDrive{
    public static void main(String[] args){
        PizzaStore nyStore =new NYPizzaStore();
        PizzaStore chicagoStore=new ChicagoPizzaStore();
        Pizza pizza=nyStore.orderPizza("cheese);
        System.out.println("Ethan ordered a"+pizza.getName()+"\n")
    }
}

工厂方法模式

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

工厂方法模式能够封装具体类型的实例化,抽象的Creator提供了一个创建对象的方法的接口,在抽象的Creator中,任何其他实现的方法,都可能使用到这个工厂方法所制造出来的产品,但只有子类真正实现这个工厂方法并创建产品。

例子类图:

工厂方法是封装的关键

平行视角:平行的类层级

04依赖倒置原则

对象依赖

当你直接实例化一个对象时,就是在依赖它的具体类,返回前页看看这个依赖性很高的比萨店例子,它由披萨店类来创建所有的披萨对象,而不是委托给工厂。

依赖倒置原则:要依赖抽象,不要依赖具体类。

依赖倒置原则更强调抽象。说明高层组件不能依赖底层组件,而且,不管高层或底层组件,都应该依赖抽象。
PizzaStore是“高层组件”,而披萨实现是“底层组件”,很清楚地,PizzaStore依赖这些具体披萨类

应用原则

此时高层组件PizzaStore和底层组件(就是这些比萨)都依赖了Pizza抽象。

05抽象工厂

需求

建造一家成产原料的工厂,并将原料运送到各家加盟店【有些加盟店,使用低价原料来增加利润,你必须采取一些手段,以免回调你的披萨店平牌】

建造原料厂

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

要做的事情是:
1.为每个区域建造一个工厂,需要创建一个继承自PizzaIngredientFactory的子类来实现每一个创建方法。
2.实现一组原料类供工厂使用,例如ReggianoCheese,RedPeppers,ThickCrustDough.这些类可以在何时的区域间共享。
3.然后你仍然需要将这一切组织起来,将新的原料工厂整合进旧的PizzaStore

创建原料工厂子类---纽约

public class NYPizzaIngredientFactory implements PizzaIngredientFactory{

    @Override
    public Dough createDough() {
        return new ThinCrusDough();
    }

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

    @Override
    public Cheese createCheese() {
        return ReggianoCheese();
    }

    @Override
    public Veggies[] createVeggies() {
        Veggies veggies[] = {new Garlic(),new Onion(),new Mushroom(),new RedPepper()};
        return new Veggies;
    }

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

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

重做Pizza类

public abstract class Pizza{
    //每个pizza持有一组原料
    String name;
    Dough dough;
    Sauce sauce;
    Veggies veggies[];
    Cheese cheese;
    Pepperoni pepperoni;
    Clams clams;

    public abstract void prepare();//该方法收集来自工厂的原料

    //除了prepare()方法,其他方法保持不变
    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 void toString2(){
        //这里打印披萨的代码
    }
}

重做Pizza类的子类

class CheesePizza extends Pizza{
    PizzaIngredientFactory ingredientFactory;

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

    @Override
    public void prepare() {
        System.out.println("Preparing"+name);
        //每次需要原理的时候,请求工厂生产
        dough=ingredientFactory.createDough();
        sauce=ingredientFactory.createSauce();
        cheese=ingredientFactory.createCheese();
    }
}

PizzaStore类的子类

public class NYPizzaStore extends PizzaStore{
   protected 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");
       }else if(item.equals("veggie")){
           //……
       }
       return pizza;
   }
}

抽象工厂

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

pizzastore类图

工厂方法和抽象工厂比较

工厂方法


用工厂方法将客户代码从需要实例化的具体类解耦,或者如果事先不知道会需要哪些具体类时,用工厂方法只需要子类化并实现工厂方法!

抽象工厂


任何时候需要创建产品家族,以及想确保客户创建相互关联的产品时,使用抽象工厂。

总结

OO原则:
封装变化
组合优于集承
针对接口编程、而不是针对实现
尽力达到交互对象之间的松耦合设计
类应该对扩展开放,对修改关闭
依赖于抽象,不要依赖于具体类
【我们有了一条新的原则,指导我们尽可能地让事情保持抽象。】
OO模式:
抽象工厂,提供一个接口,用于创建相关或依赖对象的家族,而不必指定它们的具体类
工厂方法,定义一个创建对象的接口,但让子类决定哪个类要实例化。工厂方法让一个类延迟实例化到子类。

要点

1.所有工厂都封装对象的创建。
2.简单工厂虽然不是真正体设计模式,但依然可以为一个简单的方法,将客户从具体类解耦
3.工厂方法靠继承:对象创建被委托给子类,子类实现工厂方法来创建对象
4.抽象工厂靠对象组合:对象创建在工厂接口暴露的方法中实现。
5.所有工厂模式都通过减少应用对具体类的依赖,促进了松耦合。
6.工厂方法的意图,是允许个类延迟实例化到其子类。
7.抽象工厂的意图,是创建相关对象家族,不必依赖于其具体类。
8.依赖倒置原则指导我们避免依赖具体类型,尽量依赖抽象。
9.工厂是强有力的技巧,让我们针对抽象编码,而不是针对具体类。