正文

一、定义

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

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. 如何用 React 构建前端架构

    早期的前端是由后端开发的,最开始的时候仅仅做展示,点一下链接跳转到另外一个页面去,渲染表单,再用Ajax的方式请求网络和后端交互,数据返回来还需要把数据渲染到DOM上.写这样的代码的确是很简单.在We ...

  2. shell 脚本操作informix数据库

    shell 脚本操作informix数据库的简单模板: functionName(){ dbaccess << ! database 库名; sql语句; ! } 栗子1:更新数据 fun ...

  3. Centos7 GRE Tunnel

    一.关闭防火墙及selinux 二.CentOS7默认不加载gre内核模块,加载gre内核模块 # modprobe ip_gre   临时加载gre模块(重启后失效) # lsmod |grep g ...

  4. NLP(一)

    “自然语言处理”(Natural Language Processing 简称 NLP)包含所有用计算机对自然语言进行的操作. 自然语言工具包(NLTK) 语言处理任务与相应 NLTK 模块以及功能描 ...

  5. 一张图片在Python操作下的4种玩法(附源码)

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:黄伟呢 1.利用python绘制一个小猪佩奇 turtle库是一个很 ...

  6. css input checkbox复选框控件 样式美化的多种方案

    checkbox复选框可能是网站中常用的html元素,但大多数人并不满意它的默认样式,这篇文章就讲讲如何实现input checkbox复选框控件 样式美化效果. 资源网站大全 https://55w ...

  7. iframe 透明兼容,设置iframe透明背景的方法

    从IE5.5+就支持iframe框架的背景透明.通过使用allowtransparency和background-color来设置iframe框架的透明效果,代码如下: <iframe src= ...

  8. Mysql中Union和OR性能对比

    博客已搬家,更多内容查看https://liangyongrui.github.io/ Mysql中Union和OR性能对比 在leetcode上看到一篇文章,整理一下 参考:https://leet ...

  9. 02 . SaltStack高级用法(Python API)

    Python API简单使用 第一条命令 /usr/bin/salt默认使用的接口是LocalClient,该接口只能在salt master上使用 >>> import salt. ...

  10. Mysql 常用语句实战(2)

    前置 sql 语句 用来创建表.插入数据 SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- ...