建延遲到子類)
工廠方法模式把創(chuàng)建延遲到子類簡單工廠把所有產(chǎn)品的創(chuàng)建邏輯塞進(jìn)一個(gè)工廠類新增產(chǎn)品必須修改工廠。工廠方法模式換了個(gè)思路不再用一個(gè)工廠創(chuàng)建所有產(chǎn)品而是為每種產(chǎn)品定義一個(gè)工廠讓子類決定創(chuàng)建什么。這就是 GoF 23 種設(shè)計(jì)模式中的工廠方法模式。它比簡單工廠更符合開閉原則代價(jià)是類的數(shù)量成倍增加。結(jié)構(gòu)工廠方法模式包含四個(gè)角色抽象產(chǎn)品Product定義產(chǎn)品的接口具體產(chǎn)品Concrete Product實(shí)現(xiàn)產(chǎn)品接口的具體類抽象工廠Creator聲明工廠方法返回抽象產(chǎn)品具體工廠Concrete Creator實(shí)現(xiàn)工廠方法返回具體產(chǎn)品抽象工廠中通常還包含一些依賴產(chǎn)品的業(yè)務(wù)邏輯這些邏輯調(diào)用工廠方法獲取產(chǎn)品對象但不關(guān)心具體是哪個(gè)產(chǎn)品。代碼實(shí)現(xiàn)還是用支付作為例子。抽象產(chǎn)品publicinterfacePayment{voidpay(doubleamount);}具體產(chǎn)品publicclassAliPayimplementsPayment{Overridepublicvoidpay(doubleamount){System.out.println(支付寶支付amount);}}publicclassWechatPayimplementsPayment{Overridepublicvoidpay(doubleamount){System.out.println(微信支付amount);}}抽象工廠publicabstractclassPaymentFactory{// 工廠方法由子類實(shí)現(xiàn)publicabstractPaymentcreatePayment();// 業(yè)務(wù)邏輯依賴工廠方法創(chuàng)建的產(chǎn)品publicvoidprocessPayment(doubleamount){PaymentpaymentcreatePayment();// 可以在這里加統(tǒng)一的處理邏輯比如日志、校驗(yàn)payment.pay(amount);}}具體工廠publicclassAliPayFactoryextendsPaymentFactory{OverridepublicPaymentcreatePayment(){returnnewAliPay();}}publicclassWechatPayFactoryextendsPaymentFactory{OverridepublicPaymentcreatePayment(){returnnewWechatPay();}}調(diào)用方publicclassClient{publicstaticvoidmain(String[]args){PaymentFactoryfactorynewAliPayFactory();factory.processPayment(100);}}調(diào)用方只依賴PaymentFactory抽象類和Payment接口完全不接觸具體類。與簡單工廠的關(guān)鍵區(qū)別簡單工廠是一個(gè)工廠類創(chuàng)建所有產(chǎn)品工廠方法把創(chuàng)建延遲到子類每種產(chǎn)品對應(yīng)一個(gè)工廠類。新增一種支付方式時(shí)簡單工廠修改PaymentFactory的switch加一個(gè)case。工廠方法新增UnionPayFactory類不動(dòng)任何已有代碼。前者違反開閉原則后者符合。這是工廠方法最核心的優(yōu)勢。對比維度簡單工廠工廠方法工廠類數(shù)量1 個(gè)N 個(gè)每個(gè)產(chǎn)品一個(gè)新增產(chǎn)品修改工廠類新增工廠類開閉原則違反符合代碼復(fù)雜度低中適用場景產(chǎn)品少且穩(wěn)定產(chǎn)品多且需擴(kuò)展抽象工廠中的業(yè)務(wù)邏輯工廠方法不只是“返回一個(gè)對象”。抽象工廠里通常還有依賴產(chǎn)品的業(yè)務(wù)方法這些方法調(diào)用工廠方法獲取產(chǎn)品再執(zhí)行通用邏輯。publicabstractclassPaymentFactory{publicabstractPaymentcreatePayment();publicvoidprocessPayment(doubleamount){// 通用邏輯校驗(yàn)、日志、記錄時(shí)間System.out.println(開始處理支付...);PaymentpaymentcreatePayment();payment.pay(amount);System.out.println(支付處理完成);}}子類只需要實(shí)現(xiàn)createPayment()通用的處理流程由抽象類統(tǒng)一定義。這是模板方法模式和工廠方法模式的經(jīng)典組合——抽象類定義流程骨架子類填充具體產(chǎn)品的創(chuàng)建邏輯。何時(shí)用工廠方法產(chǎn)品種類會持續(xù)擴(kuò)展。如果未來會不斷增加新的產(chǎn)品類型工廠方法比簡單工廠更合適因?yàn)閿U(kuò)展成本更低。需要把創(chuàng)建和使用分離到不同層次??蚣茉O(shè)計(jì)常用工廠方法讓框架定義抽象工廠具體產(chǎn)品由使用方通過子類提供。產(chǎn)品創(chuàng)建有復(fù)雜邏輯。每個(gè)產(chǎn)品可能有不同的初始化步驟放在各自的工廠類里更清晰。不希望調(diào)用方依賴具體產(chǎn)品類。調(diào)用方只認(rèn)識抽象工廠和抽象產(chǎn)品具體類完全隱藏。何時(shí)不用產(chǎn)品種類少且穩(wěn)定。如果只有兩三種產(chǎn)品未來也不會增加簡單工廠足夠工廠方法反而增加了不必要的類。不需要擴(kuò)展點(diǎn)。如果整個(gè)系統(tǒng)只有一處地方需要?jiǎng)?chuàng)建對象抽象出工廠類意義不大。類和接口已經(jīng)很多了。工廠方法會讓類數(shù)量翻倍如果項(xiàng)目本身結(jié)構(gòu)復(fù)雜要謹(jǐn)慎權(quán)衡。在 Spring 中的體現(xiàn)Spring 的FactoryBean接口是工廠方法模式的一個(gè)變體publicinterfaceFactoryBeanT{TgetObject()throwsException;Class?getObjectType();booleanisSingleton();}getObject()就是工廠方法。你可以通過實(shí)現(xiàn)FactoryBean來定制復(fù)雜對象的創(chuàng)建過程Spring 容器在getBean()時(shí)會調(diào)用getObject()返回真正的對象。Spring 的BeanFactory體系本身也是工廠模式的集大成者。BeanFactory是抽象工廠XmlBeanFactory、AnnotationConfigApplicationContext等是具體工廠它們負(fù)責(zé)創(chuàng)建和管理 Bean。一個(gè)容易踩的坑抽象工廠里的工廠方法不要在構(gòu)造方法或字段初始化時(shí)調(diào)用。因?yàn)樽宇悩?gòu)造方法執(zhí)行時(shí)父類構(gòu)造方法先執(zhí)行此時(shí)子類的字段還沒初始化createPayment()可能拿到不完整的對象。publicabstractclassPaymentFactory{privatePaymentdefaultPaymentcreatePayment();// ? 危險(xiǎn)publicabstractPaymentcreatePayment();}正確做法是在業(yè)務(wù)方法中調(diào)用而不是在構(gòu)造階段。總結(jié)維度說明核心思想定義創(chuàng)建對象的接口讓子類決定實(shí)例化哪個(gè)類角色抽象產(chǎn)品、具體產(chǎn)品、抽象工廠、具體工廠與簡單工廠的區(qū)別每種產(chǎn)品一個(gè)工廠擴(kuò)展時(shí)新增類而非修改已有代碼優(yōu)點(diǎn)符合開閉原則創(chuàng)建與使用分離職責(zé)清晰缺點(diǎn)類數(shù)量增加抽象層次提高典型組合與模板方法模式配合抽象類定義流程子類實(shí)現(xiàn)創(chuàng)建Spring 體現(xiàn)FactoryBean、BeanFactory體系工廠方法模式解決的是“新增產(chǎn)品要改工廠”的問題代價(jià)是多了一層繼承體系。當(dāng)產(chǎn)品種類會持續(xù)增長或者需要把創(chuàng)建邏輯下放到子類時(shí)它是比簡單工廠更合適的選擇。