设计模式之工厂模式
工厂方法模式,又称工厂模式、多态工厂模式或虚拟构造器模式,通过定义工厂父类负责定义创建对象的公共接口,而子类负责生成具体的对象抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口。
阅读本文建议先看下目录,把握整体脉络。最好是使用过工厂模式,即使没有用过该设计模式,文章中的示例建议一定要自己动手敲一敲,便于更加全面深入地了解工厂模式,有异议可以评论或私聊哦
1、简单(静态)工厂模式
1.1 含义
- 简单工厂模式又称静态方式模式(因为工厂类定义了一个静态方法)
- 现实生活中工厂是负责生产产品的;同样在设计模式中,简单工厂模式我们可以理解为负责生产对象的一个类,称为”工厂类“
1.2 解决的问题
将”类实例化的操作“与”使用对象的操作“分开,让使用者不需要知道具体参数就可以实例化出所需要的产品类,从而避免了在客户端代码中显式指定,实现了解耦,即使用者可以直接消费产品而不需要知道其生产的细节
1.3 模式原理
1.3.1 模式组成
1.3.2 UML类图
1.3.3 使用步骤
- 创建抽象产品类,定义具体产品的公共接口
- 创建具体产品类(继承抽象产品类),定义生产的具体产品
- 创建工厂类,通过创建静态方法根据传入不同参数从而创建不同产品类的实例
- 外界调用工厂类的静态方法传入不同参数具体产品类
1.4 实例讲解
1.4.1 需求概况
- 背景:小成有一个塑料生产厂,用来做塑料加工生意
- 目的:最近推出了3个产品,小成希望使用简单工厂模式
1.4.2 使用步骤
创建抽象产品类,定义具体产品类的公共接口
package com.zizhou.factory.simple;
/**
* 创建抽象产品类,定义具体产品类的公共接口
*/
public abstract class Product {
public abstract void show();
}
创建具体产品类,继承抽象产品类,定义生产的具体产品
// 创建具体产品类,继承抽象产品类,定义生产的具体产品
package com.zizhou.factory.simple;
public class ProductA extends Product{
@Override
public void show() {
System.out.println("ProductA created");
}
}
package com.zizhou.factory.simple;
public class ProductB extends Product{
@Override
public void show() {
System.out.println("ProductB created");
}
}
package com.zizhou.factory.simple;
public class ProductC extends Product{
@Override
public void show() {
System.out.println("ProductC created");
}
}
创建工厂类,通过创建静态方法根据传入的不同参数生成不同具体产品类的实例
package com.zizhou.factory.simple;
/**
* 创建工厂类,通过创建静态方法根据传入的不同参数生成不同具体产品类的实例
*/
public class Factory {
public static Product getProduct(String type) {
switch (type) {
case "A":
return new ProductA();
case "B":
return new ProductB();
case "C":
return new ProductC();
default:
return null;
}
}
}
外界调用工厂类的静态方法,传入不同参数生成不同具体产品类的实例
package com.zizhou.factory.simple;
/**
* 外界调用工厂类的静态方法,传入不同参数生成不同具体产品类的实例
*/
public class SimpleFactoryTest {
public static void main(String[] args) {
try {
Factory.getProduct("A").show();
} catch (Exception e) {
System.out.println("没有这类产品");
}
try {
Factory.getProduct("B").show();
} catch (Exception e) {
System.out.println("没有这类产品");
}
try {
Factory.getProduct("C").show();
} catch (Exception e) {
System.out.println("没有这类产品");
}
try {
Factory.getProduct("D").show();
} catch (Exception e) {
System.out.println("没有这类产品");
}
}
}
1.5 优点
- 将实例的创建与使用工作分开,使用者不需要关心产品的创建细节,实现了解耦
- 把初始化实例时的工作放到工厂里进行,使代码更容易维护,更符合面向对象原则&面向接口编程,而不是面向实现编程
1.6 缺点
- 工厂类中集中了所有实例的创建逻辑,一旦这个工厂不能工作,整个系统都会受到影响
- 违背了开放-关闭原则,一旦添加新产品就不得不修改工厂类的逻辑,这样会造成工厂逻辑过于复杂
- 简单工厂模式使用了静态工厂方法,静态方法不能被继承和重写,会造成工厂角色无法形成基于继承的等级结构
1.7 使用步骤
- 调用方如果只知道传入工厂类的参数,对于如何创建对象的逻辑并不关心时
- 当工厂类负责创建的对象比较少时
2、工厂方法模式
2.1 介绍
2.1.1 含义
工厂方法模式,又称工厂模式、多态工厂模式或虚拟构造器模式,通过定义工厂父类负责定义创建对象的公共接口,而子类负责生成具体的对象
2.1.2 主要作用
将类实例化(具体产品的创建)的工作延迟给工厂类的子类(具体工厂)中完成,由子类来决定应该实例化(创建)哪一个类
2.1.3 解决的问题
即简单工程模式中的缺点,把具体产品的创建延迟到子类中,此时工厂类不再负责所有产品的创建,而只是给出具体工厂需要实现的接口,这样工厂方式在添加新的产品时不需要修改工厂类的逻辑,而是添加新的工厂子类,符合开放关闭原则,克服了简单工厂模式中的缺点
2.2 模式原理
2.2.1 UML类图
2.2.2 模式组成
2.2.3 使用步骤
- 创建抽象工厂类,定义具体工厂的公共接口
- 创建抽象产品类,定义具体产品的公共接口
- 创建具体产品类,继承抽象产品类,定义生产的具体产品
- 创建具体工厂类,继承抽象工厂类,定义创建具体产品实例的方法
- 外界通过调用具体工厂类的方法,从而创建不同具体产品类的实例
2.3 实例讲解
2.3.1 实例概况
- 背景:小成有一间塑料加工厂(仅生产A类产品);随着客户需求的变化,客户需要生产B类产品;
- 冲突:改变原有塑料加工厂的配置和变化非常困难,假设下一次客户需要再发生变化,再次改变将增大非常大的成本;
- 解决方案:小成决定置办塑料分厂B来生产B类产品;
2.3.2 使用步骤
创建抽象工厂类,定义具体工厂的公共接口
package com.zizhou.factory.pattern;
/**
* 创建抽象工厂类,定义具体工厂的公共接口
*/
public abstract class Factory {
public abstract Product getProduct();
}
创建抽象产品类,定义具体产品的公共接口
package com.zizhou.factory.pattern;
/**
* 创建抽象产品类,定义具体产品的公共接口
*/
public abstract class Product {
public abstract void show();
}
创建具体产品类,继承抽象产品类,描述生产的具体产品
package com.zizhou.factory.pattern;
/**
* 创建具体产品类,继承抽象产品类,描述生产的具体产品
*/
public class ProductA extends Product{
@Override
public void show() {
System.out.println("ProductA created");
}
}
package com.zizhou.factory.pattern;
public class ProductB extends Product{
@Override
public void show() {
System.out.println("ProductB created");
}
}
创建具体工厂类, 继承抽象工厂类, 定义创建具体产品实例的方法
package com.zizhou.factory.pattern;
/**
* 创建具体工厂类, 继承抽象工厂类, 定义创建具体产品实例的方法
*/
public class FactoryA extends Factory{
@Override
public Product getProduct() {
return new ProductA();
}
}
package com.zizhou.factory.pattern;
public class FactoryB extends Factory{
@Override
public Product getProduct() {
return new ProductB();
}
}
外界通过调用具体工厂类的方法,从而创建不同具体产品类的实例
package com.zizhou.factory.pattern;
/**
* 外界通过调用具体工厂类的方法,从而创建不同具体产品类的实例
*/
public class FactoryPatterTest {
public static void main(String[] args) {
// 产品A
FactoryA factoryA = new FactoryA();
factoryA.getProduct().show();
// 产品B
FactoryB factoryB = new FactoryB();
factoryB.getProduct().show();
}
}
2.4 优点
-
更符合开闭原则
新增一件产品时,只需要增加相应的产品子类和工厂子类即可,简单工厂模式需要修改工厂类的判断逻辑 -
符合单一职责原则
每个具体工厂类只负责创建对应的产品,简单工厂中的工厂类存在复杂的switch逻辑判断 -
不使用静态工厂方法, 可以形成基于继承的等级结构。简单工厂模式的工厂类使用静态工厂方法
总结: 工厂模式可以说是简单工厂模式的进一步抽象和扩展,在保留了简单工厂的封装优点同时,让扩展变得简单,让继承变得可行,增加了多态性的体现
2.5 缺点
- 添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加,在一定程度上增加了系统的复杂度;同时,有更多的类需要编译和运行,给系统带来一些额外的开销
- 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM,反射等技术,增加了系统的实现难度
- 虽然保证了工厂方法的对修改关闭原则,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类
- 一个具体工厂只能创建一个具体产品
2.6 应用场景
- 当一个类不知道它所需要的对象的类时
在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道对应的工厂即可 - 当一个类需要通过子类来指定创建对象时
在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏替换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展 - 将创建对象的任务委托给多个工厂子类中的一个,客户端在使用时可以无需关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或者数据库中
3、抽象工厂模式
3.1 定义
抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口
3.2 产品等级和产品族
- 产品等级:即是相同的产品,例如美的冰箱、海尔冰箱、格力冰箱,他们都是冰箱,他们的产品等级都相同,虽然不属于同一个厂家
- 产品族:即是相同的厂家,美的冰箱、美的空调、美的电扇,他们虽然不是同一个产品等级,但属于同一个厂家,所以他们的产品族相同
3.3 抽象工厂和抽象方法的区别
抽象工厂是创建型设计模式,他强调了一系列产品对象(即属于同一个产品族)的创建过程,和工厂方法侧重点不同,工厂方法更侧重于同一产品等级,而抽象工厂侧重的是同一产品族
3.4 使用步骤
- 创建产品接口,定义产品信息方法
- 创建具体产品, 实现产品接口,描述具体产品的信息
- 创建抽象工厂接口,提供创建具体产品实例的方法
- 创建具体的工厂, 实现抽象工厂接口
- 外界调用工厂中提供的方法, 创建具体产品
3.5 实例概况
创建产品接口,描述产品信息
package com.zizhou.factory.abstractly;
/**
* 创建产品接口,描述产品信息
*/
public interface AirConditioner {
void airConditionInfo();
}
package com.zizhou.factory.abstractly;
public interface Fan {
void fanInfo();
}
package com.zizhou.factory.abstractly;
public interface Fridge {
void fridgeInfo();
}
创建具体产品, 实现产品接口,描述具体产品的信息
package com.zizhou.factory.abstractly;
/**
* 创建具体产品, 实现产品接口,描述具体产品的信息
*/
public class MediaAirConditioner implements AirConditioner{
@Override
public void airConditionInfo() {
System.out.println("美的空调");
}
}
package com.zizhou.factory.abstractly;
public class MediaFan implements Fan{
@Override
public void fanInfo() {
System.out.println("美的电风扇");
}
}
package com.zizhou.factory.abstractly;
public class MediaFridge implements Fridge{
@Override
public void fridgeInfo() {
System.out.println("美的冰箱");
}
}
创建抽象工厂接口,提供创建具体产品实例的方法
package com.zizhou.factory.abstractly;
/**
* 创建抽象工厂接口,提供创建具体产品实例的方法
*/
public interface Factory {
AirConditioner createAirConditioner();
Fridge createFridge();
Fan createFan();
}
package com.zizhou.factory.abstractly;
/**
* 创建具体的工厂, 实现抽象工厂接口
*/
public class MediaFactory implements Factory{
@Override
public AirConditioner createAirConditioner() {
return new MediaAirConditioner();
}
@Override
public Fridge createFridge() {
return new MediaFridge();
}
@Override
public Fan createFan() {
return new MediaFan();
}
}
外界调用工厂中提供的方法, 创建具体产品
/**
* 外界调用工厂中提供的方法, 创建具体产品
*/
public class AbstractlyTest {
public static void main(String[] args) {
Factory factory = new MediaFactory();
factory.createAirConditioner().airConditionInfo();
factory.createFridge().fridgeInfo();
factory.createFan().fanInfo();
}
}
3.6 优点
- 可以确信你从工厂得到的产品彼此是兼容的
- 可以避免具体产品和客户端代码之间的紧密耦合
- 符合单一职责原则
- 符合开闭原则
3.7应用场景
程序需要处理不同系列的相关产品,但是您不需要依赖于这些产品的具体类时,可以使用抽象工厂模式
4、参考文章
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)