《Head First 设计模式》:工厂方法模式
正文
一、定义
工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
PS:在设计模式中,“实现一个接口”泛指实现某个超类型(可以是类或接口)的某个方法。
要点:
- 通过子类来创建具体的对象。客户只需要知道他们所使用的抽象类型即可。
- 由子类决定要实例化的类是哪一个,是指在编写创建者类时,不需要知道实际创建的产品是哪一个。选择了使用哪个创建者子类,自然就决定了实际创建的产品是什么。
- 对象统一由定义好的工厂方法来创建。
二、实现步骤
1、创建产品抽象类
/**
* 产品抽象类
*/
public abstract class Product {
String name;
public String getName() {
return name;
}
}
2、创建具体的产品,并继承产品抽象类
(1)产品A1
/**
* 产品A1
*/
public class ConcreteProductA1 extends Product {
public ConcreteProductA1() {
name = "ConcreteProductA1";
}
}
(2)产品A2
/**
* 产品A2
*/
public class ConcreteProductA2 extends Product {
public ConcreteProductA2() {
name = "ConcreteProductA2";
}
}
(3)产品B1
/**
* 产品B1
*/
public class ConcreteProductB1 extends Product {
public ConcreteProductB1() {
name = "ConcreteProductB1";
}
}
(4)产品B2
/**
* 产品B2
*/
public class ConcreteProductB2 extends Product {
public ConcreteProductB2() {
name = "ConcreteProductB2";
}
}
3、创建创建者抽象类,并定义用来创建产品的工厂方法
创建者一般为需要用到产品的类,需要的产品则通过类中的工厂方法创建。
/**
* 创建者抽象类
*/
public abstract class Creator {
/**
* 创建产品(工厂方法)
*/
protected abstract Product createProduct(String productType);
}
4、创建具体的创建者,并继承创建者抽象类
具体的创建者需要实现创建产品的工厂方法。
(1)创建者1
/**
* 创建者1
*/
public class ConcreteCreator1 extends Creator {
@Override
protected Product createProduct(String productType) {
// 由具体的创建者(子类)决定创建哪个类的对象
if ("A".equals(productType)) {
return new ConcreteProductA1();
} else if ("B".equals(productType)) {
return new ConcreteProductB1();
}
return null;
}
}
(2)创建者2
/**
* 创建者2
*/
public class ConcreteCreator2 extends Creator {
@Override
protected Product createProduct(String productType) {
// 由具体的创建者(子类)决定创建哪个类的对象
if ("A".equals(productType)) {
return new ConcreteProductA2();
} else if ("B".equals(productType)) {
return new ConcreteProductB2();
}
return null;
}
}
5、创建者通过工厂方法创建产品
public class Test {
public static void main(String[] args) {
// 创建者1
Creator creator1 = new ConcreteCreator1();
// 创建者2
Creator creator2 = new ConcreteCreator2();
// 通过工厂方法创建产品
Product product = creator1.createProduct("A");
System.out.println("创建者1创建产品A:" + product.getName());
product = creator2.createProduct("A");
System.out.println("创建者2创建产品A:" + product.getName());
}
}
三、举个栗子
1、背景
假设你有一个披萨店,出售多种类型的披萨:芝士披萨、蛤蜊披萨、素食披萨等。由于经营有成,你打算推广自己的加盟店。
为了确保加盟店运营的质量,你希望加盟店能够采用固定的制作流程。但是由于区域的差异,每家加盟店都可能想要提供不同风味的披萨(比如纽约、芝加哥、加州),因此又必须允许加盟店能够自由地制作该区域的风味。
2、实现
披萨店子类通过实现创建披萨方法来决定要创建什么风味的披萨。
(1)创建披萨抽象类
/**
* 披萨抽象类
*/
public abstract class Pizza {
/**
* 名称
*/
String name;
/**
* 面团
*/
String dough;
/**
* 酱料
*/
String sauce;
/**
* 佐料
*/
ArrayList<String> toppings = new ArrayList<>();
void prepare() {
System.out.println("Preparing " + name);
System.out.println("Tossing dough...");
System.out.println("Adding souce...");
System.out.println("Adding toppings: ");
for (int i = 0; i < toppings.size(); i++) {
System.out.println(" "+ toppings.get(i));
}
}
/**
* 烘烤
*/
void bake() {
System.out.println("Bake for 25 minutes at 350");
}
/**
* 切片
*/
void cut() {
System.out.println("Cutting the pizza into diagonal slices");
}
/**
* 装盒
*/
void box() {
System.out.println("Place pizza in official PizzaStore box");
}
public String getName() {
return name;
}
}
(2)创建不同风味、不同类型的披萨
/**
* 纽约风味的芝士披萨
*/
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 NYStyleClamPizza extends Pizza {
public NYStyleClamPizza() {
name = "NY Style Sauce Clam Pizza";
dough = "Thin Crust Dough";
sauce = "Marinara Sauce";
toppings.add("Fresh Clams");
}
}
/**
* 芝加哥风味的芝士披萨
*/
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");
}
void cut() {
System.out.println("Cutting the pizza into square slices");
}
}
/**
* 芝加哥风味的蛤蜊披萨
*/
public class ChicagoStyleClamPizza extends Pizza {
public ChicagoStyleClamPizza() {
name = "Chicago Style Clam Pizza";
dough = "Extra Thick Crust Dough";
sauce = "Plum Tomato Sauce";
toppings.add("Frozen Clams");
}
void cut() {
System.out.println("Cutting the pizza into square slices");
}
}
(3)创建披萨店抽象类
/**
* 披萨店抽象类
*/
public abstract class PizzaStore {
/**
* 订购披萨
*/
public Pizza orderPizza(String type) {
Pizza pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
/**
* 创建披萨(工厂方法)
*/
protected abstract Pizza createPizza(String type);
}
(4)创建不同风味的披萨店
/**
* 纽约风味披萨店
*/
public class NYStylePizzaStore extends
protected Pizza createPizza(String type) {
Pizza pizza = null;
if ("cheese".equals(type)) {
pizza = new NYStyleCheesePizza();
} else if ("clam".equals(type)) {
pizza = new NYStyleClamPizza();
}
return pizza;
}
}
/**
* 芝加哥风味披萨店
*/
public class ChicagoStylePizzaStore extends PizzaStore {
@Override
protected Pizza createPizza(String type) {
Pizza pizza = null;
if ("cheese".equals(type)) {
pizza = new ChicagoStyleCheesePizza();
} else if ("clam".equals(type)) {
pizza = new ChicagoStyleClamPizza();
}
return pizza;
}
}
(5)使用不同风味的披萨店订购披萨
public class Test {
public static void main(String[] args) {
// 纽约风味披萨店
PizzaStore nyStore = new NYStylePizzaStore();
// 芝加哥风味披萨店
PizzaStore chicagoStore = new ChicagoStylePizzaStore();
// 订购芝士披萨
Pizza pizza = nyStore.orderPizza("cheese");
System.out.println("Ethan ordered a " + pizza.getName() + "\n");
pizza = chicagoStore.orderPizza("cheese");
System.out.println("Joel ordered a " + pizza.getName() + "\n");
}
}
《Head First 设计模式》:工厂方法模式的更多相关文章
- 《Android源码设计模式》--Builder模式
No1: 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示 No2: 在Android源码中,最常用到的Builder模式就是AlertDialog.Builder No3: ...
- 《Android源码设计模式》--模板方法模式
No1: 模板方法模式包括:抽象类(其中定义了一系列顺序方法).具体实现类A.具体实现类B 如果子类有实现不一样的细节,重写父类的某个方法即可 No2: AsyncTask对象调用execute方法后 ...
- 《Android源码设计模式》--状态模式--责任链模式--解释器模式--命令模式--观察者模式--备忘录模式--迭代器模式
[状态模式] No1: Wifi设置界面是一个叫做WifiSetting的Fragment实现的 No2: 在不同的状态下对于扫描Wifi这个请求的处理是完全不一样的.在初始状态下扫描请求被直接忽略, ...
- 《Android源码设计模式》--原型模式
No1: 原型模式使用场景: 1)类初始化需要消耗非常多的资源,这个资源包括数据.硬件资源等,通过原型复制避免这些消耗 2)通过new产生一个对象需要非常繁琐的数据准备货访问权限,这是可以使用原型模式 ...
- 《Android源码设计模式》--策略模式
No1: 定义:策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户而独立变化. No2: 使用场景: 1)针对同一类型问题的多种处理方式,仅 ...
- 《Android源码设计模式》学习笔记之ImageLoader
微信公众号:CodingAndroid cnblog:http://www.cnblogs.com/angel88/ CSDN:http://blog.csdn.net/xinpengfei521 需 ...
- 4. 星际争霸之php设计模式--工厂方法模式
题记==============================================================================本php设计模式专辑来源于博客(jymo ...
- C++设计模式——工厂方法模式
本文版权归果冻说所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利.» 本文链接:http://www.jellythink.com/arch ...
- 《Android源码设计模式》--工厂方法模式
No1: 对于一个应用程序来说,其真正的入口是在ActivityThread类中,ActivityThread中含有我们熟悉的main方法.ActivityThread是一个final类,不能被继承. ...
- 《Android源码设计模式》--抽象工厂模式
No1: 4种MediaPlayer Factory分别会生成不同的MediaPlayer基类:StagefrightPlayer.NuPlayerDriver.MidiFile和TestPlayer ...
随机推荐
- [ C++ ] 勿在浮沙筑高台 —— 内存管理(18~31p) std::alloc
部分内容个人感觉不是特别重要,所以没有记录了.其实还是懒 embedded pointers 把对象的前四字节当指针用. struct obj{ struct obj *free_list_link; ...
- Openvas简介
Openvas是Nessus的一个开源分支,用于管理目标系统的漏洞. Openvas初始化:openvas-setup,会自动进行初始化配置.Openvas工作原理图如下: OpenVASManage ...
- Jmeter系列(35)- 使用 ServerAgent 监控服务器
如果你想从头学习Jmeter,可以看看这个系列的文章哦 https://www.cnblogs.com/poloyy/category/1746599.html 前言 做性能测试,监控服务器资源指标是 ...
- js语法基础入门(7)
7.数组 7.1.什么是数组以及相关概念? 什么是数组?是一组数据有序排列的集合.将一组数据按一定顺序组织为一个组合,并对这个组合命名,这样便构成了数组. 什么是数组元素?组成数组的每一个数据称为数组 ...
- 用JQuery解析获取JSON数据
JSON 是一种比较方便的数据形式,下面使用$.getJSON方法,实现获得JSON数据和解析,都挺方便简单的.从http://api.flickr.com/services/feeds/photos ...
- 博弈论Nim取子问题,困扰千年的问题一行代码解决
本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是算法与数据结构专题26篇文章,我们来看看一个新的博弈论模型--Nim取子问题. 这个博弈问题非常古老,延续长度千年之久,一直到20世纪 ...
- JavaScript基础尽量少使用全局变量(001)
Pattern意思是模式,好的编码习惯经过多次实践的应用就会形成模式,而反模式(Anti-Pattern)则是不好的编码习惯.了解 JavaScript模式之前,先来看看Anti-pattern的例子 ...
- 请写出在ASP.NET中常用的几种页面间传值的方法,并说出它们的特点。
QueryString 传递一个或多个安全性要求不高或是结构简单的数值.但是对于传递数组或对象的话,就不能用这个方法了 session(viewstate) 简单,但易丢失 作用于用户个人,过量的存储 ...
- 设置CMD窗口为UTF-8编码
Windows下的CMD窗口默认是采用非UTF-8编码的,有时候运行一些UTF-8编写的批处理文件在控制台中的输出就是乱码, CHCP是MD DOS中的命令,用来显示或设置活动代码页编号的.用法是: ...
- Centos7-Docker1.12开启守护进程(远程调用)
本文讲述了Docker1.12.6在Linux下开启守护进程(远程调用),理论上来说其他版本也是一样的改法,博主参考很多都是巨坑,综合自己实战分享给大家,如有错误请留言; - 修改配置 1.修改 do ...