工厂模式
其主要目的是封装对象的创建过程,使客户端代码和具体的对象实现解耦。这样子就不用每次都new对象,更换对象的话,所有new对象的地方也要修改,违背了开闭原则(对扩展开放,对修改关闭)。使用工厂来生产对象,更换对象也直接在工厂更换即可。
工厂模式的主要好处包括:
- 解耦合:工厂模式将对象的创建过程与客户端代码分离,客户端不需要知道具体的对象是如何创建的,只需要通过工厂方法获取对象即可,从而降低了代码之间的耦合度。
- 灵活性:由于工厂负责创建对象,客户端可以通过工厂方法获取不同的对象实例,而无需关心具体的实现细节,从而提高了系统的灵活性。
- 可扩展性:如果需要添加新的产品类型,只需在工厂中添加相应的产品创建逻辑,而不需要修改客户端代码,这样可以很方便地扩展系统的功能。
- 统一管理:工厂模式将对象的创建集中在一个地方,便于统一管理和维护,提高了代码的可维护性。
使用场景:
- 当一个系统需要创建多个类型的对象,并且这些对象之间存在着共同的接口时,可以考虑使用工厂模式。
- 当客户端不需要知道具体的对象是如何创建的,只需要获取对象实例时,可以使用工厂模式。
- 当系统需要动态地决定创建哪种类型的对象时,可以使用工厂模式。
工厂模式包含以下几个核心角色:
- 抽象产品(Abstract Product):**定义了产品的共同接口或抽象类。**它可以是具体产品类的父类或接口,规定了产品对象的共同方法。
- 具体产品(Concrete Product):实现了抽象产品接口,定义了具体产品的特定行为和属性。
- 抽象工厂(Abstract Factory):声明了创建产品的抽象方法,可以是接口或抽象类。它可以有多个方法用于创建不同类型的产品。
- 具体工厂(Concrete Factory):实现了抽象工厂接口,负责实际创建具体产品的对象。
这里介绍三种工厂:
- 简单工厂模式(不属于GOF的23种经典设计模式)
- 工厂方法模式
- 抽象工厂模式
简单工厂模式
简单工厂不是一种设计模式,反而比较像是一种编程习惯。
结构:
简单工厂包含如下角色:
- 抽象产品 :定义了产品的规范,描述了产品的主要特性和功能。
- 具体产品 :实现或者继承抽象产品的子类
- 具体工厂 :提供了创建产品的方法,调用者通过该方法来获取产品。
使用场景:
- 当对象的创建逻辑相对简单,并且不需要频繁地进行变更时,可以考虑使用简单工厂模式。
- 在客户端只知道所需产品的名称或类型,而不需要关心产品的创建过程时,可以使用简单工厂模式。
实现思路:
// 抽象产品接口
interface Product {
void show();
}
// 具体产品类A
class ConcreteProductA implements Product {
@Override
public void show() {
System.out.println("This is product A.");
}
}
// 具体产品类B
class ConcreteProductB implements Product {
@Override
public void show() {
System.out.println("This is product B.");
}
}
// 简单工厂类
class SimpleFactory {
public static Product createProduct(String type) {
if ("A".equals(type)) {
return new ConcreteProductA();
} else if ("B".equals(type)) {
return new ConcreteProductB();
}
return null;
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Product productA = SimpleFactory.createProduct("A");
productA.show();
Product productB = SimpleFactory.createProduct("B");
productB.show();
}
}
上面的工厂类创建对象的功能定义为静态的,这个属于是静态工厂模式,当然你也可以不设置为静态的。
优缺点:
优点:
- 简单工厂模式中,客户端通过工厂类的静态方法来获取产品实例,而不需要直接实例化具体产品类。如果要实现新产品直接修改工厂类,而不需要在原代码中修改。
缺点:
- 工厂类负责创建所有产品,因此如果系统需要添加新的产品类型,需要修改工厂类,违反了开放封闭原则。
工厂方法模式
使用工厂方法模式可以完美的解决简单工厂模式的缺点,完全遵循开闭原则。
概念
定义一个用于创建对象的接口,让子类决定实例化哪个产品类对象。工厂方法使一个产品类的实例化延迟到其工厂的子类。
结构
工厂方法模式的主要角色:
- 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。
- 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
- 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
- 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
图例
使用工厂方法模式对上例进行改进:
工厂方法模式适用于需要创建一系列相关对象的情况
// 抽象产品接口
interface Product {
void show();
}
// 具体产品类A
class ConcreteProductA implements Product {
@Override
public void show() {
System.out.println("This is product A.");
}
}
// 具体产品类B
class ConcreteProductB implements Product {
@Override
public void show() {
System.out.println("This is product B.");
}
}
// 抽象工厂类
interface Factory {
Product createProduct();
}
// 具体工厂类A,负责创建产品A
class ConcreteFactoryA implements Factory {
@Override
public Product createProduct() {
return new ConcreteProductA();
}
}
// 具体工厂类B,负责创建产品B
class ConcreteFactoryB implements Factory {
@Override
public Product createProduct() {
return new ConcreteProductB();
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Factory factoryA = new ConcreteFactoryA();
Product productA = factoryA.createProduct();
productA.show();
Factory factoryB = new ConcreteFactoryB();
Product productB = factoryB.createProduct();
productB.show();
}
}
于是乎要增加产品类时只要相应地增加工厂类,不需要修改工厂类的代码了,这样就解决了简单工厂模式的缺点。
使用场景:
- 当需要创建的对象是一个具体的产品,但是不确定具体产品的类型时,可以使用工厂方法模式。
- 在工厂类中定义一个创建产品的抽象方法,由子类负责实现具体产品的创建过程,从而实现了产品的创建和客户端的解耦。
优缺点:
优点:
- 工厂方法模式中,客户端通过调用工厂类的方法来创建产品,具体产品的创建逻辑由子类实现,不同的产品由不同的工厂子类负责创建。
- 工厂方法模式符合开放封闭原则,因为客户端可以通过新增工厂子类来添加新的产品类型,而无需修改原有的代码。
缺点:
- 每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。
抽象工厂模式
抽象工厂模式通常涉及一族相关的产品,每个具体工厂类负责创建该族中的具体产品。
使用场景:
- 当一个系统需要创建一系列相互关联或相互依赖的产品对象时,可以考虑使用抽象工厂模式。
- 抽象工厂模式提供了一个创建一组相关或相互依赖对象的接口,客户端可以通过该接口来创建产品族中的不同产品,而不需要关心具体的产品实现。
所以由此也可看出,普通工厂模式,工厂方法模式都只是单一产品类的工厂;而很多时候我们需要综合性的,需要生产多等级产品的工厂。下图所示横轴是产品等级,也就是同一类产品;纵轴是产品族,也就是同一品牌的产品,同一品牌的产品产自同一个工厂:
结构
抽象工厂模式的主要角色如下:
- 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法,可以创建多个不同等级的产品。
- 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
- 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
- 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。
代码案例:
// 抽象产品接口
interface Product {
void show();
}
// 具体产品类A
class ConcreteProductA implements Product {
@Override
public void show() {
System.out.println("This is product A.");
}
}
// 具体产品类B
class ConcreteProductB implements Product {
@Override
public void show() {
System.out.println("This is product B.");
}
}
// 抽象工厂接口
interface AbstractFactory {
Product createProductA();
Product createProductB();
}
// 具体工厂类,负责创建产品A和产品B
class ConcreteFactory implements AbstractFactory {
@Override
public Product createProductA() {
return new ConcreteProductA();
}
@Override
public Product createProductB() {
return new ConcreteProductB();
}
}
// 客户端
public class Client {
public static void main(String[] args) {
AbstractFactory factory = new ConcreteFactory();
Product productA = factory.createProductA();
productA.show();
Product productB = factory.createProductB();
productB.show();
}
}
-
使用场景:
- 当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等。
- 系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋。
- 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。
优缺点
优点:
当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
缺点:
当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。
模式扩展 (利用反射机制来创建对象)
简单工厂+配置文件解除耦合
可以通过工厂模式+配置文件的方式解除工厂对象和产品对象的耦合。
通过使用配置文件,将创建对象的参数存储在外部配置文件中,可以在不修改客户端代码的情况下,通过修改配置文件来改变对象的创建方式。这样就可以实现对创建逻辑的解耦合,客户端不需要知道具体的创建方式,只需要从工厂类获取对象即可。
具体实现步骤如下:
- 在配置文件中配置需要创建的对象的类名或者类型。
- 在简单工厂类中读取配置文件,并根据配置的信息来创建对应的对象。
假设有一个配置文件 config.properties
,内容如下:
product.type=ConcreteProductA
创建简单工厂类 SimpleFactory.java
,用于读取配置文件并根据配置创建对象:
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class SimpleFactory {
public static Product createProduct() {
Properties properties = new Properties();
try (InputStream inputStream = SimpleFactory.class.getResourceAsStream("config.properties")) {
properties.load(inputStream);
String productType = properties.getProperty("product.type");
if ("ConcreteProductA".equals(productType)) {
return new ConcreteProductA();
} else if ("ConcreteProductB".equals(productType)) {
return new ConcreteProductB();
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
在日常开发中,设计模式中的一些常用模式包括:
- 单例模式 (Singleton):用于确保一个类只有一个实例,并提供全局访问点,例如数据库连接池、日志系统等。
- 工厂模式 (Factory):用于创建对象的接口,但是由子类决定要实例化的类是哪一个。例如,可以用工厂模式创建各种类型的文件解析器。
- 观察者模式 (Observer):用于建立对象之间一对多的依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都会得到通知。例如,GUI界面中的事件监听器。
- 策略模式 (Strategy):定义一系列算法,将每个算法封装起来,并使它们可以互换。例如,根据用户的选择使用不同的支付策略。
- 装饰器模式 (Decorator):动态地给一个对象添加一些额外的职责。例如,在图像处理中可以使用装饰器来添加滤镜效果。
- 模板方法模式 (Template Method):定义一个算法的骨架,允许子类为一个或多个步骤提供实现。例如,在构建流程中的各个阶段都有固定的步骤,但是具体实现可能不同。