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