一. 简单工厂模式介绍
0.介绍
现实生活中原始社会自给自足(没有工厂),农耕社会小作坊(简单工厂,明见酒坊),工业革命流水线(工厂方法,自产自销),现代产业链代工厂(抽象工厂,富士康),我们的项目同样是由简到繁一步步迭代而来的,但对调用者来说,却越来越简单;
在日常开发中,凡是需要生成复杂对象的地方,都可以尝试考虑使用工厂模式来代替。
注意:上述复杂对象指的是类的构造函数参数过多等对类的构造有影响的情况,因为类的构造过于复杂,如果直接在其他业务类内使用,则两者的耦合过重,后续业务更改,就需要在任何引用该类的源代码内进行更改,光是查找所有依赖就很消耗时间了,更别说要一个一个修改了。
工厂模式(Factory Pattern):
-
定义了一个创建产品对象的工厂接口,将产品对象实际创建工作推迟到具体子工厂类当中,
-
这满足创建型模式中所要求的“创建与使用相分离”的特点,
-
按实际业务场景划分,工厂模式有3种不同的实现方式,分别是简单工厂模式、工厂方法模式和抽象工厂模式。
简单工厂模式(SimpleFactory pattern)
是由一个工厂对象 决定创建出哪一种产品类的实例,简单工厂模式是工厂模式家族中最简单实用的模式;
在简单工厂模式(Simple Factory Pattern)中创建实例的方法通常为静态(static)方法,因此又叫静态工厂方法模式
但该模式每增加一个产品就要增加一个具体产品类和对应的具体工厂类,这增加了系统复杂度,违背了“开闭原则”故它不在23种设计模式之列;
1.优点
-
把对象的创建和使用分开,创建对象实例时,不直接new类,而是把这个new类的操作放在一个工厂的方法中,并返回产品对象;
-
将生产过程集中后,便于集中管理(增删改)
-
当产品类在有变动时,使用者不需要去修改代码;
2.缺点:
-
违背单一职能原则,该模式的工厂类单一,其负责所有产品的创建,代码臃肿,职责过重,一旦异常,整个系统将受影响。
-
使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解难度
-
不符合开闭原则,系统扩展困难,一旦增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂问题。
-
简单工厂模式使用了 static 工厂方法,造成工厂角色无法形成基于继承的等级结构。
3.简单工厂模式的结构
结构图如下所示:
简单工厂模式角色分析:
SimpleFactory(简单工厂):(核心)负责创建所有实例的内部逻辑,工厂类创建产品类的方法可被外界直接调用创建需要的产品对象;
Product(抽象产品): 是简单工厂创建的所有对象的父类,负责描述所有实例共有的接口;在Java中由接口或抽象类实现;
ConcreteProduct(具体产品): 简单工厂类创建的对象就是此角色的实例,在Java中由具体类实现;
4.模式实例--Pizza工厂
需求: 披萨的种类很多(比如 GreekPizza、CheesePizza 等)
披萨的制作有 prepare(准备材料工作),bake(烘烤工作), cut(切片工作), box(包装工作),完成披萨店订购功能。
1.传统思路代码
披萨类 Pizza
//将披萨类定义为抽象类;
public abstract class Pizza {
//披萨的种类名;
protected 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 void setName(String name) {
this.name = name;
}
}
奶酪披萨类 CheesePizza
//奶酪披萨类;
public class CheesePizza extends Pizza{
@Override
public void prepare() {
setName("奶酪披萨");
System.out.println("奶酪披萨准备材料工作");
}
}
希腊披萨类 GreekPizza
public class GreekPizza extends Pizza{
@Override
public void prepare() {
setName("希腊披萨");
System.out.println("希腊披萨准备工作");
}
}
订购披萨类 ToOrderPizza
//订购披萨类;
public class ToOrderPizza {
//初始化时执行构造方法;
public ToOrderPizza() {
Pizza pizza = null;
//订购的种类;
String orderType;
do {
//动态获取订购披萨的种类;
orderType=gettype();
if(orderType.equals("CheesePizza")){
pizza=new CheesePizza();
}else if(orderType.equals("GreekPizza")){
pizza=new GreekPizza();
}else {
break;
}
//进行披萨的制作;
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} while (true);
}
//输入台获取披萨的种类;
private String gettype() {
try {
BufferedReader strs = new BufferedReader(new InputStreamReader(System.in));
System.out.println("输入订购的披萨种类:");
String str = strs.readLine();
return str;
} catch (IOException e) {
e.printStackTrace();
return "";
}
}
}
披萨店 PizzaShop
//披萨店类;
public class PizzaShop {
public static void main(String[] args) {
//订购披萨,完成工作;
new ToOrderPizza();
}
}
输出结果
输入订购的披萨种类:
CheesePizza
奶酪披萨准备材料工作
奶酪披萨 烘焙中...;
奶酪披萨 切片中.....;
奶酪披萨 打包....;
输入订购的披萨种类:
GreekPizza
希腊披萨准备工作
希腊披萨 烘焙中...;
希腊披萨 切片中.....;
希腊披萨 打包....;
需要注意的是,如果需要增加另外的披萨种类时,就要去创建新的种类;然后在订购披萨时添加新的依赖;
这样的做法会导致依赖关系复杂;违反了设计模式的OCP(开闭原则);
2.引入简单工厂模式进行优化
引入一个简单工厂类将创建着各种披萨实例对象进行封装,
所有的订购披萨类都在这个简单工厂类中创建对象,保证了扩展新种类披萨后,只需要在简单工厂类中修改即可;
UML图如下:
相关代码如下:
//将Pizza 类做出抽象类
public abstract class Pizza {
protected String name;//名字
//准备原材料,不同的披萨原料不一样,因此,声明成抽象方法;
public abstract void prepare();
//烘焙
public void bake() {
System.out.println(name + "baking");
}
//切割
public void cut() {
System.out.println(name + "cutting");
}
//打包
public void box() {
System.out.println(name + "boxing");
}
//设置pizza名字
public void setName(String name) {
this.name = name;
}
}
public class CheesePizza extends Pizza{
@Override
public void prepare() {
System.out.println("给制作奶酪披萨 准备原材料");
}
}
public class GreekPizza extends Pizza{
@Override
public void prepare() {
System.out.println("给 希腊披萨 准备原材料");
}
}
public class PepperPizza extends Pizza{
@Override
public void prepare() {
System.out.println("给制作胡椒披萨 准备原材料");
}
}
//使用简单工厂将创建披萨对象封装起来, 静态工厂模式
public class SimpleFactor {
//根据orderType 返回对应的Pizza 对象
public static Pizza createPizza(String orderType){
Pizza pizza = null;
if ("greek".equals(orderType)) {
pizza = new GreekPizza();
pizza.setName("希腊披萨");
} else if ("cheese".equals(orderType)) {
pizza = new CheesePizza();
pizza.setName("奶酪披萨");
} else if ("pepper".equals(orderType)) {
pizza = new CheesePizza();
pizza.setName("胡椒披萨");
}
return pizza;
}
}
public class OrderPizza {
Pizza pizza = null;
public OrderPizza() {
setFactory();
}
public void setFactory() {
String orderType = "";//用户输入披萨类型
do {
//先获取披萨种类
orderType = getType();
//调用工厂方法创建披萨对象
pizza = SimpleFactor.createPizza(orderType);
//输出pizza 信息
if (pizza != null) {
//开始制作披萨
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} else {
System.out.println("无匹配披萨,订购披萨失败");
break;
}
} while (true);
}
//获取客户希望订购的披萨种类
private String getType() {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input piazza type");
return br.readLine();
} catch (IOException e) {
e.printStackTrace();
return "";
}
}
}
//相当于一个客户端,发出订购
public class PizzaStore {
public static void main(String[] args) {
//使用简单工厂模式订购披萨
new OrderPizza();
System.out.println("结束,欢迎下次光临使用");
}
}
二. 工厂方法模式
1. 介绍
工厂方法(FactoryMethod)模式是对简单工厂模式的进一步抽象化,可使系统在不改原来代码的情况下引进新产品,满足开闭原则。
2. 优点
-
为了弥补这种扩展性,在工厂方法模式中,将工厂方法抽象出来,成单独接口;
-
工厂类结构与产品类结构一一对应,每一种产品都对应一个工厂子类;
-
新增一个产品类型时,新加对应的工厂子类即可,不再需要修改既有类;
3. 缺点
跟静态工厂模式比,工厂模式类膨胀太多,需要根据需求取舍;
4.应用场景
扩展新的品类时,不要修改已有代码
-
客户只知道创建产品的工厂名,而不知道具体的产品名。如 TCL 电视工厂、海信电视工厂等。
-
创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。
-
客户不关心创建产品的细节,只关心产品的品牌;
5. 模式的结构
工厂方法模式角色分析:
-
Abstract Factory(抽象工厂) : 提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法createProduct()来创建产品。(核心)
-
ConcreteFactory(具体工厂): 主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
-
Product(抽象产品): 定义了产品的规范,描述了产品的主要特性和功能;
-
ConcreteProduct(具体产品): 实现了抽象产品角色定义的接口,有具体工厂来创建,它同具体工厂之间一一对应;
6. 模式的实例--pizza工厂
这时需要在点披萨时考虑产地问题,若用简单工厂模式解决需创建不同产地的简单工厂类(每个工厂类内部决定创建什么口味的披萨实例对象), 那只要来个订购类,就需去聚合,项目耦合过高,维护性差;
引入工厂方法模式,在工厂基类中定义创建不同口味披萨对象的抽象方法,让不同产地的子类实现方法时,依赖于不同口味的披萨对象;
//将Pizza 类做出抽象类
public abstract class Pizza {
protected String name;//名字
//准备原材料,不同的披萨原料不一样,因此,声明成抽象方法;
public abstract void prepare();
//烘焙
public void bake() {
System.out.println(name + "baking");
}
//切割
public void cut() {
System.out.println(name + "cutting");
}
//打包
public void box() {
System.out.println(name + "boxing");
}
//设置pizza名字
public void setName(String name) {
this.name = name;
}
}
public class LDPepperPizza extends Pizza {
@Override
public void prepare() {
setName("伦敦的胡椒pizza");
System.out.println("给制作伦敦胡椒披萨 准备原材料");
}
}
public class LDCheesePizza extends Pizza {
@Override
public void prepare() {
setName("伦敦的奶酪pizza");
System.out.println("给制作伦敦奶酪披萨 准备原材料");
}
}
public class BJPepperPizza extends Pizza {
@Override
public void prepare() {
setName("北京的胡椒pizza");
System.out.println("给制作北京的胡椒披萨 准备原材料");
}
}
public class BJCheesePizza extends Pizza {
@Override
public void prepare() {
setName("北京的奶酪pizza");
System.out.println("给制作北京奶酪披萨 准备原材料");
}
}
//抽象工厂类
public abstract class PizzaFactory {
//定义根据披萨种类创建披萨对象的抽象方法,让工厂子类去实现
abstract Pizza createPizza(String orderType);
//初始化构造方法
public PizzaFactory() {
Pizza pizza = null;
String orderType = "";//订购披萨的类型
do {
orderType = getType(); //先获取披萨种类
pizza = createPizza(orderType);//这里的抽象方法,由工厂子类完成
//输出pizza 制作信息
if (pizza != null) {
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} else {
System.out.println("无匹配披萨,订购披萨失败");
break;
}
} while (true);
}
//可获取客户希望订购的披萨种类
private String getType() {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input piazza type");
return br.readLine();
} catch (IOException e) {
e.printStackTrace();
return "";
}
}
}
//具体工厂类1- 伦敦披萨工厂类
public class LDPizzaFactory extends PizzaFactory {
@Override
Pizza createPizza(String pType) {
Pizza pizza = null;
if ("cheese".equals(pType)) {
pizza = new LDCheesePizza();
} else if ("pepper".equals(pType)) {
pizza = new LDPepperPizza();
}
return pizza;
}
}
//具体工厂类2 - 北京披萨工厂类
public class BJPizzaFactory extends PizzaFactory {
@Override
Pizza createPizza(String pType) {
Pizza pizza=null;
if ("cheese".equals(pType)) {
pizza = new BJCheesePizza();
} else if ("pepper".equals(pType)) {
pizza = new BJPepperPizza();
}
return pizza;
}
}
//相当于一个客户端,发出订购
public class PizzaStore {
public static void main(String[] args) {
//创建北京口味的各种pizza
String loc=getLoc();//订购披萨的地点
if (loc.equals("bj")) {
new BJPizzaFactory();
} else if (loc.equals("ld")){
new LDPizzaFactory();
}
System.out.println("退出程序");
}
//可获取客户希望的订购地点
private static String getLoc() {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input piazza local");
return br.readLine();
} catch (IOException e) {
e.printStackTrace();
return "";
}
}
}
3. 抽象工厂模式
1.介绍
前面介绍的工厂方法模式中考虑的是一类产品的生产,如牧场只养动物,电视机厂只生产电视机,同种类称为同等级,也就是说:工厂方法模式只考虑生产同等级的产品,但现实生活中许多工厂是综合型工厂,能生产多等级(种类)的产品,如农场里既养动物又种植物,电器厂既生产电视机又生产空调或洗衣机,而抽象工厂模式就将考虑多等级产品的生产,将同一个具体工厂所生产的位于不同等级的一组产品称为一个产品族。
抽象工厂模式(AbstractFactory)
-
为访问类提供一个接口用于创建相关或有依赖关系的对象簇,无需指明具体的类就能得到同族的不同等级的产品的模式结构;
-
抽象工厂模式可以将简单工厂模式和工厂方法模式进行整合;
-
从设计层面看,抽象工厂模式就是对简单工厂模式的改进(或叫进一步的抽象);
-
将工厂抽象成两层:抽象工厂 和 具体实现的工厂子类,可据创建对象类型使用对应的工厂子类,将单个简单工厂变为工厂簇以便维护和扩展;
2. 优点
抽象工厂模式除了具有工厂方法模式的优点外,其他主要优点如下。
-
可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
-
当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品组。
-
抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。
3. 缺点
-
当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改,增加了系统的抽象性和理解难度;
2) 当系统中只存在一个等级结构的产品时,抽象工厂模式将退化到工厂方法模式。
4. 应用场景
抽象工厂模式通常适用于以下场景:
1.当需要创建的对象是一系列相互关系或相互依赖的产品族时,如电器厂中的电视机,洗衣机,空调等;
2.系统中有多个产品族,但每次只使用其中某一个族产品,如有人只喜欢穿某一个品牌的衣服和鞋;
3.系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例创建细节和内部结构;
5. 模式的结构
抽象工厂模式角色分析:
同工厂方法模式一样,也是由抽象工厂、具体工厂、抽象产品和具体产品等4个要素构成,但抽象工厂中方法个数不同,抽象产品的个数也不同。
-
AbstractFactory(抽象工厂):它包含多个创建产品的方法 createProduct(),可创建多个不同等级的产品。
-
ConcreteFactory(具体工厂):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
-
Product(抽象产品): 定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
-
ConcreteProduct(具体产品):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。
6. 模式的实现
1.图像产品族结构图
2. 相关代码实现
public class AbsFactoryClient {
public static void main(String[] args){
//获取形状工厂
GraphicFactory shapeFactory = FactoryProducer.getFactory("SHAPE");
if (shapeFactory==null) return;
//获取形状为 Circle 的对象
Shape circle = shapeFactory.getShape("CIRCLE");
//调用 Circle 的draw方法
circle.draw();
//获取形状为 rectangle 的对象
Shape rectangle = shapeFactory.getShape("RECTANGLE");
//调用 rectangle 的draw方法
rectangle.draw();
//获取颜色工厂
GraphicFactory colorFactory = FactoryProducer.getFactory("Color");
if (colorFactory==null) return;
//获取颜色为 Red 的对象
Color red = colorFactory.getColor("RED");
red.fill(); //调用 Red 的 fill 方法
//获取颜色为 Blue 的对象
Color blue = colorFactory.getColor("BLUE");
blue.fill();
}
}
//创建一个工厂创造器/生成器类,通过传递形状或颜色信息来获取工厂。
public class FactoryProducer {
public static GraphicFactory getFactory(String choice){
if (choice.equalsIgnoreCase("SHAPE")) {
return new ShapeFactory();
} else if (choice.equalsIgnoreCase("COLOR")) {
return new ColorFactory();
}
return null;
}
}
//为Color 和 Shape 对象创建抽象类来获取工厂
public abstract class GraphicFactory {
public abstract Color getColor(String colorType);
public abstract Shape getShape(String shapeType);
}
//创建扩展了 AbstractFactory 的工厂类,基于给定的信息生成实体类的对象。
public class ShapeFactory extends GraphicFactory {
@Override
public Color getColor(String color) {
return null;
}
@Override
public Shape getShape(String shapeType) {
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
} else if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
}
return null;
}
}
public class ColorFactory extends GraphicFactory {
@Override
public Color getColor(String color) {
if(color == null){
return null;
}
if(color.equalsIgnoreCase("RED")){
return new Red();
}else if(color.equalsIgnoreCase("BLUE")){
return new Blue();
}
return null;
}
@Override
public Shape getShape(String shapeType) {
return null;
}
}
//为形状创建一个接口
public interface Shape {
void draw();
}
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
public interface Color {
void fill();
}
public class Red implements Color{
@Override
public void fill() {
System.out.println("Inside Red::fill() method.");
}
}
public class Blue implements Color{
@Override
public void fill() {
System.out.println("Inside Blue::fill() method.");
}
}
程序运行结果
Inside Circle::draw() method.
Inside Rectangle::draw() method.
Inside Red::fill() method.
Inside Blue::fill() method.