大家好,我是CodingLong!
在上一篇《简单工厂模式》中我们了解到,简单工厂模式每次增加新的产品时,都要修改其“工厂类”,这违背了开闭原则。而本篇介绍的工厂方法模式,对“工厂类”进一步抽象,即使新增产品也不用修改原来的代码,这满足了开闭原则。
1 定义
工厂方法模式(Factory Method Pattern)定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
也就是说,我们需要提供一个抽象的工厂接口,声明一个创建对象的工厂方法,由其子类具体实现工厂方法,并决定创建哪一个产品对象。
2 角色
下面是工厂方法模式中的几个角色:
- 抽象产品(Product):所有具体产品的公共接口。
- 具体产品(ConcreteProduct):实现了Product接口,是工厂方法中创建的对象。
- 抽象工厂(Factory):所有具体工厂的公共接口。声明一个工厂方法,该方法返回一个Product类型的对象。
- 具体工厂(ConcreteFactory):实现了Factory接口,工厂方法返回一个ConcreteProduct实例。
- 客户端(Client):客户端通过具体工厂来创建对应的产品实例。
下面是该模式的UML类图:
3 实现
还是以《简单工厂模式》中的手机案例,来讲解该模式的实现。
3.1 抽象产品接口:Phone
创建一个手机接口,它可以打电话和发短信。
public interface Phone {
void call();
void sendSMS();
}
3.2 两个具体产品类:XiaomiPhone和RedmiPhone
为手机接口创建两个实现类,一个是小米手机,一个是红米手机,当然也可以有更多的实现类。
public class XiaomiPhone implements Phone {
@Override
public void call() {
System.out.println("使用小米手机打电话");
}
@Override
public void sendSMS() {
System.out.println("使用小米手机发短信");
}
}
public class RedmiPhone implements Phone {
@Override
public void call() {
System.out.println("使用红米手机打电话");
}
@Override
public void sendSMS() {
System.out.println("使用红米手机发短信");
}
}
3.3 一个工厂接口:PhoneFactory
创建一个手机工厂的接口,包含一个创建Phone的方法。
public interface PhoneFactory {
Phone createPhone();
}
3.4 两个具体工厂类:XiaomiPhoneFactory和RedmiPhoneFactory
创建两个具体手机工厂实现类,每个工厂创建各自的产品实例。
public class XiaomiPhoneFactory implements PhoneFactory {
@Override
public Phone createPhone() {
return new XiaomiPhone();
}
}
public class RedmiPhoneFactory implements PhoneFactory {
@Override
public Phone createPhone() {
return new RedmiPhone();
}
}
3.5 客户端:Client
创建一个客户端,通过XiaomiPhoneFactory工厂创建小米手机;通过RedmiPhoneFactory工厂创建红米手机。
public class Client {
public static void main(String[] args) {
System.out.println("================小米手机================");
Phone xiaomiPhone = new XiaomiPhoneFactory().createPhone();
xiaomiPhone.call();
xiaomiPhone.sendSMS();
System.out.println("================红米手机================");
Phone redmiPhone = new RedmiPhoneFactory().createPhone();
redmiPhone.call();
redmiPhone.sendSMS();
}
}
3.6 运行结果
运行客户端main 方法,会打印如下内容:
================小米手机================
使用小米手机打电话
使用小米手机发短信
================红米手机================
使用红米手机打电话
使用红米手机发短信
4 优缺点
优点:
- 使对象的创建和使用分离,对象的创建交给具体的工厂类负责,客户端不需要关心是怎么创建的,只关心如何使用就行了。
- 一个具体工厂只负责创建一个产品实例,遵循了单一职责。
- 新增产品时,只需创建一个对应的工厂类即可,遵循了开闭原则。
缺点:
- 每增加一个产品,都要增加一个对应的工厂类,类的数量激增。
5 典型应用
我们以Redis连接为例,来看下工厂方法模式在Spring Boot中的应用。
在Java中,连接Redis的两个比较常用的客户端是Jedis和Lettuce,它们都被Spring官方加入到了spring-data-redis 项目中,使用户可以自由的选择使用哪种连接方式。
来看下它的整体结构:
RedisConnection 是Redis连接接口,在工厂方法模式中属于抽象产品角色。它有两个主要实现类:JedisConnection 和LettuceConnection ,是工厂方法中创建的具体产品。
RedisConnectionFactory 是Redis连接工厂接口,在工厂方法模式中属于抽象工厂角色。它有两个实现类:JedisConnectionFactory 和LettuceConnectionFactory 。
在JedisConnectionFactory 工厂中创建的是JedisConnection 对象:
public class JedisConnectionFactory implements InitializingBean, DisposableBean, RedisConnectionFactory {
public RedisConnection getConnection() {
assertInitialized();
if (isRedisClusterAware()) {
return getClusterConnection();
}
Jedis jedis = fetchJedisConnector();
JedisClientConfig sentinelConfig = this.clientConfig;
SentinelConfiguration sentinelConfiguration = getSentinelConfiguration();
if (sentinelConfiguration != null) {
sentinelConfig = createSentinelClientConfig(sentinelConfiguration);
}
JedisConnection connection = (getUsePool() ? new JedisConnection(jedis, pool, this.clientConfig, sentinelConfig)
: new JedisConnection(jedis, null, this.clientConfig, sentinelConfig));
connection.setConvertPipelineAndTxResults(convertPipelineAndTxResults);
return postProcessConnection(connection);
}
}
而在LettuceConnectionFactory 工厂中创建的是LettuceConnection 对象:
public class LettuceConnectionFactory
implements InitializingBean, DisposableBean, RedisConnectionFactory, ReactiveRedisConnectionFactory {
public RedisConnection getConnection() {
assertInitialized();
if (isClusterAware()) {
return getClusterConnection();
}
LettuceConnection connection;
connection = doCreateLettuceConnection(getSharedConnection(), connectionProvider, getTimeout(), getDatabase());
connection.setConvertPipelineAndTxResults(convertPipelineAndTxResults);
return connection;
}
}
6 完整代码
完整代码请访问我的Github,若对你有帮助,欢迎给个?,感谢~~🌹🌹🌹
https://github.com/gozhuyinglong/blog-demos/blob/main/design-patterns/src/main/java/io/github/gozhuyinglong/designpatterns/factory/FactoryMethod.java
7 推荐阅读
微信搜索:码农StayUp
主页地址:https://gozhuyinglong.github.io
源码分享:https://github.com/gozhuyinglong/blog-demos
|