设计模式——工厂模式
本篇博文通过学习尚硅谷设计模式课程所做的总结,在此表示感谢!
文章目录
这篇文章通过一个披萨订购的问题来分别引出:简单工厂模式、工厂方法模式、抽象工厂模式;
看一个项目需求:
- 有一个披萨的项目:要便于披萨种类的扩展,要便于维护,
- 披萨的种类很多(比如 GreekPizz、CheesePizz 等),后续有可能还会增加;
- 披萨的制作有 prepare,bake, cut, box
- 完成披萨店订购功能。
简单工厂模式
基本介绍:
- 简单工厂模式是属于创建型模式,是工厂模式的一种,简单工厂模式是由一个工厂对象决定建造出哪一种产品类的实例,简单工厂模式是工厂模式家族中最简单实用的模式;
- 简单工厂模式:定义了一个创建对象的类,由这个类来封装实例化对象的行为(代码);
- 在软件开发中,当我们会用到大量的创建某种、某类或者某批对象时,就会使用到工厂模式;
使用该模式来解决上述的问题。
UML
编码实现
制作披萨类的构建
我们先创建一个制作披萨的基类Pizza
,该类抽取出制作披萨的共性方法,然后让所有披萨都去继承它,如下:
Pizza
代码:
package edu.hebeu.simplefactory.pizza;
public abstract class Pizza {
private String name; // 这个属性用来接收披萨的名字
public abstract void prepare(); // 该方法是按照披萨的名字准备该披萨的原材料
public void bake() {
System.out.println(name + ":烘烤中");
}
public void cut() {
System.out.println(name + ":切分中");
}
public void box() {
System.out.println(name + "打包中...,制作完成!");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
制作各种类型的披萨类创建,如下:
package edu.hebeu.simplefactory.pizza;
public class CheessPizza extends Pizza {
@Override
public void prepare() {
System.out.println("正在准备奶酪披萨的原材料...");
}
}
package edu.hebeu.simplefactory.pizza;
public class ChinaPizza extends Pizza { // 增加一个披萨种类
@Override
public void prepare() {
System.out.println("正在准备中式披萨的原材料...");
}
}
package edu.hebeu.simplefactory.pizza;
public class GreekPizza extends Pizza{
@Override
public void prepare() {
System.out.println("正在准备希腊披萨的原材料...");
}
}
简单工厂类的创建
简单工厂生产披萨(产品),有两种方式,一是采用其实例方法来生产,二是采用其静态方法来生产,这两种方式基本一样,只是前者需要通过工厂实例来生产,后者直接可以使用类来生产,这里我们将这两种方式都列出,如下:
package edu.hebeu.simplefactory.order;
import edu.hebeu.simplefactory.pizza.CheessPizza;
import edu.hebeu.simplefactory.pizza.ChinaPizza;
import edu.hebeu.simplefactory.pizza.GreekPizza;
import edu.hebeu.simplefactory.pizza.Pizza;
/**
* 简单工厂
* @author 13651
*
*/
public class PizzaSimpleFactory {
/**
* 该方法根据披萨名创建相应的披萨,但是该方法因为是实例方法,因此需要通过工厂实例来调用该方法
* @param name
* @return
*/
public Pizza createPizza(String name) {
Pizza pizza = null;
if("奶酪披萨".equals(name)) {
pizza = new CheessPizza();
pizza.setName("奶酪披萨");
} else if("希腊披萨".equals(name)) {
pizza = new GreekPizza();
pizza.setName("希腊披萨");
} else if("中式披萨".equals(name)) {
pizza = new ChinaPizza();
pizza.setName("中式披萨");
}
return pizza;
}
/**
* 该方法根据披萨名创建相应的披萨,对上述的方法进行改进(声明为静态的),所以可以直接使用该工厂类来调用该方法
* @param name
* @return
*/
public static Pizza createPizza2(String name) {
Pizza pizza = null;
if("奶酪披萨".equals(name)) {
pizza = new CheessPizza();
pizza.setName("奶酪披萨");
} else if("希腊披萨".equals(name)) {
pizza = new GreekPizza();
pizza.setName("希腊披萨");
} else if("中式披萨".equals(name)) {
pizza = new ChinaPizza();
pizza.setName("中式披萨");
}
return pizza;
}
}
针对createPizza()方法
(实例方法生产),我们通过如下的类使用该方法:
package edu.hebeu.simplefactory.order;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import edu.hebeu.simplefactory.pizza.Pizza;
/**
* 通过简单工厂的实例方法来定制披萨(生产产品),此时需要创建工厂对象
* @author 13651
*
*/
public class OrderPizza1 {
private PizzaSimpleFactory pizzaFactory = null; // 定义一个简单工厂对象
private Pizza pizza = null;
/**
* 进行披萨的定制,调用工厂类的实例方法来生产
* @param factory
*/
public OrderPizza1(PizzaSimpleFactory factory) {
String name = null;
setFactory(factory);
do {
name = getName();
if("exit".equals(name)) {
System.out.println("bye~~~");
return;
}
pizza = this.pizzaFactory.createPizza(name);
if(pizza == null) {
System.err.println("~~~不知道您要的是什么披萨,请重新选择~~~");
continue;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} while(true);
}
public void setFactory(PizzaSimpleFactory factory) {
this.pizzaFactory = factory;
}
private String getName() { // 这个方法用来接收用户输入的披萨名
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("请输入购买的披萨名:");
try {
return br.readLine();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
}
针对createPizza()方法
(静态方法)来生产,我们通过如下类:
package edu.hebeu.simplefactory.order;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import edu.hebeu.simplefactory.pizza.Pizza;
/*
* 使用静态的createPizza2()方法来订购披萨,不需要工厂对象,直接调用,所以就不需要传入创建工厂类实例
*
*/
public class OrderPizza2 {
private Pizza pizza = null;
/**
* 这个方法用来订购披萨,通过工厂类的静态方法
*/
public OrderPizza2() {
String name = null;
do {
name = getName();
if("exit".equals(name)) {
System.out.println("bye~~~");
return;
}
pizza = PizzaSimpleFactory.createPizza2(name);
if(pizza == null) {
System.err.println("~~~不知道您要的是什么披萨,请重新选择~~~");
continue;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} while(true);
}
/**
* 这个方法用来接收用户输入的披萨名
* @return
*/
private String getName() {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("请输入购买的披萨名:");
try {
return br.readLine();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
}
测试类PizzaStore
的编写,我们直接来测试静态方式创建披萨(上面的OrderPizza2'类
),如下:
package edu.hebeu.simplefactory.order;
/**
*
* 从这个例子 通过简单工厂模式 来实现披萨的购买(相当于一个客户端,完成披萨的订购任务)
*
* 如果此时项目出现新的需求;客户在点披萨时,可以先选择地点,然后在该地点选择披萨,如:北京的奶酪披萨、
* 北京的希腊披萨、伦敦的奶酪披萨、伦敦的希腊披萨等;
*
* 思路:如果此时继续使用这种 简单工厂模式,就需要创建不同的简单工厂类,如:BJPizzaSimpleFactory类、
* LDPizzaSimpleFactory类等,但是这样子会导致工厂类过多,对项目的扩展性、维护性不是特别好!
* 此时我们可以使用工厂方法模式去实现该需求,如包methodfactory所示
*
* @author 13651
*
*/
public class PizzaStore {
public static void main(String[] args) {
// System.out.println("***使用简单工厂模式***");
// new OrderPizza1(new PizzaSimpleFactory());
System.out.println("***使用简单工厂模式的第二种写法(静态工厂模式)***");
new OrderPizza2();
}
}
测试
工厂方法模式
问题引入:我们以上面刚刚实现的例子为基准,如果此时项目出现新的需求;客户在点披萨时,可以先选择地点,然后在该地点选择披萨,如:北京的奶酪披萨、北京的希腊披萨、伦敦的奶酪披萨、伦敦的希腊披萨等;如果此时继续使用这种 简单工厂模式,就需要创建不同的简单工厂类,如:BJPizzaSimpleFactory类、LDPizzaSimpleFactory类等,但是这样子会导致工厂类过多,对项目的扩展性、维护性不是特别好!此时我们可以使用工厂方法模式去实现该需求;
基本介绍:定义了一个创建对象的抽象方法,由子类决定要实例化的类,工厂方法模式将对象的实例化推迟到子类;
UML
编码实现
同样我们先来创建披萨类,和上面的流程一样,创建如下的几个披萨类:
Pizza
package edu.hebeu.methodfactory.pizza;
public abstract class Pizza {
private String name; // 这个属性用来接收披萨的名字
public abstract void prepare(); // 该方法是按照披萨的名字准备该披萨的原材料
public void bake() {
System.out.println(name + ":烘烤中");
}
public void cut() {
System.out.println(name + ":切分中");
}
public void box() {
System.out.println(name + "打包中...,制作完成!");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
BJCheessPizza
package edu.hebeu.methodfactory.pizza;
public class BJCheessPizza extends Pizza {
@Override
public void prepare() {
System.out.println("正在准备北京的奶酪披萨的原材料...");
}
}
BJGreekPizza
package edu.hebeu.methodfactory.pizza;
public class BJGreekPizza extends Pizza{
@Override
public void prepare() {
System.out.println("正在准备北京的希腊披萨的原材料...");
}
}
LDCheessPizza
package edu.hebeu.methodfactory.pizza;
public class LDCheessPizza extends Pizza {
@Override
public void prepare() {
System.out.println("正在准备伦敦的奶酪披萨的原材料...");
}
}
LDGreekPizza
package edu.hebeu.methodfactory.pizza;
public class LDGreekPizza extends Pizza {
@Override
public void prepare() {
System.out.println("正在准备伦敦的希腊披萨的原材料...");
}
}
工厂方法模式的核心
这里就和上面的不太一样了,我们前面所说:定义了一个创建对象的抽象方法,由子类决定要实例化的类,工厂方法模式将对象的实例化推迟到子类;
,那么此时我们应该创建订购披萨的抽象类OrderPizza
,将创建披萨的方法,交给子类的具体方法去实现即可,如下:
OrderPizza
package edu.hebeu.methodfactory.order;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import edu.hebeu.methodfactory.pizza.Pizza;
public abstract class OrderPizza {
public OrderPizza() {
String name = null;
Pizza pizza = null;
System.out.println("开始制作披萨");
do {
name = getName();
if("exit".equals(name)) {
System.out.println("bye~~~");
return;
}
pizza = this.createPizza(name);
if(pizza == null) {
System.err.println("~~~不知道您要的是什么披萨,交易结束~~~");
continue;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} while(true);
}
public abstract Pizza createPizza(String name); // 创建披萨方法抽象成抽象方法,具体细节交由具体的子类实现
private String getName() { // 这个方法用来接收用户输入的披萨名
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("请输入购买的披萨名:");
try {
return br.readLine();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
}
BJOrderPizza
package edu.hebeu.methodfactory.order;
import edu.hebeu.methodfactory.pizza.Pizza;
import edu.hebeu.methodfactory.pizza.BJCheessPizza;
import edu.hebeu.methodfactory.pizza.BJGreekPizza;
public class BJOrederPizza extends OrderPizza {
@Override
public Pizza createPizza(String name) {
Pizza pizza = null;
if("奶酪披萨".equals(name)) {
pizza = new BJCheessPizza();
pizza.setName("北京奶酪披萨");
} else if("希腊披萨".equals(name)) {
pizza = new BJGreekPizza();
pizza.setName("北京希腊披萨");
}
return pizza;
}
}
LDOrderPizza
package edu.hebeu.methodfactory.order;
import edu.hebeu.methodfactory.pizza.LDCheessPizza;
import edu.hebeu.methodfactory.pizza.LDGreekPizza;
import edu.hebeu.methodfactory.pizza.Pizza;
public class LDOrderPizza extends OrderPizza {
@Override
public Pizza createPizza(String name) {
Pizza pizza = null;
if("奶酪披萨".equals(name)) {
pizza = new LDCheessPizza();
pizza.setName("伦敦奶酪披萨");
} else if("希腊披萨".equals(name)) {
pizza = new LDGreekPizza();
pizza.setName("伦敦希腊披萨");
}
return pizza;
}
}
测试类PizzaStore的编写
package edu.hebeu.methodfactory.order;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* 这个例子 通过工厂方法模式 来实现披萨的购买
*
* 设计方案:将披萨项目的实例化功能抽象成抽象方法,在不同的口味点餐子类中具体实现;
*
* @author 13651
*
*/
public class PizzaStore {
public static void main(String[] args) {
System.out.println("***工厂方法模式实现***");
String location = null;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
while(true) {
System.out.println("请输入购买的哪个地方的披萨:");
try {
location = br.readLine();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if("北京".equals(location)) {
new BJOrederPizza();
} else if("伦敦".contentEquals(location)) {
new LDOrderPizza();
} else if("exit".equals(location)){
break;
} else {
System.err.println("没有找到此地方~~~");
}
}
}
}
测试
抽象工厂模式
问题引入:我们如何将上述的工厂方法模式改写为抽象工厂模式?
抽象工厂模式的小结:
- 抽象工厂模式,定义了一个interface用于创建相关或有依赖关系的对象蔟,而无需指明具体类;
- 抽象工厂模式可以将简单工厂模式和工厂方法模式进行整合;
- 从设计层面看,抽象工厂模式就是对简单工厂模式的改进(或者称为进一步抽象);
- 将工厂抽象为两层,AbsFactory(抽象工厂)和具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。这样就将单个的简单工厂变成了工厂蔟,更利于代码的维护和扩展;
UML
编码实现
和上面一样,我们先创建下面的披萨类,如下:
package edu.hebeu.interfacefactory.pizza;
public abstract class Pizza {
private String name; // 这个属性用来接收披萨的名字
public abstract void prepare(); // 该方法是按照披萨的名字准备该披萨的原材料
public void bake() {
System.out.println(name + ":烘烤中");
}
public void cut() {
System.out.println(name + ":切分中");
}
public void box() {
System.out.println(name + "打包中...,制作完成!");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package edu.hebeu.interfacefactory.pizza;
public class BJCheessPizza extends Pizza {
@Override
public void prepare() {
System.out.println("正在准备北京的奶酪披萨的原材料...");
}
}
package edu.hebeu.interfacefactory.pizza;
public class BJGreekPizza extends Pizza{
@Override
public void prepare() {
System.out.println("正在准备北京的希腊披萨的原材料...");
}
}
package edu.hebeu.interfacefactory.pizza;
public class LDCheessPizza extends Pizza {
@Override
public void prepare() {
System.out.println("正在准备伦敦的奶酪披萨的原材料...");
}
}
package edu.hebeu.interfacefactory.pizza;
public class LDGreekPizza extends Pizza {
@Override
public void prepare() {
System.out.println("正在准备伦敦的希腊披萨的原材料...");
}
}
抽象工厂模式的核心
抽象工厂类AbsFactory
package edu.hebeu.interfacefactory.factory;
import edu.hebeu.interfacefactory.pizza.Pizza;
public interface AbsFactory {
Pizza createPizza(String name);
}
子工厂类的创建
package edu.hebeu.interfacefactory.factory;
import edu.hebeu.interfacefactory.pizza.Pizza;
import edu.hebeu.interfacefactory.pizza.BJCheessPizza;
import edu.hebeu.interfacefactory.pizza.BJGreekPizza;
public class BJFactory implements AbsFactory { // 一个子类工厂
@Override
public Pizza createPizza(String name) {
Pizza pizza = null;
if("奶酪披萨".equals(name)) {
pizza = new BJCheessPizza();
pizza.setName("北京奶酪披萨");
} else if("希腊披萨".equals(name)) {
pizza = new BJGreekPizza();
pizza.setName("北京希腊披萨");
}
return pizza;
}
}
package edu.hebeu.interfacefactory.factory;
import edu.hebeu.interfacefactory.pizza.Pizza;
import edu.hebeu.interfacefactory.pizza.LDCheessPizza;
import edu.hebeu.interfacefactory.pizza.LDGreekPizza;
public class LDFactory implements AbsFactory { // 一个子类工厂
@Override
public Pizza createPizza(String name) {
Pizza pizza = null;
if("奶酪披萨".equals(name)) {
pizza = new LDCheessPizza();
pizza.setName("伦敦奶酪披萨");
} else if("希腊披萨".equals(name)) {
pizza = new LDGreekPizza();
pizza.setName("伦敦希腊披萨");
}
return pizza;
}
}
创建两个销售的站点OrderPizza1
和OrderPizza2
,如下:
package edu.hebeu.interfacefactory.order;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import edu.hebeu.interfacefactory.factory.AbsFactory;
import edu.hebeu.interfacefactory.pizza.Pizza;
public class OrderPizza1 {
public OrderPizza1(AbsFactory factory) {
String name = null;
Pizza pizza = null;
do {
name = getName();
if("exit".equals(name)) {
System.out.println("bye~~~");
return;
}
pizza = factory.createPizza(name);
if(pizza == null) {
System.err.println("~~~不知道您要的是什么披萨,请重新选择~~~");
continue;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} while(true);
}
private String getName() { // 这个方法用来接收用户输入的披萨名
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("一号店:请输入购买的披萨名:");
try {
return br.readLine();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
}
package edu.hebeu.interfacefactory.order;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import edu.hebeu.interfacefactory.factory.AbsFactory;
import edu.hebeu.interfacefactory.pizza.Pizza;
public class OrderPizza2 {
public OrderPizza2(AbsFactory factory) {
String name = null;
Pizza pizza = null;
do {
name = getName();
if("exit".equals(name)) {
System.out.println("bye~~~");
return;
}
pizza = factory.createPizza(name);
if(pizza == null) {
System.out.println("~~~不知道您要的是什么披萨,请重新选择~~~");
continue;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} while(true);
}
private String getName() { // 这个方法用来接收用户输入的披萨名
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("二号店:请输入购买的披萨名:");
try {
return br.readLine();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
}
编写测试类PizzaStore
,如下:
package edu.hebeu.interfacefactory.order;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Random;
import edu.hebeu.interfacefactory.factory.BJFactory;
import edu.hebeu.interfacefactory.factory.LDFactory;
public class PizzaStore {
public static void main(String[] args) {
System.out.println("***抽象工厂模式实现***");
String location = null;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
while(true) {
System.out.println("请输入购买的哪个地方的披萨:");
try {
location = br.readLine();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Random random = new Random();
boolean oneORTwo = random.nextBoolean(); // 决定本次在那个店(OrderPizza1或OrderPizza2)购买
if("北京".equals(location)) {
if(oneORTwo) {
new OrderPizza1(new BJFactory());
} else {
new OrderPizza2(new BJFactory());
}
} else if("伦敦".contentEquals(location)) {
if(!oneORTwo) {
new OrderPizza1(new LDFactory());
} else {
new OrderPizza2(new LDFactory());
}
} else if("exit".equals(location)) {
System.out.println("bye~~~");
break;
} else {
System.err.println("没有找到此地方~~~");
}
}
}
}
测试
工厂模式的小结
- 工厂模式的意义是将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和主项目的依赖关系的解耦。从而提高项目的扩展和维护性。
- 三种工厂模式 :简单工厂模式、工厂方法模式、抽象工厂模式;
设计模式的依赖抽象原则:
- 创建对象实例时,不要直接 new 类, 而是把这个 new 类的动作放在一个工厂的方法中,并返回。有的书上说,变量不要直接持有具体类的引用;
- 不要让类继承具体类,而是继承抽象类或者是实现 interface(接口);
- 不要覆盖基类中已经实现的方法;