工厂方法模式定义:定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。(注:“决定”不是指模式允许子类本身在运行时做决定,而是指在编写创建者类时,不需要知道实际创建的产品是哪一个。选择了使用哪个子类,自然就决定了实际创建的产品是什么)

假设我们要开一间披萨店,提供不同口味的披萨。

首先有一个PizzaStore的类,里面提供一个orderPizza的方法,让客户选择要购买的Pizza。

最开始想到的是这样写:

public class PizzaStore {

    public Pizza orderPizza(String type) {
Pizza pizza = null; 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;
}
}

当披萨店增加比萨口味的时候,必须修改if else部分的代码,这样做没有对修改关闭。

所以,将代码会变化的部分提取出来,封装创建对象的代码到另一个类,就是SimplePizzaFactory。

public class SimplePizzaFactory {
public Pizza createPizza(String type) {
Pizza pizza = null; if(type.equals("cheese")) {
pizza = new CheesePizza();
} else if(type.equals("greek")){
pizza = new GreekPizza();
} else if(type.equals("pepperoni")){
pizza = new PepperoniPizza();
} return pizza;
}
}

PizzaStore变成了:

public class PizzaStore {

    SimplePizzaFactory factory;

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

问题:是否只是将问题搬到另一个对象?问题依然存在。

这样做有好处,SimplePizzaFactory可以有许多客户,不仅仅是PizzaStore,如果有一个类,PizzaShopMenu要求获得比萨的价格和描述,它只需与SimplePizzaFactory交互。所以,把创建比萨的代码包装进一个类,当以后实现改变时,只需要修改这个类即可。这就是简单工厂方法。

披萨店发展的很好,现在要在全球各地开分店,但不同区域的口味有差异,比如(纽约风味、芝加哥风味、加州风味)。

一种做法是,利用SimplePizzaFactory,写出三种不同的工厂NYPizzaFactory,ChicagoPizzaFactory、CaliforniaPizzaFactory。坏处是:比如在中国要开一个China风味的Pizza,要增加一个ChinaPizzaFactory的类,如果披萨的口味增加,比如增加一种不添加任何料的pizza,那么需要修改全部SimplePiizaFactory的子类。

另一种做法是,在实际中,不太可能单独划分不同工厂来生产不同披萨,而且,制造披萨一般是在披萨店现场制造的。那么就需要将制作披萨与披萨店绑定。

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

PizzaStore现在是一个抽象类,让NYStylePizzaStore,ChicagoStylePizzaStore,CaliforniaPizzaFactory继承PizzaStore实现各自的createPizza方法。让不同的披萨店(子类)来决定披萨的做法。坏处是:每增加一种披萨口味,要增加一个继承Pizza的子类和修改全部继承PizzaStore的子类的代码。这就是工厂方法。

现在为确保每家披萨店使用高质量原料,打算建造一家生产原料的工厂,并将原料运送到各家加盟店。问题是,不同地方的原料是不一样的,比如(芝加哥的人喜欢番茄酱料,纽约的人喜欢大蒜番茄酱料。

先定义一个原料工厂接口:

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

现在可为不同区域的披萨店提供不同原料了:

public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
public Dough createDough() {
return new ThinCrustDough();
} public Sauce createSauce() {
return new MarinaraSauce();
}
public Cheese createCheese() {
return new ReggianoCheese();
}
public Veggies[] createVeggies() {
Veggies veggies[] = { new Garlic(), new Onion(), new Mushroom(), new RedPepper() };
return veggies;
}
public Pepperoni createPepperoni() {
return new SlicedPepperoni();
}
public Clams createClam() {
return new FreshClams();
}
}

重做披萨类,使用我们的工厂生产的原材料:

public abstract class Pizza {
String name;
Dough dough;
Sauce sauce;
Veggies veggies[];
Cheese cheese;
Pepperoni pepperoni;
Clams clam; abstract void perpare(); //现在perpare方法声明为抽象,这个方法实现收集特定pizza的原材料 void bake() {
System.out.println("Bake for 25 minutes at 350");
}
void cut() {
System.out.println("Place pizza in official PizzaStore box");
}
void setName(String name) {
this.name = name;
}
String getName() {
return name;
}
public String toString() {
System.out.println(name);
}
}

一个Pizza类的子类:

public class CheesPizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
public CheesePizza(PizzaIngredientFactory i) {
ingredientFactory = i;
} void prepare(){ //现在不同的pizza实现不同的prepare方式
System.out.println("Preparing " + name);
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
cheese = ingredientFactory.createCheese();
}
}

再回到披萨店:

public class NYPizzaStore extends PizzaStroe {
public 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")) {
pizza = new VeggiePizza(ingredientFactory);
pizza.setName("New York Style Veggie Pizza");
}
...... return pizza;
}
}

我们引入一个抽象工厂----原料生产工厂,通过抽象工厂接口,创建产品家族,利用这个接口写代码,就从实际工厂解耦,当增加另一个实际工厂,只需要让它实现这个接口。

这就是抽象工厂模式。

工厂方法(Factory Pattern)的更多相关文章

  1. 抽象工厂(Abstract Factory),工厂方法(Factory Method),单例模式(Singleton Pattern)

    在谈工厂之前,先阐述一个观点:那就是在实际程序设计中,为了设计灵活的多态代码,代码中尽量不使用new去实例化一个对象,那么不使用new去实例化对象,剩下可用的方法就可以选择使用工厂方法,原型复制等去实 ...

  2. 设计模式 - 工厂模式(factory pattern) 具体解释

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/u012515223/article/details/27081511 工厂模式(factory pa ...

  3. Spring 通过工厂方法(Factory Method)来配置bean

    Spring 通过工厂方法(Factory Method)来配置bean 在Spring的世界中, 我们通常会利用bean config file 或者 annotation注解方式来配置bean. ...

  4. 工厂模式(factory pattern)

    工厂模式主要用来封装对象的创建,有3种分类:简单工厂(simple factory).工厂方法(factory method).抽象工厂(abstract factory). 简单工厂包括3种组成元素 ...

  5. python 设计模式之工厂模式 Factory Pattern (简单工厂模式,工厂方法模式,抽象工厂模式)

    十一回了趟老家,十一前工作一大堆忙成了狗,十一回来后又积累了一大堆又 忙成了狗,今天刚好抽了一点空开始写工厂方法模式 我看了<Head First 设计模式>P109--P133 这25页 ...

  6. 创建型模式篇(工厂模式Factory Pattern)

    一.工厂模式(Factory Pattern) 1.定义: 在软件系统,经常面临着“某个对象”的创建工作,由于需求的变化,这个对象的具体实现经常面临着剧烈的变化,但是它却拥有比较稳定的接口.提供一种封 ...

  7. [设计模式-创建型]工厂方法(Factory Method)

    概括 名称 Factory Method 结构 动机 定义一个用于创建对象的接口,让子类决定实例化哪一个类.Factory Method 使一个类的实例化延迟到其子类. 适用性 当一个类不知道它所必 ...

  8. 设计模式 笔记 工厂方法 Factory Methon

    //---------------------------15/04/09---------------------------- //factory method 工厂方法-------对象创建型模 ...

  9. 工厂模式(factory pattern) ------创造型模式

    创建型模式 简单工厂模式的缺点是: 当我们引入新产品时,由于静态工厂方法通过所传入参数的不同来创建不同的产品,需要修改工厂类的方法,违反了“开闭原则”. 工厂模式提供了一个抽象工厂接口来声明抽象工厂方 ...

  10. 设计模式——工厂方法(Factory Method)

    定义一个用于创建对象的接口,让子类决定实例化哪一个类.工厂方法使一个类的实例化延迟到其子类. ——DP UML类图 模式说明 抽象业务基类 实际业务类的公共基类,也是工厂要创建的所有对象的父类,这部分 ...

随机推荐

  1. zabbix邮件报警脚本(Python)

    #!/usr/bin/python #coding:utf-8 import smtplib from email.mime.text import MIMEText import sys mail_ ...

  2. 将某个组中的账户移动到新的OU下

    将某个组中的账户移动到新的OU下 #定义组名 $groupname = "testg" #定义新的OU名称 $newou = "OU=oo,OU=Admins,dc=dd ...

  3. PHP str_replace() 函数

    定义和用法 str_replace() 函数使用一个字符串替换字符串中的另一些字符. 语法 str_replace(find,replace,string,count) 参数 描述 find 必需.规 ...

  4. Codeforces Round #325 (Div. 2) A. Alena's Schedule 水题

    A. Alena's Schedule Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/586/pr ...

  5. [Whole Web] [Node.js] Using npm run to launch local scripts

    npm run allows you to configure scripts inside of your package.json file which can access locally in ...

  6. POJ 1329 三角外接圆

    Circle Through Three Points Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 3169   Acce ...

  7. SharePoint 2013+ Sqlserver 2014 Kerberos 配置传奇, 最终的解决方案 验证。

    SharePoint 2013+ Sqlserver 2014 Kerberos 配置传奇. 1,安装数据库,我就不多说安装,客户一定要注意. 我将参照以下实施例和账户. 2,建立DNS,假设没有DN ...

  8. Java连接redis的使用演示样例

    Java连接redis的使用演示样例 Redis是开源的key-value存储工具,redis通经常使用来存储结构化的数据,由于redis的key能够包括String.hash.listset和sor ...

  9. Linux lsof命令使用小结

        lsof(list open files)是一个列出当前系统打开文件的工具.在Linux环境下,任何事物都是以文件的形式存在,通过文件不仅可以访问常规数据,还可以访问网络连接和硬件.所以,如传 ...

  10. unicode 编码总结

    unicode简介: unicode又称为unicode character set,缩写为ucs,意为字符集.编码方式有utf-7,utf-8,utf-16,utf-32几种,常用的是utf-8和u ...