设计模式 - 工厂模式

预设场景

假设我们要经营一个披萨店,我们希望根据用户要求的为其提供不同口味的披萨,例如用户可能喜欢:CheesePizzaPepperoniPizzaPepperoniPizzaClamPizzaVeggiePizza等等,创造出这样一块符合用户口味的披萨之后,我们再对其进行准备,烘烤,切片和包装等一系列加工,那么就产生这么一个预订披萨的方法orderPizza()

//披萨店的实现类
public class PizzaShop {

    //预订披萨方法
    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();
        } else if (type.equals("clam")) {
            pizza = new ClamPizza();
        } else if (type.equals("veggie")){
            pizza = new VeggiePizza();
        } else {
            pizza = new CommonPizza();
        }
		//加工
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();

        return pizza;

    }
}

但是,当披萨店想要新增或者下架一些口味的披萨时,就需要对这个披萨店的orderPizza()的逻辑做一定的修改,这违反了我们的设计原则:

面对修改关闭,面对拓展开放

我们已经知道面对新的需求,有哪些地方是会发生改变的,就应该使用封装去设计,比如:简单工厂

简单工厂

我们可以以上orderPizza()方法中,创建实例的代码放到一个简单工厂中,由这个工厂负责所有实例的创建,而orderPizza()方法只负责对Pizza进行加工,因此我们代码就变成:

//披萨店的实现类
public class PizzaShop {
    SimplePizzaFactory factory;
    
    public PizzaShop(SimplePizzaFactory factory) {
        this.factory = factory;
    }
    
    //预订披萨方法
    Pizza orderPizza(String type) {

        Pizza pizza;
        //简单工厂负责创建实例
        pizza = factory.createPizza(type);
        
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;

    }
}

//简单披萨工厂
public class SimplePizzaFactory {
    
    public Pizza createPizza(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();
        } else if (type.equals("clam")) {
            pizza = new ClamPizza();
        } else if (type.equals("veggie")){
            pizza = new VeggiePizza();
        } else {
            pizza = new CommonPizza();
        }
        return pizza;
    }

}

这样我们就把创建实例的代码放到了简单工厂中,如果有新的需求,我们只需要在简单工厂中修改,披萨店PizzaShop的代码就可以面对修改封闭。同时这个简单工厂还可以被多个不同的PizzaShop实现类调用,这样也能更好的实现模块化。

其实简单工厂严格上来说不是一种设计模式,而更像是一种编程习惯

工厂模式

  • 代码演练

当在使用简单工厂创建披萨的时候,你会发现,调用工厂的不同披萨店(我们称其为加盟店),每个店铺除了创建Pizza是用了我们的工厂创建,其他的加工部分(prepare,bake,cut,box)都是用他们自己店铺的实现,加工的方法每个店铺都不一样。
想想这个问题,我们希望能够建立起一个框架,把加盟店和所创建的Pizza绑定到一起,多一些加工流程的质量控制,同时又具有一定的弹性。

所以我们创建一个抽象的披萨店类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;
    }
	//把工厂对象放到这个抽象方法中
    public abstract Pizza createPizza(String type);
    
}

此时这个抽象披萨店类已经有一个不错的订单系统了,orderPizza()方法负责处理订单,能满足我们希望所有加盟店对订单处理一致的要求
各个不同的披萨店之间的差异主要在于他们制作披萨的风味,我们按区域划分不同的披萨店,每个披萨店都有自己的特色,比如(纽约的披萨饼薄,芝加哥的披萨饼厚),现在我们就想让抽象披萨店中的createPizza()方法能够面对这些变化来创建出正确种类的披萨。做法就是让子类自己负责createPizza()方法,这也是为什么该方法是抽象的原因,如图:

在这里插入图片描述
加盟店有它自己的特色,又可以从PizzaStore继承标准的加工方法,加盟店只需要提供createPizza()方法的实现即可,以下是两个加盟店的实现编码:

//纽约哥加盟披萨店
public class NYPizzaStore extends PizzaStore {

    @Override
    public Pizza createPizza(String type) {
        Pizza pizza;
        if (type.equals("cheese")) {
            pizza = new NYStyleCheesePizza();
        } else if (type.equals("greek")) {
            pizza = new NYStyleGreekPizza();
        } else if (type.equals("pepperoni")) {
            pizza = new NYStylePepperoniPizza();
        } else if (type.equals("clam")) {
            pizza = new NYStyleClamPizza();
        } else if (type.equals("veggie")){
            pizza = new NYStyleVeggiePizza();
        } else {
            pizza = new NYStyleCommonPizza();
        }
        return pizza;
    }
}

//芝加哥加盟披萨店
public class ChicagoPizzaStore extends PizzaStore {

    @Override
    public Pizza createPizza(String type) {
        Pizza pizza;
        if (type.equals("cheese")) {
            pizza = new ChicagoStyleCheesePizza();
        } else if (type.equals("greek")) {
            pizza = new ChicagoStyleGreekPizza();
        } else if (type.equals("pepperoni")) {
            pizza = new ChicagoStylePepperoniPizza();
        } else if (type.equals("clam")) {
            pizza = new ChicagoStyleClamPizza();
        } else if (type.equals("veggie")){
            pizza = new ChicagoStyleVeggiePizza();
        } else {
            pizza = new ChicagoStyleCommonPizza();
        }
        return 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 sauce");
        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("Cut the pizza into diagonal slices");
    }

    public void box() {
        System.out.println("Place pizza in official PizzaStore box");
    }

    public String getName() {
        return name;
    }

}

同时我们还需要一些具体的Pizza子类来定义纽约和芝加哥风味的披萨,这里给出两个芝士口味的披萨作为范例,其他的子类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 ChicagoStyleCheesePizza extends Pizza {

    public ChicagoStyleCheesePizza() {
        name = "Chicago Style Deep Dish Cheese Pizza";
        dough = "Extra Thick Crust Dough";
        sauce = "Plum Tomato Sauce";

        toppings.add("Shredded mozzarella Cheese");
    }

    public void cut() {
        System.out.println("Cutting the pizza into square slices");
    }

}

测试案例:

public class PizzaTestDrive {

    public static void main(String[] args) {
    
        NYPizzaStore nyPizzaStore = new NYPizzaStore();
        ChicagoPizzaStore chicagoPizzaStore = new ChicagoPizzaStore();

        Pizza nyCheesePizza = nyPizzaStore.createPizza("cheese");
        System.out.println("小二预订了:" + nyCheesePizza.getName());

        Pizza chicagoCheesePizza = chicagoPizzaStore.createPizza("cheese");
        System.out.println("张三预订了:" + chicagoCheesePizza.getName());
   
    }
}

测试结果:

小二预订了:NY Style Sauce and  Cheese Pizza
张三预订了:Chicago Style Deep Dish Cheese Pizza

Process finished with exit code 0
  • 优势分析

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

当我们实例化一个对象时,实际上就是在依赖它的具体类,当我们返回观看第一个代码块时,可以发现这个PizzaShop的订单系统依赖所有的具体披萨类,依赖关系如图:
在这里插入图片描述
一但这些披萨具体类发生变化,我们披萨店订单系统需要跟着变化,如此高度耦合的代码,不是一个很好的设计。
而减少代码中对具体类的依赖一直是我们推崇的设计方法,这也是面向对象的设计原则,这也有一个比较响亮的名字:依赖倒置原则(Dependency Inversion Principle)
虽然我们创建了一个抽象Pizza类,但是我们也在代码中实实在在地创建了Pizza,所以这个抽象没有什么影响力。
如何在orderPizza()方法中把这些实例对象代码独立出来?工厂方法刚好能派上用场,用上工厂方法之后,依赖图就变成:
在这里插入图片描述
这样高层组件(PizzaShop)变成只依赖Pizza抽象类,底层组件(披萨具体类)也也来Pizza抽象类,这样就实现了依赖倒置,对代码的实现进行了解耦。想要实现依赖倒置,工厂方法可能不是唯一的方法,但是确是一个最好的方法。

抽象工厂模式

我去复习一下…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值