工廠方法模式:在 Spring Boot 中的典型應(yīng)用
大家好,我是CodingLong!
在上一篇《簡單工廠模式》中我們了解到,簡單工廠模式每次增加新的產(chǎn)品時,都要修改其“工廠類”,這違背了開閉原則。而本篇介紹的工廠方法模式,對“工廠類”進一步抽象,即使新增產(chǎn)品也不用修改原來的代碼,這滿足了開閉原則。
1 定義
工廠方法模式(Factory Method Pattern)定義一個用于創(chuàng)建對象的接口,讓子類決定實例化哪一個類。工廠方法使一個類的實例化延遲到其子類。
Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
也就是說,我們需要提供一個抽象的工廠接口,聲明一個創(chuàng)建對象的工廠方法,由其子類具體實現(xiàn)工廠方法,并決定創(chuàng)建哪一個產(chǎn)品對象。
2 角色
下面是工廠方法模式中的幾個角色:
抽象產(chǎn)品(Product):所有具體產(chǎn)品的公共接口。 具體產(chǎn)品(ConcreteProduct):實現(xiàn)了Product接口,是工廠方法中創(chuàng)建的對象。 抽象工廠(Factory):所有具體工廠的公共接口。聲明一個工廠方法,該方法返回一個Product類型的對象。 具體工廠(ConcreteFactory):實現(xiàn)了Factory接口,工廠方法返回一個ConcreteProduct實例。 客戶端(Client):客戶端通過具體工廠來創(chuàng)建對應(yīng)的產(chǎn)品實例。
下面是該模式的UML類圖:

3 實現(xiàn)
還是以《簡單工廠模式》中的手機案例,來講解該模式的實現(xiàn)。
3.1 抽象產(chǎn)品接口:Phone
創(chuàng)建一個手機接口,它可以打電話和發(fā)短信。
/**
?*?手機接口
?*/
public?interface?Phone?{
????/**
?????*?打電話
?????*/
????void?call();
????/**
?????*?發(fā)短信
?????*/
????void?sendSMS();
}
3.2 兩個具體產(chǎn)品類:XiaomiPhone和RedmiPhone
為手機接口創(chuàng)建兩個實現(xiàn)類,一個是小米手機,一個是紅米手機,當然也可以有更多的實現(xiàn)類。
/**
?*?小米手機實現(xiàn)類
?*/
public?class?XiaomiPhone?implements?Phone?{
????@Override
????public?void?call()?{
????????System.out.println("使用小米手機打電話");
????}
????@Override
????public?void?sendSMS()?{
????????System.out.println("使用小米手機發(fā)短信");
????}
}
/**
?*?紅米手機實現(xiàn)類
?*/
public?class?RedmiPhone?implements?Phone?{
????@Override
????public?void?call()?{
????????System.out.println("使用紅米手機打電話");
????}
????@Override
????public?void?sendSMS()?{
????????System.out.println("使用紅米手機發(fā)短信");
????}
}
3.3 一個工廠接口:PhoneFactory
創(chuàng)建一個手機工廠的接口,包含一個創(chuàng)建Phone的方法。
/**
?*?手機工廠接口
?*/
public?interface?PhoneFactory?{
????/**
?????*?獲取手機實例
?????*
?????*?@return
?????*/
????Phone?createPhone();
}
3.4 兩個具體工廠類:XiaomiPhoneFactory和RedmiPhoneFactory
創(chuàng)建兩個具體手機工廠實現(xiàn)類,每個工廠創(chuàng)建各自的產(chǎn)品實例。
/**
?*?小米手機工廠類
?*/
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
創(chuàng)建一個客戶端,通過XiaomiPhoneFactory工廠創(chuàng)建小米手機;通過RedmiPhoneFactory工廠創(chuàng)建紅米手機。
/**
?*?客戶端類
?*/
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 運行結(jié)果
運行客戶端main方法,會打印如下內(nèi)容:
================小米手機================
使用小米手機打電話
使用小米手機發(fā)短信
================紅米手機================
使用紅米手機打電話
使用紅米手機發(fā)短信
4 優(yōu)缺點
優(yōu)點:
使對象的創(chuàng)建和使用分離,對象的創(chuàng)建交給具體的工廠類負責,客戶端不需要關(guān)心是怎么創(chuàng)建的,只關(guān)心如何使用就行了。 一個具體工廠只負責創(chuàng)建一個產(chǎn)品實例,遵循了單一職責。 新增產(chǎn)品時,只需創(chuàng)建一個對應(yīng)的工廠類即可,遵循了開閉原則。
缺點:
每增加一個產(chǎn)品,都要增加一個對應(yīng)的工廠類,類的數(shù)量激增。
5 典型應(yīng)用
我們以Redis連接為例,來看下工廠方法模式在Spring Boot中的應(yīng)用。
在Java中,連接Redis的兩個比較常用的客戶端是Jedis和Lettuce,它們都被Spring官方加入到了spring-data-redis項目中,使用戶可以自由的選擇使用哪種連接方式。
來看下它的整體結(jié)構(gòu):
RedisConnection是Redis連接接口,在工廠方法模式中屬于抽象產(chǎn)品角色。它有兩個主要實現(xiàn)類:JedisConnection和LettuceConnection,是工廠方法中創(chuàng)建的具體產(chǎn)品。
RedisConnectionFactory是Redis連接工廠接口,在工廠方法模式中屬于抽象工廠角色。它有兩個實現(xiàn)類:JedisConnectionFactory和LettuceConnectionFactory。
在JedisConnectionFactory工廠中創(chuàng)建的是JedisConnection對象:
public?class?JedisConnectionFactory?implements?InitializingBean,?DisposableBean,?RedisConnectionFactory?{
??????//...
??????
??????/*
???????*?獲取Redis連接
???????*/
??????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工廠中創(chuàng)建的是LettuceConnection對象:
public?class?LettuceConnectionFactory
????implements?InitializingBean,?DisposableBean,?RedisConnectionFactory,?ReactiveRedisConnectionFactory?{
??????//?...
??????/*
???????*?獲取Redis連接
???????*/
??????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 推薦閱讀
微信搜索:碼農(nóng)StayUp
主頁地址:https://gozhuyinglong.github.io
源碼分享:https://github.com/gozhuyinglong/blog-demos
