工厂方法(Factory Pattern)
工厂方法模式定义:定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。(注:“决定”不是指模式允许子类本身在运行时做决定,而是指在编写创建者类时,不需要知道实际创建的产品是哪一个。选择了使用哪个子类,自然就决定了实际创建的产品是什么)
假设我们要开一间披萨店,提供不同口味的披萨。
首先有一个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)的更多相关文章
- 抽象工厂(Abstract Factory),工厂方法(Factory Method),单例模式(Singleton Pattern)
在谈工厂之前,先阐述一个观点:那就是在实际程序设计中,为了设计灵活的多态代码,代码中尽量不使用new去实例化一个对象,那么不使用new去实例化对象,剩下可用的方法就可以选择使用工厂方法,原型复制等去实 ...
- 设计模式 - 工厂模式(factory pattern) 具体解释
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/u012515223/article/details/27081511 工厂模式(factory pa ...
- Spring 通过工厂方法(Factory Method)来配置bean
Spring 通过工厂方法(Factory Method)来配置bean 在Spring的世界中, 我们通常会利用bean config file 或者 annotation注解方式来配置bean. ...
- 工厂模式(factory pattern)
工厂模式主要用来封装对象的创建,有3种分类:简单工厂(simple factory).工厂方法(factory method).抽象工厂(abstract factory). 简单工厂包括3种组成元素 ...
- python 设计模式之工厂模式 Factory Pattern (简单工厂模式,工厂方法模式,抽象工厂模式)
十一回了趟老家,十一前工作一大堆忙成了狗,十一回来后又积累了一大堆又 忙成了狗,今天刚好抽了一点空开始写工厂方法模式 我看了<Head First 设计模式>P109--P133 这25页 ...
- 创建型模式篇(工厂模式Factory Pattern)
一.工厂模式(Factory Pattern) 1.定义: 在软件系统,经常面临着“某个对象”的创建工作,由于需求的变化,这个对象的具体实现经常面临着剧烈的变化,但是它却拥有比较稳定的接口.提供一种封 ...
- [设计模式-创建型]工厂方法(Factory Method)
概括 名称 Factory Method 结构 动机 定义一个用于创建对象的接口,让子类决定实例化哪一个类.Factory Method 使一个类的实例化延迟到其子类. 适用性 当一个类不知道它所必 ...
- 设计模式 笔记 工厂方法 Factory Methon
//---------------------------15/04/09---------------------------- //factory method 工厂方法-------对象创建型模 ...
- 工厂模式(factory pattern) ------创造型模式
创建型模式 简单工厂模式的缺点是: 当我们引入新产品时,由于静态工厂方法通过所传入参数的不同来创建不同的产品,需要修改工厂类的方法,违反了“开闭原则”. 工厂模式提供了一个抽象工厂接口来声明抽象工厂方 ...
- 设计模式——工厂方法(Factory Method)
定义一个用于创建对象的接口,让子类决定实例化哪一个类.工厂方法使一个类的实例化延迟到其子类. ——DP UML类图 模式说明 抽象业务基类 实际业务类的公共基类,也是工厂要创建的所有对象的父类,这部分 ...
随机推荐
- Oracle DataGuard数据备份方案详解
Oracle DataGuard是一种数据库级别的HA方案,最主要功能是冗灾.数据保护.故障恢复等. 在生产数据库的"事务一致性"时,使用生产库的物理全备份(或物理COPY)创建备 ...
- 博客转移到lyso.me
博客转移到http://lyso.me :)
- 使用Go语言两三事
使用Go语言两三事,在网上看到的总结的很不错哦,转自http://www.cnblogs.com/sevenyuan/archive/2013/02/27/2935887.html 一.channel ...
- 网络子系统55_ip协议分片重组_加入ipq
//ip分片加入到正确的ipq结构 //调用路径:ip_defrag->ip_frag_queue // 处理过程: // 1.正在被释放的ipq,不处理新加入的分片(ipq正在被释放由last ...
- SQLSERVER复制表的方法
1.复制表结构及数据 格式:select * into 新表名 from 要复制的表名 --例如:select * into temp from users 2.只复制表数据 格式 ...
- 递归循环JSON
using System; using System.Collections.Generic; using System.Linq; using System.Web; /// <summary ...
- Xcode中的Info.plist字段列表详解
Info.plist用于向iOS提供关于app,bundle或者framework的一些重要信息.它指定了比如一个应用应该怎样启动,它如何被本地化,应用的名称,要显示的图标,还有更多.Info.pli ...
- careercup-中等难题
17.1 编写一个函数,不用临时变量,直接交换两函数. 解法: 方法一:这个是经典面试题,也相当直接.我们将用a0表示a的初值,b0表示b的初始值,用diff表示a0-b0的值. 让我们将a>b ...
- 深入研究Block捕获外部变量和__block实现原理
Blocks是C语言的扩充功能,而Apple 在OS X Snow Leopard 和 iOS 4中引入了这个新功能“Blocks”.从那开始,Block就出现在iOS和Mac系统各个API中,并被大 ...
- 深入理解Binder(一),从AIDL谈起
打算写一篇Binder介绍的文章,仔细想想一篇文章貌似很难厘清,于是我打算从AIDL入手,本篇先来简单介绍下AIDL的使用,然后在此基础上我们继续来研究Binder的工作原理.小伙伴们都知道,AIDL ...