工廠模式
工廠模式是開發中常用的一種設計模式,每一種設計模式都會極大的解決程序設計方面的問題,工廠模式也是一樣,本文將會用通俗的語言來解釋什么是工廠模式?工廠模式的種類、代碼示例、每種工廠模式的優缺點和工廠模式適用的場景。
為什么要是使用工廠模式?
首先我們用一個生動故事來描述下什么是工廠模式,這會讓你更快的理解工廠模式,為后面理解的工廠模式的幾種實現方式打下基礎。
假如,你需要讓公司開一個收入證明為自己貸款買房提供收入證明,一般開收入證明的過程是:
- 打印收入證明;
- 在收入證明上蓋上公司的章;
貫穿整個過程,可以知道,你需要創建一個收入證明,并使用收入證明為貸款買房提供資料。這個過程中,有兩個關鍵的行為:創建收入證明,使用收入證明。創建收入證明的過程中又分為兩步:打印、蓋章。不熟悉這個流程的同事在創建收入證明的時候,往往會遇到很多麻煩導致開收入證明頻頻受阻,于是公司決定把員工開收入證明這件事做個優化,以前需要兩步做的事,現在只需要發一份郵件給財務部門,財務部門就會在下班之前就會把蓋好章的收入證明送到工位,這就是生活中遇到的工廠模式
。
說了這么多,工廠模式
究竟解決了生活中的哪些問題呢?
在這個生活案例中,它讓員工創建收入證明這件事情變得更加簡單,員工不需要知道收入證明是怎么創建的,只需要發一份郵件給財務部門,財務部門就會幫助員工打印并蓋章。而且在后續的公司發展中,如果需要在收入證明中蓋上更多的章,員工也不需要自己取熟悉整個流程,拿著收入證明跑斷腿的到各個部門去蓋章。
那么,在程序的世界中,如何使用代碼演示員工為貸款買房,去公司開收入證明這個行為呢?又是如何用代碼展示工廠模式的呢?
類比上面的例子,假如有個收入證明類
創建只需要new一下就行,入參是打印證明
和給證明蓋章
,如果后續收入證明的蓋章變了,需要修改入參。這就需要改大量調用創建收入證明類
的new代碼。
于是這時工廠模式就可以使用了,工廠模式將類的創建和類的使用分離出來,當 Class A 想調用 Class B ,那么A只是調用B的方法,而至于B的實例化,就交給工廠類。
那又有人說,也可以把這些創建過程的代碼放到類的構造函數里,同樣可以降低重復率,而且構造函數本身的作用也是初始化對象。針對這個觀點,我們可以對比下工廠模式相較于構造函數的優點:
優點:
靜態工廠方法有名字而構造函數沒有,因為工廠方法可以有多個,通過名字來區別各個方法,但構造函數名字只能有一個,只能通過參數來區別,所以使用靜態工廠方法更明了。
靜態工廠方法支持條件性實例化,就是說你創建對象時,有時需要添加一些條件判斷是否應該創建,如果滿足條件則返回一個實例,不滿足則返回NULL,比如單例模式。構造函數時做不到這樣的,構造函數的功能需要保持單一,只為了構造而存在,而靜態工廠方法可以很簡單的做到。
方法能夠返回返回類型的子類型
這就是工廠模式在創建對象上的一些優點,而工廠模式最核心的知識點是:將對象的創建和使用做分離。請默念三遍。
抓住了核心點,再去了解工廠模式的各種實現就簡單的多了,工廠模式一般可以分為三種:
- 簡單/靜態工廠模式
- 工廠方法模式
- 抽象工廠模式
我們學習步驟按照:工廠方法模式 到 簡單靜態工廠模式 到 抽象工廠模式,示例都非常簡單,一看就懂,文中的代碼我放在github上感興趣的同學可以下載:
github地址: java23種設計模式代碼示例--工廠模式
我知道大家都是愛心人士,白嫖當然不是你們的習慣,歡迎大家來一波素質三連:關注、點贊、點star
工廠方法模式
工廠方法相對比較和簡單\靜態工廠相對簡單比較容易理解。
如果我們需要在工廠里造一臺手機,那么先定義一個phone抽象類,手機必須要有打電話功能,加一個call抽象方法
package com.shuai.design.factory.normals;
public abstract class Phone {
// 所有的手機必須要有打電話的功能
public abstract void call();
}
創建一個手機的工廠接口,里面有個createPhone方法,其他類型手機都要繼承這個接口,創建手機必須實現這個方法
package com.shuai.design.factory.normals;
import com.shuai.design.factory.simples.Phone;
public interface PhoneFactory {
Phone createPhone();
}
創建小米手機類。繼承phone,實現call方法
package com.shuai.design.factory.normals;
import com.shuai.design.factory.simples.Phone;
public class MiPhone extends Phone {
@Override
public void call() {
System.out.println("this is MI call");
}
}
創建小米手機工廠
package com.shuai.design.factory.normals;
public class MiPhoneFactory implements PhoneFactory {
@Override
public MiPhone createPhone() {
return new MiPhone();
}
}
類似小米手機,實現華為手機類,繼承phoen抽象類,實現call方法
package com.shuai.design.factory.normals;
import com.shuai.design.factory.simples.Phone;
public class HuaWeiPhone extends Phone {
@Override
public void call() {
System.out.println("this is HuaWei Phone");
}
}
實現華為手機工廠
package com.shuai.design.factory.normals;
public class HuaWeiPhoneFactory implements PhoneFactory{
@Override
public HuaWeiPhone createPhone() {
return new HuaWeiPhone();
}
}
簡單靜態工廠模式
靜態工廠相對于工廠方法模式簡單的多,首先創建phone的抽象類
package com.shuai.design.factory.simples;
public abstract class Phone {
// 所有的手機必須要有打電話的功能
public abstract void call();
}
再創建phone的構建工廠類,根據傳入的類型創建不同類型的手機
package com.shuai.design.factory.simples;
public class PhoneFactory {
public static MiPhone createMiPhone() {
return new MiPhone();
}
public static HuaWeiPhone createHuaWeiPhone() {
return new HuaWeiPhone();
}
public static Phone createPhone(String type) {
if ("Mi".equals(type)) {
return new MiPhone();
} else {
return new HuaWeiPhone();
}
}
}
實現小米手機類
package com.shuai.design.factory.simples;
public class MiPhone extends Phone {
@Override
public void call() {
System.out.println("this is MI call");
}
}
實現華為手機類
package com.shuai.design.factory.simples;
public class HuaWeiPhone extends Phone {
@Override
public void call() {
System.out.println("this is HuaWei Phone");
}
}
抽象工廠模式
定義:為創建一組相關或相互依賴的對象提供一個接口,而且無須指定它們的具體類
圖為:
這里還是用創建手機的方式來實現抽象工廠,先創建一個phone的抽象類,里面不僅有call方法,還有手機的屏幕類型
package com.shuai.design.factory.abstracts;
public abstract class Phone {
// 所有的手機必須要有打電話的功能
public abstract void call();
// 手機的屏幕類型
public abstract void screenType();
}
再創建一個手機構建工廠的父接口
package com.shuai.design.factory.abstracts;
public interface PhoneFactory {
Phone createMiPhone();
Phone createHuaWeiPhone();
}
首先所有小米手機和華為手機都必須實現通話功能,重寫call方法
package com.shuai.design.factory.abstracts;
public abstract class MiPhone extends Phone {
@Override
public void call() {
System.out.println("this is MI call");
}
}
package com.shuai.design.factory.abstracts;
public abstract class HuaWeiPhone extends Phone {
@Override
public void call() {
System.out.println("this is HuaWei Phone");
}
}
在根據手機的屏幕類型分為小米折疊屏手機:
package com.shuai.design.factory.abstracts;
public class FoldingScreenMiPhone extends MiPhone {
@Override
public void screenType() {
System.out.println("this is 小米折疊屏手機");
}
}
在根據手機的屏幕類型分為華為折疊屏手機:
package com.shuai.design.factory.abstracts;
public class FoldingScreenHuaWeiPhone extends HuaWeiPhone {
@Override
public void screenType() {
System.out.println("this is 華為折疊屏手機");
}
}
在根據手機的屏幕類型分為小米曲面屏屏手機:
package com.shuai.design.factory.abstracts;
public class CurvedScreenMiPhone extends MiPhone {
@Override
public void screenType() {
System.out.println("this is 小米曲面屏手機");
}
}
在根據手機的屏幕類型分為華為曲面屏手機:
package com.shuai.design.factory.abstracts;
public class CurvedScreenHuaWeiPhone extends HuaWeiPhone{
@Override
public void screenType(){
System.out.println("this is 華為曲面屏手機");
}
}
針對不同的屏幕類型手機,抽象出根據屏幕類型的構建工廠,有曲面屏手機工廠和折疊屏手機工廠:
package com.shuai.design.factory.abstracts;
//曲面屏手機工廠
public class CurvedScreenPhoneFactory implements PhoneFactory {
@Override
public CurvedScreenMiPhone createMiPhone() {
return new CurvedScreenMiPhone();
}
@Override
public CurvedScreenHuaWeiPhone createHuaWeiPhone() {
return new CurvedScreenHuaWeiPhone();
}
}
折疊屏手機工廠
package com.shuai.design.factory.abstracts;
//折疊屏手機工廠
public class FoldingScreenPhoneFactory implements PhoneFactory{
@Override
public FoldingScreenMiPhone createMiPhone() {
return new FoldingScreenMiPhone();
}
@Override
public FoldingScreenHuaWeiPhone createHuaWeiPhone() {
return new FoldingScreenHuaWeiPhone();
}
}
再寫一個測試類測試一下結果:
package com.shuai.design.factory.abstracts;
public class Test {
public static void main(String[] args) {
// 創建一個華為曲面屏手機
CurvedScreenHuaWeiPhone curvedScreenHuaWeiPhone = new CurvedScreenPhoneFactory().createHuaWeiPhone();
System.out.println("curvedScreenHuaWeiPhone 的手機類型為:");
curvedScreenHuaWeiPhone.screenType();
// 創建一個小米曲面屏手機
CurvedScreenMiPhone curvedScreenMiPhone = new CurvedScreenPhoneFactory().createMiPhone();
System.out.println("curvedScreenMiPhone 的手機類型為:");
curvedScreenMiPhone.screenType();
// 創建一個華為折疊屏手機
FoldingScreenHuaWeiPhone foldingScreenHuaWeiPhone = new FoldingScreenPhoneFactory().createHuaWeiPhone();
System.out.println("foldingScreenHuaWeiPhone 的手機類型為:");
foldingScreenHuaWeiPhone.screenType();
// 創建一個小米折疊屏手機
FoldingScreenMiPhone foldingScreenMiPhone = new FoldingScreenPhoneFactory().createMiPhone();
System.out.println("foldingScreenMiPhone 的手機類型為:");
foldingScreenMiPhone.screenType();
}
}
抽象工廠模式的使用場景:一個對象族(或是一組沒有任何關系的對象)都有相同的約束,則可以使用抽象工廠模式。通過工廠類,只要知道工廠類是誰,我就能創建出一個需要的對象
缺點 :
擴展產品族困難。比如在phone中類型增加一個帶手寫筆類型的手機,那么每個已經實現的手機類就都需要實現這個方法。這嚴重違反了開閉原則。
優點:
增加等級簡單。如果在折疊屏手機下增加一個雙折疊屏和三折疊屏的手機這就比較簡單,只需要在折疊屏手機構建工廠下面修改就行。
總結
實際上,一般開發過程中,我們使用簡單工廠模式比較多,抽象工廠模式的話需要業務比較大的情況下才會用到。如果你有更好的觀點,歡迎在評論區提出,互相學習。
參考資料:
- 《工廠模式理解了沒有?(https://segmentfault.com/a/1190000014949595)》
- 《設計模式系列-Builder模式,工廠方法模式和抽象工廠模式》
- 《產品等級結構與產品族》
歡迎關注公眾號:java之旅