工厂模式02之工厂方法模式

参考:Head First设计模式

概述

  • 简单工厂模式实现了生成产品类的代码与客户端代码分离,在工厂类中可以添加生成产品的逻辑代码。

  • 但是简单工厂模式不符合“开放-封闭”原则。例如要加一个 新产品类,就要修改 工厂类 生成产品的逻辑代码,增加if-else判断。对于这个问题,工厂方法模式可以解决。

定义

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

类图

  • 角色
  1. 抽象产品类 : Product
  2. 具体产品类 : ConcreteProductA 和 ConcreteProductB
  3. 抽象工厂类 : AbstractFactory
  4. 具体工厂类 : ConcreteFactoryA 和 ConcreteFactoryB

实例

还是以披萨店的披萨订单为例,用工厂方法模式来处理披萨店的订单。并且披萨店还开了加盟店,有纽约披萨店芝加哥披萨店

类图

  • 创建者类(抽象工厂类 + 具体工厂实现类)
  • 产品类(抽象产品类 + 具体产品实现类)

代码实现

  • 抽象工厂类PizzaStore
package cn.edu.nwpu.factoryMethod;
/**
 * 
 * @author yylin
 *
 */
public abstract class PizzaStore {

    public Pizza orderPizza(String type) {
        Pizza pizza = null;
        
        pizza = createPizza(type);
        
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        
        return pizza;
    }

    // 抽象的工厂方法
    abstract Pizza createPizza(String type);
}
  • 具体工厂实现类1 NYPizzaStore
package cn.edu.nwpu.factoryMethod;
/**
 * 
 * @author yylin
 *
 */
public class NYStylePizzaStore extends PizzaStore {

    @Override
    Pizza createPizza(String type) {

        Pizza pizza = null;
        
        if(type.equals("cheese")) {
            pizza = new NYStyleCheesePizza();
        } else if(type.equals("prpperoni")) {
            pizza = new NYStylePepperoniPizza();
        } else if(type.equals("clam")) {
            pizza = new NYStyleClamPizza();
        } else if(type.equals("veggie")) {
            pizza = new NYStyleVeggiePizza();
        }
        
        return pizza;
    }

}
  • 具体工厂实现类2 ChicagoPizzaStore
package cn.edu.nwpu.factoryMethod;
/**
 * 
 * @author yylin
 *
 */
public class ChicagoStylePizzaStore extends PizzaStore {

    @Override
    Pizza createPizza(String type) {

        Pizza pizza = null;
        
        if(type.equals("cheese")) {
            pizza = new ChicagoStyleCheesePizza();
        } else if(type.equals("prpperoni")) {
            pizza = new ChicagoStylePepperoniPizza();
        } else if(type.equals("clam")) {
            pizza = new ChicagoStyleClamPizza();
        } else if(type.equals("veggie")) {
            pizza = new ChicagoStyleVeggiePizza();
        }
        
        return pizza;
    }

}
  • 抽象产品类Pizza
package cn.edu.nwpu.factoryMethod;

import java.util.ArrayList;

/**
 * 
 * @author yylin
 *
 */
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;
    }
}
  • 具体产品实现类1 NYStyleCheesePizza
package cn.edu.nwpu.factoryMethod;
/**
 * 
 * @author yylin
 *
 */
public class NYStyleCheesePizza extends Pizza {

    /*
     * 具体产品实现类中可以加入自己的特色,或者覆盖Pizza类中的方法
     */
    public NYStyleCheesePizza(){
        name = "NY Style Sauce and Cheese Pizza";
        dough = "Thin Crust Dough";
        sauce = "Marinara Sauce";
        
        toppings.add("Grated Reggiano Cheese");
    }
}
  • 具体产品实现类2 NYStylePepperoniPizza
package cn.edu.nwpu.factoryMethod;
/**
 * 
 * @author yylin
 *
 */
public class NYStylePepperoniPizza extends Pizza {

}
  • 具体产品实现类3 NYStyleClamPizza
package cn.edu.nwpu.factoryMethod;
/**
 * 
 * @author yylin
 *
 */
public class NYStyleClamPizza extends Pizza {

}
  • 具体产品实现类4 NYStyleVeggiePizza
package cn.edu.nwpu.factoryMethod;
/**
 * 
 * @author yylin
 *
 */
public class NYStyleVeggiePizza extends Pizza {

}
  • 具体产品实现类5 ChicagoStyleCheesePizza
package cn.edu.nwpu.factoryMethod;

/**
 * 
 * @author yylin
 *
 */
public class ChicagoStyleCheesePizza extends Pizza {

    /*
     * 具体产品实现类中可以加入自己的特色,或者覆盖Pizza类中的方法
     */
    public ChicagoStyleCheesePizza() {
        name = "";
        dough = "";
        sauce = "";

        toppings.add("");
    }

    @Override
    public void cut() {
        System.out.println("Cutting the pizza into square slices");
    }
}
  • 具体产品实现类6 ChicagoStylePepperoniPizza
package cn.edu.nwpu.factoryMethod;
/**
 * 
 * @author yylin
 *
 */
public class ChicagoStylePepperoniPizza extends Pizza {

}
  • 具体产品实现类7 ChicagoStyleClamPizza
package cn.edu.nwpu.factoryMethod;
/**
 * 
 * @author yylin
 *
 */
public class ChicagoStyleClamPizza extends Pizza {

}
  • 具体产品实现类8 ChicagoStyleVeggiePizza
package cn.edu.nwpu.factoryMethod;
/**
 * 
 * @author yylin
 *
 */
public class ChicagoStyleVeggiePizza extends Pizza {

}
  • 测试类 PizzaTestDrive

顾客Ethan想要纽约风格的奶酪披萨,顾客Joel想要芝加哥风格的奶酪披萨。

package cn.edu.nwpu.factoryMethod;
/**
 * 
 * @author yylin
 *
 */
public class PizzaTestDrive {

    public static void main(String[] args) {

        // 建立两个不同的披萨店
        PizzaStore nyStore = new NYStylePizzaStore();
        PizzaStore chicagoStore = new ChicagoStylePizzaStore();

        // 顾客Ethan的订单
        Pizza pizza = nyStore.orderPizza("cheese");
        System.out.println("Ethan ordered a " + pizza.getName() + "\n");
        
        // 顾客Joel的订单
        pizza = chicagoStore.orderPizza("cheese");
        System.out.println("Joel ordered a " + pizza.getName() + "\n");
    }
}
  • 测试结果
Preparing NY Style Sauce and Cheese Pizza
Tossing dough...
Adding sauce...
Adding toppings: 
   Grated Reggiano Cheese
Bake for 25 minutes at 350
Cutting the pizza into diagonal slices
Place pizza in official PizzaStore box
Ethan ordered a NY Style Sauce and Cheese Pizza

Preparing Chicago Style Deep Dish Cheese Pizza
Tossing dough...
Adding sauce...
Adding toppings: 
   Shredded Mozzarella Cheese
Bake for 25 minutes at 350
Cutting the pizza into square slices
Place pizza in official PizzaStore box
Joel ordered a Chicago Style Deep Dish Cheese Pizza

小结

  1. 工厂方法模式又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂模式(Polymorphic Factory)。

  2. 在工厂方法模式中,父类负责定义创建对象的公共接口,而子类则负责生成具体的对象;
    这样做的目的是将类的实例化操作延迟到子类中完成;
    即由子类来决定究竟应该实例化(创建)哪一个类。

  3. 工厂方法模式包含四个角色:
    (1)抽象产品是定义产品的接口,是工厂方法模式所创建对象的超类型,即产品对象的共同父类或接口;
    (2)具体产品实现了抽象产品接口,某种类型的具体产品由专门的具体工厂创建,它们之间往往一一对应;
    (3)抽象工厂中声明了工厂方法,用于返回一个产品,它是工厂方法模式的核心,任何在模式中创建对象的工厂类都必须实现该接口;
    (4)具体工厂是抽象工厂类的子类,实现了抽象工厂中定义的工厂方法,并可由客户调用,返回一个具体产品类的实例。

  4. 工厂方法模式是简单工厂模式的进一步抽象和推广。
    由于使用了面向对象的多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。
    在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给子类去做。
    这个核心类仅仅负责给出具体工厂必须实现的接口,而不负责产品类被实例化这种细节,这使得工厂方法模式可以允许系统在不修改工厂角色的情况下引进新产品。

  5. 工厂方法模式:
    (1)主要优点:增加新的产品类时无须修改现有系统,并封装了产品对象的创建细节,系统具有良好的灵活性和可扩展性;
    (2)缺点在于增加新产品的同时需要增加新的工厂,导致系统类的个数成对增加,在一定程度上增加了系统的复杂性。

  6. 工厂方法模式适用情况包括:
    (1)一个类不知道它所需要的对象的类;
    (2)一个类通过其子类来指定创建哪个对象;
    (3)将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,340评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,762评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,329评论 0 329
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,678评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,583评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,995评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,493评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,145评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,293评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,250评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,267评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,973评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,556评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,648评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,873评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,257评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,809评论 2 339

推荐阅读更多精彩内容

  • 设计模式概述 在学习面向对象七大设计原则时需要注意以下几点:a) 高内聚、低耦合和单一职能的“冲突”实际上,这两者...
    彦帧阅读 3,733评论 0 14
  • 设计模式汇总 一、基础知识 1. 设计模式概述 定义:设计模式(Design Pattern)是一套被反复使用、多...
    MinoyJet阅读 3,884评论 1 15
  • 该文章属于刘小壮原创,转载请注明:刘小壮[https://www.jianshu.com/u/2de707c93d...
    刘小壮阅读 12,709评论 29 59
  • 除了使用new操作符之外,还有更多制造对象的方法。你讲了解到实例化这个活动不应该总是公开的进行,也会认识到初始化经...
    pilipalaKing阅读 362评论 0 0
  • 第一章 制定教学目标的策略 策略一 :把握教学方向 《义务教育数学课程标准》(20111年版)明确提出要通过...
    平常心666阅读 1,515评论 0 1