开发之路(设计模式四:工厂模式上)

本期咱们要介绍一个能让你烘烤本身的OO设计的一种模式,工厂模式

请问除了使用new之外,你还有其余创造对象的方法吗?若是你说没有,那么和我一块儿好好学习下这个模式吧。你会认识到每每实例化不该该老是公开进行,也会认识到初始化常常形成的“耦合”问题。java


在以前的模式当中,我有介绍过一个原则,咱们不该该针对实现编程。但当看到“new”时,就会想到“具体”,不管是 A a=new A()仍是 A a=new B(),都其实在实例化一个具体类,因此的确用的是实现,而不是接口,或者再好比在以前“模拟鸭子”当中
Duck d = new MallardDuck(); Duck是一个接口,但MallardDuck仍是得创建具体类呀,OK若这样想那么至少明白了,代码与具体类“捆绑”是耦合,缺乏弹性一种表现。数据库

那这时就有小伙伴说了:“但总要有建立对象的时候把,Java中只提供了new关键字键对象,还能有别的吗?” 固然new关键字自己没有问题,这是Java原生部分,其实阻碍咱们的是“改变”,针对接口编程,能够隔离掉当“改变”来了之后的许多问题,若是代码是针对接口(这里我在此强调,接口是一个大概念,不仅仅interface是接口,抽象类,类也能是一个“接口”)而写,那么经过多态,它能够与任何新类实现该接口。可是我我的也以为彷佛有问题:当代码大量使用具体类时,会自找麻烦,由于当一旦要求增长某些内容时,就必须改变代码。也就是说这样代码也不是“对修改关闭”(前期内容)了,想要新的具体类型来拓展代码,必须从新修改并覆盖它。编程

说了那么多,具体咋写代码呢,不能纸上谈兵把?OK,我具体来写个实例。假设你有家披萨店。设计模式

注:设计模式须要反复屡次练习,方可灵活运用,前期能够先模仿。

一开始这样写(这里我引用书中笔记)
图片描述app

固然咱们还要传入类型来制做不一样口味的披萨,以下图
图片描述框架

可是你知道吗?这样设计是会有很大压力的,好比你要添加新的口味披萨,或者取出掉某些卖的很差风味的披萨的话。要这么写,不管增长新披萨或者删除掉原有披萨你都要到这里操做,上面部分会进行修改,而下面部分准备原料,烘烤,切片,装盘,操做都是不改变的,只有上面打算作什么口味披萨的这个动做会改变。ide

注:请区别出哪些是会改变的,哪些不会改变

图片描述

咱们作的改进,封装建立对象的代码,要把建立披萨的代码转移到另外一个对象中,由这个对象专职建立披萨,这个新对象只干这一件事,如图
图片描述学习

没错,咱们称呼为它“工厂”。
在JavaWeb当中有个持久层框架Hibernate,其一个核心接口“工厂”(SessionFactory)就是用到工厂模式,负责初始化hibernate,是数据源的代理,负责建立Session对象,一个数据库对象只需配置一个SessionFactory,如有多个也能够配置多个。测试

工厂(factory)处理建立对象的细节,一旦有了Factory,orderPizza()就变成了,此对象的客户。当须要什么类型披萨时,就叫披萨工厂作一个。如今orderPizza()方法只关心从工厂获得一个披萨,而这个披萨实现了Pizza接口,因此能够调用准备材料,烘烤,切片,装盘方法。spa

那咱们来建立个简单工厂。
图片描述

可能有小伙伴提问:“感受只是把以前会被大量修改的问题移到另外一个对象里而已”
注:SimlePizzaFactory会有不少客户,目前咱们看到orderPizza()方法是它客户,可是,可能还有PizzaShopMenu(披萨店菜单),xxxxPizzaShop(各类不一样风格披萨店)等等会利用这个工厂来取得披萨里的内容,总而言之,SimlePizzaFactory会有许多客户。因此,把建立披萨的代码包装进一个类,当之后实现改变时,只需修改此类便可。我正要把具体实例化的过程,在客户代码中删除。

此时咱们写一个披萨“工厂”的客户类(PizzaStore)
图片描述

以上就是简单工厂模式的内容了,
注:简单工程严格来讲不是一个设计模式,更像是一种编程习惯,但因为常常被使用,因此有时候不少开发人员误认为“工厂模式”。

下面是简单工程的类图。(请注意理解)
图片描述

注:
再次提醒:所谓的“实现一个接口”并不必定表示“写一个(interface)类,而后利用implements关键词来实现某个Java接口”。“实现一个接口”泛指“实现某个超类型(能够是类或接口)的某个方法”。

你觉得这篇文章写道这里就结束了?哈哈,别忘了个人口号是啥~~

既然咱们有了本身的披萨店,咱们固然但愿将它作大作强,咱们开始加盟披萨店。
图片描述

这里咱们建立对应的“工厂”对象。
图片描述

但这又有一个问题了,难道你能听任加盟店无论吗?这样各个加盟店就按照本身的方式,烘烤的作法不一样,不要切片,使用其余厂商的盒子,若是可以创建一个框架,把加盟店和建立披萨捆绑在一块儿的同时又保持必定的弹性。

有个作法能够把披萨制做活动局限在客户(PizzaStore)类中,而同时又能让这些加盟店依然能够自由的建立该区域披萨,就是将createPizza方法放回PizzaStore内,将其设置为“抽象方法”,让后每一个区域的加盟店建立它的子类,就是说二者之间造成依赖关系。
图片描述
让咱们详细一点。
图片描述

从orderPizza()方法观点来看,此方法在抽象父类PizzaStore里定义,但具体实现仍是在不一样子类中。面对每一个不一样子类,父类的orderPizza()方法其实并不知道哪一个子类将实际上制做披萨。

orderPizza()方法对Pizza对象指向,(准备材料,烘烤,切片,装盒)但因为Pizza对象是抽象的,orderPizza()对象也并不知道哪些实际参与的具体类,换句话说,这就是解耦。(WTF?这就是解耦,不少时候每每初学设计模式的人还不能作到很敏感,包括我本身,须要长时间不断学习)

OK,咱们这时候开一家加盟店咯。
图片描述
注:超类(父类)PizzaStore中orderPizza()方法,并不知道正要建立什么披萨,它只知道制做披萨有四个步骤。。。

如今咱们建立工厂方法,以前都是new,对象负责具体类的实例化,如今咱们经过PizzaStore作一个小转变(其实在这里,我我的感受说他是工厂方法感受怪怪的)
图片描述

咱们再来巩固下工厂方法(这里我放出个人笔记截图)
图片描述

咱们来写下购买披萨流程
1:首先,客户们须要取得披萨店实例。客户须要实例化一个NYStylePizzaStore或ChicagoStylePizzaStore
2:有了各自的PizzaStore订单,客户们分别调用orderPizza()方法,并传入他们所喜好的比萨类型(芝士,素食)。
3:orderPizza调用createPizza()建立披萨,纽约风味披萨(NYStylePizzaStore)或者芝加哥风味披萨(ChicagoStylePizzaStore)。
4:orderPizza()并不知道具体建立的是哪种披萨,只知道这是一个披萨,可以准备原料,烘烤,切片,被打包装盒,而后送出给顾客。

定义工厂方式模式:工厂模式定义了一个建立对象的接口,不在直接使用new了,但由子类决定要实例化的类是哪个。工厂方法让类把实例化推迟到子类了。
注:工厂方法模式可以封装具体类型的实例化。工厂方法让子类决定要实例化的类是哪个。所谓“决定”,并非指模式容许子类自己在运行时作决定,而是指在编写建立者类时,不须要知道实际建立的产品是哪个。选择了哪一个子类,天然就决定了实际建立的东西是什么(可能有点难以理解。。。)

下图是工厂方法模式类图
图片描述

好的说这么多,下面就是具体实现的时候了,因为此例子量稍多,可是量大不表明我会偷工减料的,嘿嘿嘿~~~

这是参考的Java工程class图
图片描述

代码开始

PizzaStore(工厂类)

package Store;

import Pizza.Pizza;

/**
 * PizzaStore至关于客户(工厂类), 日后会有不少相似此类进行拓展
 * 
 * 在这里orderPizza()方法对pizz对象作了不少事(createPizza,prepare,bake,cut,box)
 * 可是因为Pizza对象是抽象的(接口),
 * orderPizza()并不知道具体哪些实际类参与进来了(至关于实现Pizza接口的实现类有哪些orderPizza()方法并不知道)
 * 换句话说,这就是解耦,减小相互依赖
 * 
 * 同理createPizza()方法中也不知道建立的具体是什么披萨 只知道披萨能够被准备,被烘烤,被切片,被装盒行为而已
 * 
 */
// 披萨店
public abstract class PizzaStore {

    // 点单
    public Pizza orderPizza(String type) {
        // createPizza()方法从工厂对象中取出,
        // 而且方法也再也不这里实现,定义为抽象类,谁须要在拿走方法去具体实现
        Pizza pizza = createPizza(type);
        System.out.println("----制做一个" + pizza);
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }

    // 把工厂对象移到这个方法里,并封装
    // 如今工厂方法为抽象的,子类必须实现这个抽象方法
    public abstract Pizza createPizza(String item);
}

下面是“工厂”具体实现
纽约风格披萨店(分店)

package Store;

import Pizza.NYStyleCheesePizza;
import Pizza.NYStyleClamPizza;
import Pizza.NYStylePepperoniPizza;
import Pizza.NYStyleVeggiePizza;
import Pizza.Pizza;

/**
 * 此时NYStylePizzaStore所封装的是关于如何制做纽约风格的比萨
 * 
 * @author Joy
 * 
 */

// 纽约风格披萨店(分店)
public class NYStylePizzaStore extends PizzaStore {

    // 根据披萨的不一样,建立不一样披萨(具体实现类)
    @Override
    public Pizza createPizza(String item) {
        if (item.equals("芝士")) {
            return new NYStyleCheesePizza();
        } else if (item.equals("素食")) {
            return new NYStyleVeggiePizza();
        } else if (item.equals("海鲜")) {
            return new NYStyleClamPizza();

        } else if (item.equals("香肠")) {
            return new NYStylePepperoniPizza();
        }
        return null;
    }
}

芝加哥披萨店(分店)

package Store;

import Pizza.ChicagoStyleCheesePizza;
import Pizza.ChicagoStyleClamPizza;
import Pizza.ChicagoStylePepperoniPizza;
import Pizza.ChicagoStyleVeggiePizza;
import Pizza.Pizza;

/**
 * 此时ChicagoStylePizzaStore所封装的是关于如何制做芝加哥风格的比萨
 * 
 * @author Joy
 * 
 */
// 芝加哥披萨店(分店)
public class ChicagoStylePizzaStore extends PizzaStore {

    @Override
    public Pizza createPizza(String item) {
        if (item.equals("芝士")) {
            return new ChicagoStyleCheesePizza();
        } else if (item.equals("素食")) {
            return new ChicagoStyleVeggiePizza();
        } else if (item.equals("海鲜")) {
            return new ChicagoStyleClamPizza();
        } else if (item.equals("香肠")) {
            return new ChicagoStylePepperoniPizza();
        }
        return null;
    }
}

Pizza类
(至关于各个“产品”的接口)

package Pizza;

import java.util.ArrayList;

public abstract class Pizza {
    String name;// 披萨名称
    String dough;// 面团类型
    String sauce;// 酱料
    ArrayList toppings = new ArrayList();// 一套佐料

    public 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.print(toppings.get(i) + "   ");
        }
    }

    public void bake() {
        System.out.println("\n" + "披萨正在烘烤,需烘烤25分钟。。。");
    }

    public void cut() {
        System.out.println("制做完成,给披萨切片。。。");
    }

    public void box() {
        System.out.println("给披萨打包装盒。。。");
    }

    public String getName() {
        return name;
    }

    // 输出客人点的披萨信息
    @Override
    public String toString() {
        StringBuffer display = new StringBuffer();
        display.append(name + " ----\n");
        display.append(dough + "\n");
        display.append(sauce + "\n");
        for (int i = 0; i < toppings.size(); i++) {
            display.append((String) toppings.get(i) + "\n");
        }
        return display.toString();
    }

}

下面是各类披萨具体实现类

package Pizza;

//加州披萨店的芝士披萨
public class ChicagoStyleCheesePizza extends Pizza {
    public ChicagoStyleCheesePizza() {
        name="加州芝士披萨";
        dough="厚饼";
        sauce="番茄酱";
        toppings.add("干碎奶酪");                
    }
    @Override
    public void cut() {    
        System.out.println("将披萨切成方形");
    }

}
package Pizza;

//加州披萨店的海鲜披萨
public class ChicagoStyleClamPizza extends Pizza {

    public ChicagoStyleClamPizza() {
        name = "加州海鲜披萨";
        dough = "厚饼";
        sauce = "番茄酱";
        toppings.add("干碎奶酪");
        toppings.add("蟹黄");
        toppings.add("龙虾");
        toppings.add("各类海鲜");
    }
    
    @Override
    public void cut() {    
        System.out.println("将披萨切成三角形");
    }    

}
package Pizza;

//加州披萨店里的香肠披萨
public class ChicagoStylePepperoniPizza extends Pizza {

    public ChicagoStylePepperoniPizza() {
        name = "加州香肠披萨";
        dough = "厚饼";
        sauce = "番茄酱";

        toppings.add("干碎奶酪");
        toppings.add("黑胡椒");
        toppings.add("菠菜");
        toppings.add("茄子");
        toppings.add("香肠切片");
    }

    @Override
    public void cut() {
        System.out.println("切成方形薄披萨");
    }

}
package Pizza;

//加州披萨店里的素披萨
public class ChicagoStyleVeggiePizza extends Pizza {

    public ChicagoStyleVeggiePizza() {
        name = "加州素披萨";
        dough = "厚饼";
        sauce = "番茄酱";
        toppings.add("干碎奶酪");
        toppings.add("圣女果");
        toppings.add("黑胡椒粉");
        toppings.add("红辣椒");
        toppings.add("甜玉米粒");
        toppings.add("色拉");
    }

    @Override
    public void cut() {
        System.out.println("切成方形薄披萨");
    }
}
package Pizza;


//纽约披萨店里的芝士披萨
public class NYStyleCheesePizza extends Pizza {

    public NYStyleCheesePizza() {
        name="纽约芝士披萨";
        dough="薄饼";
        sauce="番茄酱";
        toppings.add("干奶酪");    
    }    
}
package Pizza;

//纽约披萨店里的河鲜披萨
public class NYStyleClamPizza extends Pizza {

    public NYStyleClamPizza() {
        name = "纽约海鲜披萨";
        dough="薄饼";
        sauce="番茄酱";
        toppings.add("巴马干奶酪");
        toppings.add("蟹黄");
        toppings.add("龙虾");
    }
}
package Pizza;




//纽约披萨店里的香肠披萨
public class NYStylePepperoniPizza extends Pizza {

    public NYStylePepperoniPizza() {
        name = "纽约香肠披萨";
        dough="薄饼";
        sauce="番茄酱";
        toppings.add("巴马干酪奶酪");
        toppings.add("意大利香肠切片");
        toppings.add("大蒜");
        toppings.add("洋葱");
        toppings.add("香菇");
        toppings.add("红辣椒");
    }
}
package Pizza;

//纽约披萨店里的素披萨
public class NYStyleVeggiePizza extends Pizza {

    public NYStyleVeggiePizza() {
        name = "纽约素食披萨";
        dough = "薄饼";
        sauce = "番茄酱";
        toppings.add("巴马干酪奶酪");
        toppings.add("洋葱");
        toppings.add("各类蔬菜");
    }
}

测试类

package TestMain;

import Pizza.Pizza;
import Store.ChicagoStylePizzaStore;
import Store.NYStylePizzaStore;
import Store.PizzaStore;

public class TestMain {
    public static void main(String[] args) {
        PizzaStore nyStore = new NYStylePizzaStore();
        PizzaStore chicagoStore = new ChicagoStylePizzaStore();
        // System.out.println("====================================");
        // Pizza pizza1 = nyStore.orderPizza("芝士");
        // System.out.println("Joy点了一个" + pizza1.getName());
        // System.out.println("====================================");
        // Pizza pizza2 = nyStore.orderPizza("素食");
        // System.out.println("Joy点了一个" + pizza2.getName());
        System.out.println("====================================");
        Pizza pizza3 = nyStore.orderPizza("海鲜");
        System.out.println("Joy点了一个" + pizza3.getName() + " ,正在送出");
        // System.out.println("====================================");
        // Pizza pizza4 = nyStore.orderPizza("香肠");
        // System.out.println("Joy点了一个" + pizza4.getName());
        System.out.println("====================================");
        Pizza pizza11 = chicagoStore.orderPizza("芝士");
        System.out.println("Joy点了一个" + pizza11.getName() + " ,正在送出");
        // System.out.println("====================================");
        // Pizza pizza22 = chicagoStore.orderPizza("素食");
        // System.out.println("Joy点了一个" + pizza22.getName());
        // System.out.println("====================================");
        // Pizza pizza33 = chicagoStore.orderPizza("海鲜");
        // System.out.println("Joy点了一个" + pizza33.getName());
        // System.out.println("====================================");
        // Pizza pizza44 = chicagoStore.orderPizza("香肠");
        // System.out.println("Joy点了一个" + pizza44.getName());

    }
}

效果以下:
图片描述

这个模式是个颇有用的模式,让我更加能封装变化了,模拟代码已经所有放出,注释也写的比较清楚,如有不理解欢迎留言。

感谢你看到这里,工厂模式上部分结束,但工厂模式内容还没彻底结束 本人文笔随便,如有不足或错误之处望给予指点,90度弯腰~~~很快我会补全完这个内容。 生命不息,编程不止!

参考书籍:《Head First 设计模式》
相关文章
相关标签/搜索