今天来学习23种设计模式的第二种,工厂方法模式,同时也是Java 中最常用的设计模式之一。
概念:
定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中所要求的“创建与使用相分离”的特点。
我们把被创建的对象称为“产品”,把创建产品的对象称为“工厂”。如果要创建的产品不多,只要一个工厂类就可以完成,这种模式叫“简单工厂模式”,它不属于 GOF 的 23 种经典设计模式,它的缺点是增加新产品时会违背“开闭原则”。
本文介绍的“工厂方法模式”是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则。
特点:
- 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。
- 在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则。
工厂方法的主要角色:
- 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法
new Product()
来创建产品。 - 具体工厂(Concrete Factory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
- 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
- 具体产品(Concrete Product):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
类图分析:
核心代码:
- 图形的抽象类
package cn.ppdxzz.factorymethod.graph;
/**
* Description:图形抽象类
*
* @Date: 2020/3/4 17:51
* @Author: PeiChen
*/
public abstract class Graph {
//开始绘制
public abstract void startDraw();
//结束绘制
public abstract void finishDraw();
}
- 图形的具体实现类(这里仅列举小万、小李绘制圆形的实现,重在这种设计思想的领悟)
package cn.ppdxzz.factorymethod.graph;
/**
* Description:小万绘制圆形
*
* @Date: 2020/3/4 18:03
* @Author: PeiChen
*/
public class WanCircle extends Graph {
@Override
public void startDraw() {
System.out.println("小万开始绘制圆形...");
}
@Override
public void finishDraw() {
System.out.println("小万结束绘制圆形...");
System.out.println("-------------------");
}
}
package cn.ppdxzz.factorymethod.graph;
/**
* Description:小李绘制圆形
*
* @Date: 2020/3/4 18:08
* @Author: PeiChen
*/
public class LiCircle extends Graph {
@Override
public void startDraw() {
System.out.println("小李开始绘制圆形...");
}
@Override
public void finishDraw() {
System.out.println("小李结束绘制圆形...");
System.out.println("-------------------");
}
}
- 将绘制图形的实例化功能抽象为抽象方法,在不同的绘制者的子类中具体实现。
package cn.ppdxzz.factorymethod.draw;
import cn.ppdxzz.factorymethod.graph.Graph;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* Description:绘制图形的工厂类
*
* @Date: 2020/3/4 18:14
* @Author: PeiChen
*/
public abstract class DrawGraph {
//定义一个绘图抽象方法,让各个工厂子类自己实现需要绘制什么图形
abstract Graph createGraph(String createType);
//构造方法
public DrawGraph() {
Graph graph = null;
String drawType;
do {
drawType = getType();
//抽象方法,由工厂子类完成图形的绘制
graph = createGraph(drawType);
if (graph != null) {
graph.startDraw();
graph.finishDraw();
}else {
System.out.println("图形输入有误,已退出!");
break;
}
}while (true);
}
//获取绘制者输入绘制的图形形状
private String getType() {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
System.out.println("请输入绘制的图形:");
try {
String str = reader.readLine();
return str;
} catch (IOException e) {
e.printStackTrace();
return "";
}
}
}
- 小万这个绘制者的具体实现类。
package cn.ppdxzz.factorymethod.draw;
import cn.ppdxzz.factorymethod.graph.Graph;
import cn.ppdxzz.factorymethod.graph.WanCircle;
import cn.ppdxzz.factorymethod.graph.WanRectangle;
import cn.ppdxzz.factorymethod.graph.WanTriangle;
/**
* Description:小万绘制的图形
*
* @Date: 2020/3/4 19:28
* @Author: PeiChen
*/
public class DrawWanGraph extends DrawGraph {
@Override
Graph createGraph(String createType) {
Graph graph = null;
if ("circle".equals(createType)) {
graph = new WanCircle();
}else if ("rectangle".equals(createType)) {
graph = new WanRectangle();
}else if ("triangle".equals(createType)){
graph = new WanTriangle();
}
return graph;
}
}
- 小李这个绘制者的具体实现类。
package cn.ppdxzz.factorymethod.draw;
import cn.ppdxzz.factorymethod.graph.Graph;
import cn.ppdxzz.factorymethod.graph.LiCircle;
import cn.ppdxzz.factorymethod.graph.LiRectangle;
/**
* Description:小李绘制的图形
*
* @Date: 2020/3/4 19:20
* @Author: PeiChen
*/
public class DrawLiGraph extends DrawGraph {
@Override
Graph createGraph(String createType) {
Graph graph = null;
if ("circle".equals(createType)) {
graph = new LiCircle();
}else if ("rectangle".equals(createType)){
graph = new LiRectangle();
}
return graph;
}
}
总结:
工厂方法模式非常符合“开闭原则”,当需要增加一个新的产品时,我们只需要增加一个具体的产品类和与之对应的具体工厂即可,无须修改原有系统。同时在工厂方法模式中用户只需要知道生产产品的具体工厂即可,无须关系产品的创建过程,甚至连具体的产品类名称都不需要知道。
虽然他很好的符合了“开闭原则”,但是由于每新增一个新产品时就需要增加两个类,这样势必会导致系统的复杂度增加。
工厂方法模式就讲解到这里,后面我们将会引入另一种设计模式——抽象工厂模式。