正文

一、定义

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

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 设计模式》:工厂方法模式的更多相关文章

  1. 《Android源码设计模式》--Builder模式

    No1: 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示 No2: 在Android源码中,最常用到的Builder模式就是AlertDialog.Builder No3: ...

  2. 《Android源码设计模式》--模板方法模式

    No1: 模板方法模式包括:抽象类(其中定义了一系列顺序方法).具体实现类A.具体实现类B 如果子类有实现不一样的细节,重写父类的某个方法即可 No2: AsyncTask对象调用execute方法后 ...

  3. 《Android源码设计模式》--状态模式--责任链模式--解释器模式--命令模式--观察者模式--备忘录模式--迭代器模式

    [状态模式] No1: Wifi设置界面是一个叫做WifiSetting的Fragment实现的 No2: 在不同的状态下对于扫描Wifi这个请求的处理是完全不一样的.在初始状态下扫描请求被直接忽略, ...

  4. 《Android源码设计模式》--原型模式

    No1: 原型模式使用场景: 1)类初始化需要消耗非常多的资源,这个资源包括数据.硬件资源等,通过原型复制避免这些消耗 2)通过new产生一个对象需要非常繁琐的数据准备货访问权限,这是可以使用原型模式 ...

  5. 《Android源码设计模式》--策略模式

    No1: 定义:策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户而独立变化. No2: 使用场景: 1)针对同一类型问题的多种处理方式,仅 ...

  6. 《Android源码设计模式》学习笔记之ImageLoader

    微信公众号:CodingAndroid cnblog:http://www.cnblogs.com/angel88/ CSDN:http://blog.csdn.net/xinpengfei521 需 ...

  7. 4. 星际争霸之php设计模式--工厂方法模式

    题记==============================================================================本php设计模式专辑来源于博客(jymo ...

  8. C++设计模式——工厂方法模式

    本文版权归果冻说所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利.» 本文链接:http://www.jellythink.com/arch ...

  9. 《Android源码设计模式》--工厂方法模式

    No1: 对于一个应用程序来说,其真正的入口是在ActivityThread类中,ActivityThread中含有我们熟悉的main方法.ActivityThread是一个final类,不能被继承. ...

  10. 《Android源码设计模式》--抽象工厂模式

    No1: 4种MediaPlayer Factory分别会生成不同的MediaPlayer基类:StagefrightPlayer.NuPlayerDriver.MidiFile和TestPlayer ...

随机推荐

  1. php计算两个时间段内的 工作日 工作小时

    <?php class WorkTime { // 定义工作日 [1, 2, 3, 4, 5, 6, 0] public $week_workingday = [1, 2, 3, 4, 5]; ...

  2. SDL软件安全读书笔记(一)

    # 如何应对当前的全球网络安全威胁? 开发安全漏洞尽可能少的软件,应该着眼于源头安全. 边界安全盒深度防御是重要的安全手段,但软件自身的安全是安全防护的第一关. 即使软件源头存在较少的漏洞,这些漏洞也 ...

  3. maven在windows10系统下安装配置和打包war

    maven下载地址:http://maven.apache.org/ 下载完成解压到 D盘 目录下D:\apache-maven-3.5.0\bin 配置maven环境变量: M2_HOME   D: ...

  4. MongoDB副本集replica set(三)--添加删除成员

    在上一篇文章中,我们搭建了3个节点的副本集,集群信息如下: rstest:PRIMARY> rs.config() { "_id" : "rstest", ...

  5. 传统声学模型之HMM和GMM

    声学模型是指给定声学符号(音素)的情况下对音频特征建立的模型. 数学表达 用 \(X\) 表示音频特征向量 (观察向量),用 \(S\) 表示音素 (隐藏/内部状态),声学模型表示为 \(P(X|S) ...

  6. 数据解析_bs进行数据解析

    1.bs4进行数据解析 数据解析的原理 1.标签定位 2.提取标签,标签属性中存储的数据值 bs4数据解析的原理 1.实例化一个BeautifulSoup对象,并且将页面源码数据加载到该对象中 2.通 ...

  7. c++随机生成树

    分析 当我们写完一道题,自认为它是正解,但是交上去却WA的时候,我们该怎么办呢 当我们已经想出了一道的暴力解法,又想出了一种比较优秀的解法,但不知道这种解法对错与否,我们该怎么办呢 答案显然是对拍 对 ...

  8. 11.unity3d 摄像机快速定位到Scene视角

    选中Camera,比如Main Camera摄像机,在菜单选择GameObject->Align With View就可以了.如下图所示,参照前三步操作,第4步是最终效果.

  9. TKCTF-学校内部的校赛

    *Reverse easy_C easy_re1.exe 在网络百度到解决逆向需要用到软件IDA 然后用IDA打开一条条的找我找到了一条很怪的ZmxhZ3s1ZWU1ZjYyOC1mMzVhLTQxN ...

  10. 执行ArrayList的remove(object)方法抛异常?

    简介 或许有很多小伙伴都尝试过如下的代码: ArrayList<Object> list = ...; for (Object object : list) { if (条件成立) { l ...