Factory Design Pattern - tenji/ks GitHub Wiki
工厂模式
Java 里边共有 23 种设计模式而工厂模式就有三种,它们分别是:
- 简单工厂模式(并不在 23 种模式之中)
- 工厂方法模式
- 抽象工厂模式(不常用)
其中我们通常所说的工厂模式指的是工厂方法模式,工厂方法模式是日常开发中使用频率最高的一种设计模式,甚至在 Android 的源码中也是随处可见。
三种工厂模式是逐步重构的结果吗?
可以这么认为。
- 最开始,对象创建直接使用 if-else
- if 分支太多,将代码重构都简单工厂类中
- 如果对象创建逻辑复杂,代码量大,可以使用工厂方法进行拆分
- 如果工厂类膨胀太快,过多,可以使用抽象工厂将部分工厂组合起来使用
一、简单工厂模式(Simple Factory Pattern)
简单工厂模式不是一个正式的设计模式,但它是工厂模式的基础。它使用一个单独的工厂类来创建不同的对象,根据传入的参数决定创建哪种类型的对象。
1.1 结构
1.2 代码示例
Phone 类:手机标准规范类(AbstractProduct)
public interface Phone {
void make();
}
MiPhone 类:制造小米手机(Product1)
public class MiPhone implements Phone {
public MiPhone() {
this.make();
}
@Override
public void make() {
System.out.println("make xiaomi phone!");
}
}
IPhone 类:制造苹果手机(Product2)
public class IPhone implements Phone {
public IPhone() {
this.make();
}
@Override
public void make() {
System.out.println("make iphone!");
}
}
PhoneFactory 类:手机代工厂(Factory)
public class PhoneFactory {
public Phone makePhone(String phoneType) {
if(phoneType.equalsIgnoreCase("MiPhone")){
return new MiPhone();
} else if(phoneType.equalsIgnoreCase("iPhone")) {
return new IPhone();
}
return null;
}
}
Driver Program
public class Demo {
public static void main(String[] arg) {
PhoneFactory factory = new PhoneFactory();
Phone miPhone = factory.makePhone("MiPhone"); // make xiaomi phone!
IPhone iPhone = (IPhone)factory.makePhone("iPhone"); // make iphone!
}
}
简单工厂模式符合 OCP 吗?
不符合。因为每次添加新产品就需要修改工厂类。
开放封闭原则(OCP,Open Closed Principle)的定义:In object-oriented programming, the open–closed principle (OCP) states "software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification".
1.3 优点
- 封装(Encapsulation):它对客户端代码隐藏了具体的产品类,从而减少了代码对特定实现的依赖。这提高了可维护性并减少了耦合;
- 解耦(Decoupling):简单工厂模式提供专门的工厂类用于创建对象,它将对象创建逻辑与使用这些对象的客户端代码分开。这使得代码更加灵活和可维护,因为创建过程的更改不需要修改客户端代码。
1.4 缺点
不符合“开闭原则”,每次添加新产品就需要修改工厂类。在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展维护,并且工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。为了解决简单工厂模式的问题,出现了工厂方法模式。
二、工厂方法模式(Factory Method Pattern)
工厂方法模式定义了一个创建对象的接口,但由子类决定实例化哪个类。工厂方法将对象的创建延迟到子类。工厂方法模式是简单工厂的进一步深化, 在工厂方法模式中,我们不再提供一个统一的工厂类来创建所有的对象,而是针对不同的对象提供不同的工厂。也就是说每个对象都有一个与之对应的工厂。
2.1 结构
2.2 代码示例
AbstractFactory 类:生产不同产品的工厂的抽象类
public interface AbstractFactory {
Phone makePhone();
}
XiaoMiFactory 类:生产小米手机的工厂(ConcreteFactory1)
public class XiaoMiFactory implements AbstractFactory {
@Override
public Phone makePhone() {
return new MiPhone();
}
}
AppleFactory类:生产苹果手机的工厂(ConcreteFactory2)
public class AppleFactory implements AbstractFactory {
@Override
public Phone makePhone() {
return new IPhone();
}
}
Driver Program
public class Demo {
public static void main(String[] arg) {
AbstractFactory miFactory = new XiaoMiFactory();
AbstractFactory appleFactory = new AppleFactory();
miFactory.makePhone(); // make xiaomi phone!
appleFactory.makePhone(); // make iphone!
}
}
2.3 工厂方法模式 vs 模板方法模式
工厂方法模式是模板方法模式(Template Method Pattern)的一种特例吗?
两者的相同点:
- 依赖继承。
两者的不同点:
- 模板方法:定义算法的步骤,并将实现它们的任务留给子类。
- 工厂方法:超类定义了用于创建对象的接口。子类决定实例化哪个具体类。
两者都是将对象的创建延迟到子类,甚至可以认为定义对象创建接口也是定义算法的一种,这么看的话貌似可以把工厂方法模式看做是模板方法模式的一种特例。
更多关于模板方法模式,传送门
2.4 优点
- 解耦(Decoupling):它将对象创建逻辑与使用这些对象的客户端代码分开。这使得代码更加灵活和可维护,因为创建过程的更改不需要修改客户端代码;
- 可扩展性(Extensibility):无需更改客户端代码即可轻松引入新产品类型。您只需创建一个新的 Concrete Creator 子类并实现工厂方法即可生产新产品;
- 代码可重用性(Code Reusability):工厂方法可以在需要创建对象的应用程序的不同部分中重用。这促进了对象创建逻辑的集中和重用;
- 封装(Encapsulation):它对客户端代码隐藏了具体的产品类,从而减少了代码对特定实现的依赖。这提高了可维护性并减少了耦合。
2.5 缺点
- 增加复杂性(Increased Complexity):它引入了额外的类和接口,添加了一个抽象层,可以使代码更难以理解和维护,特别是对于那些不熟悉该模式的人来说;
- 开销(Overhead):使用多态性和动态绑定可能会对性能产生轻微影响,尽管这在大多数应用程序中通常可以忽略不计;
- 对具体子类的依赖(Dependency on Concrete Subclasses):客户端代码仍然依赖于抽象 Creator 类,需要了解其具体子类才能进行正确的工厂方法调用;
- 过度使用的可能性(Potential for Overuse):明智地使用工厂方法模式以避免过度设计应用程序非常重要。简单的对象创建通常可以直接处理,而不需要工厂。
2.6 实际使用场景
- Creational Frameworks:
- JDBC(Java 数据库连接)广泛使用工厂来创建连接、语句和结果集。像 Spring 和 Guice 这样的依赖注入框架严重依赖工厂来创建和管理 bean。
- Logging Frameworks:
- Log4j 和 Logback 等日志记录框架使用工厂来创建具有不同配置的记录器,从而能够控制日志记录级别和输出目的地。比如:
org.apache.log4j.spi.LoggerFactory
- Log4j 和 Logback 等日志记录框架使用工厂来创建具有不同配置的记录器,从而能够控制日志记录级别和输出目的地。比如:
- Serialization and Deserialization:
- 对象序列化框架通常使用工厂从序列化数据创建对象,支持不同的序列化格式和版本控制。
- Game Development:
- 游戏引擎通常使用工厂来创建不同类型的游戏对象、角色和关卡,从而促进代码组织和灵活性。
三、抽象工厂模式(Abstract Factory Pattern)
抽象工厂模式提供一个创建一系列相关或互相依赖对象的接口,而无需指定它们具体的类。这个模式最不好理解,而且在实际应用中局限性也蛮大的,因为这个模式并不符合开闭原则。实际开发还需要做好权衡。抽象工厂模式是工厂方法的仅一步深化,在这个模式中的工厂类不单单可以创建一个对象,而是可以创建一组对象。这是和工厂方法最大的不同点。
3.1 结构
一个实际的例子:小米和苹果产品生产
3.2 代码示例
PC 类:定义 PC 产品的接口(AbstractPC)
public interface PC {
void make();
}
MiPC 类:定义小米电脑产品(MIPC)
public class MiPC implements PC {
public MiPC() {
this.make();
}
@Override
public void make() {
System.out.println("make xiaomi PC!");
}
}
MAC 类:定义苹果电脑产品(MAC)
public class MAC implements PC {
public MAC() {
this.make();
}
@Override
public void make() {
System.out.println("make MAC!");
}
}
AbstractFactory 类:增加 PC 产品制造接口
public interface AbstractFactory {
Phone makePhone();
PC makePC();
}
XiaoMiFactory 类:增加小米 PC 的制造(ConcreteFactory1)
public class XiaoMiFactory implements AbstractFactory {
@Override
public Phone makePhone() {
return new MiPhone();
}
@Override
public PC makePC() {
return new MiPC();
}
}
AppleFactory 类:增加苹果 PC 的制造(ConcreteFactory2)
public class AppleFactory implements AbstractFactory {
@Override
public Phone makePhone() {
return new IPhone();
}
@Override
public PC makePC() {
return new MAC();
}
}
Driver Program
public class Demo {
public static void main(String[] arg) {
AbstractFactory miFactory = new XiaoMiFactory();
AbstractFactory appleFactory = new AppleFactory();
miFactory.makePhone(); // make xiaomi phone!
miFactory.makePC(); // make xiaomi PC!
appleFactory.makePhone(); // make iphone!
appleFactory.makePC(); // make MAC!
}
}
优点
- Isolation of concrete classes:
- 抽象工厂模式可帮助你控制应用程序创建的对象的类;
- 由于工厂封装了创建产品对象的职责和过程,因此它将客户端与实现类隔离;
- 客户端通过抽象接口来操作实例。产品类名在具体工厂的实现中是隔离的;它们不会出现在客户端代码中。
缺点
- Complexity:
- 抽象工厂可能会给代码库带来额外的复杂性;
- 对于更简单的项目来说,拥有多个工厂和抽象产品接口可能有点过分了。
- Increased Number of Classes:
- 当您引入更多的抽象工厂和产品系列时,系统中的类数量会快速增长;
- 这可能会使代码更难管理和理解,特别是对于较小的项目。
- Dependency Inversion Principle Violation:
- 在某些情况下,抽象工厂模式可能会导致违反依赖倒置原则,特别是当客户端代码直接依赖于具体工厂实现而不是抽象接口时。
- Limited Extensibility:
- 扩展抽象工厂层次结构或引入新的产品系列可能需要修改代码的多个部分,这可能会导致级联更改并降低系统的可扩展性。
- Not Ideal for Simple Systems:
- 对于较小、不太复杂的系统来说,抽象工厂模式可能有点过分了,在这些系统中,定义抽象工厂和产品的开销超过了该模式的好处。
四、工厂方法模式 vs 抽象工厂模式
- 工厂方法只有一个抽象产品类和一个抽象工厂类,但可以派生出多个具体产品类和具体工厂类,每个具体工厂类只能创建一个具体产品类的实例;
- 抽象工厂模式拥有多个抽象产品类(产品族)和一个抽象工厂类,每个抽象产品类可以派生出多个具体产品类;抽象工厂类也可以派生出多个具体工厂类,同时每个具体工厂类可以创建多个具体产品类的实例。
什么是产品等级和产品族?