《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 ...
随机推荐
- JVM的堆内存泄漏排查-性能测试
JVM异常说明 https://testerhome.com/articles/24259 一文中已介绍了,JVM每个运行时区域--程序计数器 .Java虚拟机栈.本地方法栈.Java堆.方法区.直接 ...
- 入门大数据---基于Zookeeper搭建Kafka高可用集群
一.Zookeeper集群搭建 为保证集群高可用,Zookeeper 集群的节点数最好是奇数,最少有三个节点,所以这里搭建一个三个节点的集群. 1.1 下载 & 解压 下载对应版本 Zooke ...
- app自动化测试环境配置:adb环境配置、monkey环境配置、appium环境配置大全
1. 安装jdk 2. 安装配置Andriod sdk 安装Andriod sdk前首先需要安装配置好jdk环境. 然后安装Android sdk 安装完成后需要配置环境变量:ANDROID_HOME ...
- LeetCode第29场双周赛题解
第一题 用一个新数组newSalary保存去掉最低和最高工资的工资列表,然后遍历newSalary,计算总和,除以元素个数,就得到了平均值. class Solution { public: doub ...
- vscode 配置 c++ 环境
vscode 配置 c++ 环境 参考的这篇bloghttps://blog.csdn.net/bat67/article/details/81268581 1.安装编译器.这里安装 codebloc ...
- PBR(基于物理的渲染)学习笔记
PBR基本介绍 PBR代表基于物理的渲染,本质上还是 gl_FragColor = Emssive + Ambient + Diffuse + Specular 可能高级一些在考虑下AO也就是环境光遮 ...
- git bash中提示 bash:node: command not found
昨天小伙伴私信,git bash以及windows 的cmd命令行下均无法运行node npm. 究其原因是环境变量的问题.解决步骤: 1>在"此电脑"中右击,选择" ...
- DirectX11 With Windows SDK--32 SSAO(屏幕空间环境光遮蔽)
前言 由于性能的限制,实时光照模型往往会忽略间接光因素(即场景中其他物体所反弹的光线).但在现实生活中,大部分光照其实是间接光.在第7章里面的光照方程里面引入了环境光项: \[C_a = \mathb ...
- MySQL 事务 异常 事务隔离的级别
MySQL 事务 异常 事务隔离的级别 事务 在你操作数据库的同时,有可能其他用户还会不断地对数据进行增删改查操作.为了避免并行进行时出现混乱,就产生了"事务".事务就是要保证 ...
- 2020最新的Spring Boot 分布式锁的具体实现(内附代码)
前言 面试总是会被问到有没有用过分布式锁.redis 锁,大部分读者平时很少接触到,所以只能很无奈的回答 "没有".本文通过 Spring Boot 整合 redisson 来实现 ...