国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

聊一聊模板方法模式

這篇具有很好參考價(jià)值的文章主要介紹了聊一聊模板方法模式。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

統(tǒng)一抽取,制定規(guī)范;

一、概述

模板方法模式,又叫模板模式,屬于23種設(shè)計(jì)模式中的行為型模式。在抽象類中公開定義了執(zhí)行的方法,子類可以按需重寫其方法,但是要以抽象類中定義的方式調(diào)用方法??偨Y(jié)起來就是:定義一個(gè)操作的算法結(jié)構(gòu),而將一些步驟延遲到子類中。在不改變算法結(jié)構(gòu)的情況下,子類能重定義該算法的特定步驟。

下面是模板模式的UML圖,抽象類(AbstractClass)定義公共的步驟和方法,依次調(diào)用實(shí)際的模板方法,當(dāng)然每個(gè)方法可以是抽象方法(需交給子類實(shí)現(xiàn)),也可以是提供默認(rèn)的方法。具體的類(ConcreteClass)可以重寫所有的方法,但是不能改變抽象類中定義的整體結(jié)構(gòu)。

聊一聊模板方法模式

二、入門案例

相信大家都吃過蛋糕,現(xiàn)在市面上的蛋糕可謂是五花八門,你能想到的造型商家能給你整出來,你想不到的,他們也能整出來。不過無論造型如何變化,不變的有兩種東西:“奶油”和“面包”。其余的材料隨意搭配,就湊成了各式各樣的蛋糕。

聊一聊模板方法模式

基于這個(gè)場(chǎng)景,我們來寫一個(gè)案例,進(jìn)一步了解下模板模式;創(chuàng)建三個(gè)類:Cake(蛋糕)、StrawberryCake(草莓蛋糕)、CherryCake(櫻桃蛋糕)。最后創(chuàng)建一個(gè)Client類,實(shí)現(xiàn)這個(gè)制作蛋糕的調(diào)用過程。

package com.wsrf.template;

/**
 * @author 往事如風(fēng)
 * @version 1.0
 * @date 2023/5/4 16:12
 * @description:抽象類:蛋糕
 */
public abstract class Cake {

    /**
     * 制作
     */
    public void make() {
        System.out.println("開始準(zhǔn)備材料。");
        bread();
        cream();
        fruit();
        System.out.println("經(jīng)過一系列的操作。");
        System.out.println("制作完成。");
    }

    /**
     * 準(zhǔn)備面包
     */
    public void bread() {
        System.out.println("準(zhǔn)備材料:面包");
    }

    /**
     * 準(zhǔn)備奶油
     */
    public void cream() {
        System.out.println("準(zhǔn)備材料:奶油");
    }

    /**
     * 準(zhǔn)備水果
     */
    protected abstract void fruit();

}
package com.wsrf.template;

/**
 * @author 往事如風(fēng)
 * @version 1.0
 * @date 2023/5/4 16:13
 * @description:具體類:草莓蛋糕
 */
public class StrawberryCake extends Cake{
    @Override
    protected void fruit() {
        System.out.println("準(zhǔn)備材料:草莓");
    }
}
package com.wsrf.template;

/**
 * @author 往事如風(fēng)
 * @version 1.0
 * @date 2023/5/4 16:14
 * @description:具體類:櫻桃蛋糕
 */
public class CherryCake extends Cake{
    @Override
    protected void fruit() {
        System.out.println("準(zhǔn)備材料:櫻桃");
    }
}
package com.wsrf.template;

/**
 * @author 往事如風(fēng)
 * @version 1.0
 * @date 2023/5/4 16:21
 * @description
 */
public class Client {

    public static void main(String[] args) {
        Cake c1 = new CherryCake();
        c1.make();
        System.out.println("-------------------------------------");
        Cake c2 = new StrawberryCake();
        c2.make();
    }
}
/**
輸出結(jié)果:
開始準(zhǔn)備材料。
準(zhǔn)備材料:面包
準(zhǔn)備材料:奶油
準(zhǔn)備材料:櫻桃
經(jīng)過一系列的操作。
制作完成。
-------------------------------------
開始準(zhǔn)備材料。
準(zhǔn)備材料:面包
準(zhǔn)備材料:奶油
準(zhǔn)備材料:草莓
經(jīng)過一系列的操作。
制作完成。
*/

Cake類中定義了制作蛋糕的整個(gè)步驟,也就是make方法;然后抽取了公用的方法,bread方法和cream方法;最后定義一個(gè)抽象方法fruit,這個(gè)方法需要交給具體的子類StrawberryCakeCherryCake去實(shí)現(xiàn),從而定制差異化的“蛋糕”。

三、運(yùn)用場(chǎng)景

通過上面的“蛋糕”案例,在平時(shí)開發(fā)中我們可以具體分析一下業(yè)務(wù)需求,首先在父類中定義需求需要實(shí)現(xiàn)的步驟,然后將可以公用的方法抽取到父類中,將個(gè)性化的方法放到具體的子類中去實(shí)現(xiàn);這樣可以很好的培養(yǎng)“抽象化”的思維模式,這是拉開差距的第一步。

最近在開發(fā)中,遇到這樣的一個(gè)業(yè)務(wù)場(chǎng)景:需要給不同的管理人員計(jì)算各種不同的津貼,如區(qū)域總監(jiān)有區(qū)域管理津貼、傭金、培養(yǎng)育成津貼等等。通過分析,每種不用類型的津貼,都是需要金額x比例x系數(shù),比例每種津貼都有不同的計(jì)算方式,系數(shù)也是。所以,大致的想法就是:金額x比例x系數(shù)這個(gè)計(jì)算方式設(shè)置為統(tǒng)一的方法,系數(shù)和比例讓具體的津貼子類去實(shí)現(xiàn)。所以大致的偽代碼如下;

首先,我定義了一個(gè)抽象類AbstractManageAllowanceCalService,用于定義統(tǒng)一的計(jì)算方法,并預(yù)留了獲取比例和獲取系數(shù)的抽象方法。

/**
 * @author 往事如風(fēng)
 * @version 1.0
 * @date 2023/5/4 17:12
 * @description:津貼計(jì)算父類
 */
@Slf4j
public abstract class AbstractManageAllowanceCalService {

    /**
     * 計(jì)算津貼
     * @param amount
     * @return
     */
    public BigDecimal calAmount(BigDecimal amount) {
        if (Objects.isNull(amount)) {
            return BigDecimal.ZERO;
        }
        BigDecimal ratio = getRatio();
        BigDecimal coefficient = getCoefficient();
        log.info("金額:{},系數(shù):{},比例:{}", amount, coefficient, ratio);
        return amount.multiply(ratio).multiply(coefficient);
    }

    /**
     * 獲取比例
     * @return
     */
    protected abstract BigDecimal getRatio();

    /**
     * 獲取系數(shù)
     * @return
     */
    protected abstract BigDecimal getCoefficient();
}

然后,定義兩個(gè)具體的子類,用于計(jì)算區(qū)域管理津貼和傭金。

/**
 * @author 往事如風(fēng)
 * @version 1.0
 * @date 2023/5/4 17:17
 * @description:區(qū)域管理津貼計(jì)算
 */
@Service
public class AreaBusinessAllowanceCalService extends AbstractManageAllowanceCalService{
    /**
     * 區(qū)域管理津貼比例
     * @return
     */
    @Override
    protected BigDecimal getRatio() {
        return new BigDecimal(0.5).setScale(1, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 區(qū)域管理津貼系數(shù)
     * @return
     */
    @Override
    protected BigDecimal getCoefficient() {
        return new BigDecimal(0.92).setScale(2, BigDecimal.ROUND_HALF_UP);
    }
}
/**
 * @author 往事如風(fēng)
 * @version 1.0
 * @date 2023/5/4 17:19
 * @description:傭金計(jì)算
 */
@Service
public class SalaryCalService extends AbstractManageAllowanceCalService{
    /**
     * 傭金比例
     * @return
     */
    @Override
    protected BigDecimal getRatio() {
        return new BigDecimal(0.45).setScale(2, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 傭金系數(shù)
     * @return
     */
    @Override
    protected BigDecimal getCoefficient() {
        return new BigDecimal(0.88).setScale(2, BigDecimal.ROUND_HALF_UP);
    }
}

最后,定義一個(gè)controller類,用于接口調(diào)用,提供計(jì)算能力;接收兩個(gè)參數(shù),金額和計(jì)算津貼類型。

/**
 * @author 往事如風(fēng)
 * @version 1.0
 * @date 2023/5/4 17:21
 * @description
 */
@RestController
@RequestMapping("/cal")
public class CalController implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @PostMapping("/amount")
    public Result<BigDecimal> calAmount(BigDecimal amount, String calType) {
        AbstractManageAllowanceCalService service = null;
        if ("AREA".equals(calType)) {
            // 區(qū)域管理津貼
             service = (AbstractManageAllowanceCalService) applicationContext.getBean("areaBusinessAllowanceCalService");
        } else if ("SALARY".equals(calType)) {
            // 傭金
            service = (AbstractManageAllowanceCalService) applicationContext.getBean("salaryCalService");
        }
        if (Objects.nonNull(service)) {
            return Result.success(service.calAmount(amount));
        }
        return Result.fail();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        CalController.applicationContext = applicationContext;
    }
}

在這個(gè)controller類中,我通過分析“類型”這個(gè)參數(shù),來判斷需要調(diào)用哪個(gè)service去實(shí)現(xiàn)具體的計(jì)算邏輯。這里用了if-else的方式去實(shí)現(xiàn);其實(shí)也可以用到另一個(gè)設(shè)計(jì)模式——策略模式,這樣寫出來的代碼就會(huì)比較優(yōu)雅,這里就不對(duì)策略模式展開贅述了。

四、源碼中運(yùn)用

4.1、JDK源碼中的模板模式

在JDK中其實(shí)也有很多地方運(yùn)用到了模板模式,這里咱挑一個(gè)講。并發(fā)包下的AbstractQueuedSynchronizer類,就是一個(gè)抽象類,也就是我們先前的文章中提到過的AQS。

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
	
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

	public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

    protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }

    protected boolean tryRelease(int arg) {
        throw new UnsupportedOperationException();
    }
}        

其中,tryAcquire和tryRelease這兩個(gè)方式直接拋了異常,用protected關(guān)鍵詞修飾,需要由子類去實(shí)現(xiàn)。然后再acquire和release方法中分別去調(diào)用這兩方法。也就是acquire方法定義了一個(gè)統(tǒng)一的結(jié)構(gòu),差異化的tryAcquire方法需要具體的子類去實(shí)現(xiàn)功能,實(shí)現(xiàn)了模板模式。

4.2、Spring源碼中的模板模式

說到源碼,Spring是一個(gè)繞不開的話題,那就來學(xué)習(xí)下Spring中的模板模式。其中,有一個(gè)類DefaultBeanDefinitionDocumentReader,它是BeanDefinitionDocumentReader的實(shí)現(xiàn)類,是提取spring配置文件中的bean信息,并轉(zhuǎn)化為BeanDefinition。

public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
	
    protected void doRegisterBeanDefinitions(Element root) {
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = this.createDelegate(this.getReaderContext(), root, parent);
        //...

        this.preProcessXml(root);
        this.parseBeanDefinitions(root, this.delegate);
        this.postProcessXml(root);
        this.delegate = parent;
    }

    protected void preProcessXml(Element root) {
    }

    protected void postProcessXml(Element root) {
    }
}    

這里我截圖了其中的一段代碼,主要是doRegisterBeanDefinitions這個(gè)方法,從跟節(jié)點(diǎn)root出發(fā),root下的每個(gè)bean注冊(cè)定義。

該方法中還調(diào)用了preProcessXml和postProcessXml這兩個(gè)方法,但是在DefaultBeanDefinitionDocumentReader類中,這兩個(gè)方法是未實(shí)現(xiàn)的,需要其子類去實(shí)現(xiàn)具體的邏輯。所以,這里也是一個(gè)很典型的模板模式的運(yùn)用。

五、總結(jié)

模板方法模式其實(shí)是一個(gè)比較簡(jiǎn)單的設(shè)計(jì)模式,它有如下優(yōu)點(diǎn):1、封裝不變的邏輯,擴(kuò)展差異化的邏輯;2、抽取公共代碼,提高代碼的復(fù)用性;3、父類控制行為,子類實(shí)現(xiàn)細(xì)節(jié)。

其缺點(diǎn)就是不同的實(shí)現(xiàn)都需要一個(gè)子類去維護(hù),會(huì)導(dǎo)致子類的個(gè)數(shù)不斷增加,造成系統(tǒng)更加龐大。

用一句話總結(jié):將公用的方法抽取到父類,在父類中預(yù)留可變的方法,最后子類去實(shí)現(xiàn)可變的方法。

模板模式更多的是考察我們對(duì)于公用方法的提取;對(duì)于編程也是這樣,更多的是一種思維能力,不能只局限于代碼,要把格局打開。文章來源地址http://www.zghlxwxcb.cn/news/detail-444939.html

到了這里,關(guān)于聊一聊模板方法模式的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 聊一聊大模型

    事情還得從ChatGPT說起。 2022年12月OpenAI發(fā)布了自然語言生成模型ChatGPT,一個(gè)可以基于用戶輸入文本自動(dòng)生成回答的人工智能體。它有著趕超人類的自然對(duì)話程度以及逆天的學(xué)識(shí)。一時(shí)間引爆了整個(gè)人工智能界,各大巨頭也紛紛跟進(jìn)發(fā)布了自家的大模型,如:百度-文心一言、科

    2024年02月05日
    瀏覽(32)
  • 聊一聊synchronized

    在 Java 中, synchronized 可以用于實(shí)現(xiàn)線程同步,有以下幾種常見的使用方式: 修飾代碼塊:將 synchronized 放在代碼塊的前面, 例如: 在這種方式下,會(huì)為給定的對(duì)象 obj 獲取鎖,在代碼塊執(zhí)行期間,只有持有該鎖的線程才能進(jìn)入代碼塊執(zhí)行。 修飾方法:將 sync

    2024年01月22日
    瀏覽(27)
  • 聊一聊AIGC

    聊一聊AIGC

    “UGC不存在了”——借鑒自《三體》 ChatGPT 的橫空出世將一個(gè)全新的概念推上風(fēng)口——AIGC( AI Generated Content)。 GC即創(chuàng)作內(nèi)容(Generated Content),和傳統(tǒng)的UGC、PGC,OGC不同的是,AIGC的創(chuàng)作主體由人變成了人工智能。 xGC PGC:Professionally Generated Content,專業(yè)生產(chǎn)內(nèi)容 UGC:User G

    2024年02月10日
    瀏覽(23)
  • 聊一聊Vue和Ts

    1 前言 Vue3 已經(jīng)正式發(fā)布了一段時(shí)間了,各種生態(tài)已經(jīng)成熟。最近使用 taro+vue3 重構(gòu)冷鏈的小程序,經(jīng)過了一段時(shí)間的開發(fā)和使用,有了一些自己的思考。 總的來說,Vue3 無論是在底層原理還是在實(shí)際開發(fā)過程中,都有了很大的進(jìn)步。 從源碼層面來說,使用 Proxy 代替 Object.d

    2023年04月08日
    瀏覽(27)
  • 聊一聊mysql中的間隙鎖

    聊一聊mysql中的間隙鎖

    間隙鎖在mysql中經(jīng)常使用到,今天就聊一聊mysql的間隙鎖的內(nèi)容。 間隙鎖是為了解決幻讀的問題,并且在當(dāng)前讀的場(chǎng)景下解決的。 當(dāng)前讀包含:update,delete,insert,select…lock in share mode,select…for update 一基本概念 1、行鎖:給某一行進(jìn)行加鎖 2、間隙鎖:兩個(gè)值之間的間隙,為解

    2024年02月12日
    瀏覽(23)
  • 聊一聊Java抽象同步隊(duì)列AQS

    聊一聊Java抽象同步隊(duì)列AQS

    AQS是鎖的底層支持 由該圖可以看到,AQS是一個(gè)FIFO的雙向隊(duì)列,其內(nèi)部通過節(jié)點(diǎn)head和tail記錄隊(duì)首和隊(duì)尾元素,隊(duì)列元素的類型為Node。其中Node中的thread變量用來存放進(jìn)入AQS隊(duì)列里面的線程;Node節(jié)點(diǎn)內(nèi)部的SHARED用來標(biāo)記該線程是獲取共享資源時(shí)被阻塞掛起后放入AQS隊(duì)列的,EX

    2024年02月16日
    瀏覽(33)
  • 聊一聊nginx中KeepAlive的設(shè)置

    聊一聊nginx中KeepAlive的設(shè)置

    之前工作中遇到一個(gè)KeepAlive的問題,現(xiàn)在把它記錄下來,場(chǎng)景是這樣的: 從上圖可以看出,用戶通過Client訪問的是LVS的VIP, VIP后端掛載的RealServer是Nginx服務(wù)器。 Client可以是瀏覽器也可以是一個(gè)客戶端程序。一般情況下, 這種架構(gòu)不會(huì)出現(xiàn)問題,但是如果Client端把請(qǐng)求發(fā)送給

    2024年02月01日
    瀏覽(28)
  • 聊一聊近期測(cè)試行情以及個(gè)人的感受

    聊一聊近期測(cè)試行情以及個(gè)人的感受

    眾所周知,去年年底的裁員潮再加上今年的疫情影響,失業(yè)、找工作成為了蠻多人的當(dāng)務(wù)之急。最近一些招聘網(wǎng)站也出現(xiàn)被刷爆的情況,其中順利找到工作的并不多,說明行情很冷,但是總有許多人順利跳槽。 其實(shí)對(duì)于大牛來說,行業(yè)是否景氣,影響真不大。反而有時(shí)候更容

    2024年02月09日
    瀏覽(15)
  • C++面試八股文:聊一聊指針?

    C++面試八股文:聊一聊指針?

    某日二師兄參加XXX科技公司的C++工程師開發(fā)崗位第17面: 面試官:聊一聊指針? 二師兄:好的。 面試官:你覺得指針本質(zhì)上是什么? 二師兄:這要從內(nèi)存地址開始說起了。如果有一塊容量是1G的內(nèi)存,假設(shè)它的地址是從 0x00000000 到 0x3fffffff ,每一個(gè)字節(jié)都對(duì)應(yīng)一個(gè)地址。當(dāng)

    2024年02月09日
    瀏覽(24)
  • 聊一聊springboot項(xiàng)目配置參數(shù)的三個(gè)來源

    大家好,我是G探險(xiǎn)者,今天我們聊一聊,springboot的參數(shù)的三個(gè)來源。 平時(shí)我們部署項(xiàng)目后,難免都會(huì)進(jìn)行一下配置修改,有的是在修改環(huán)境變量,有的是直接通過命令行參數(shù)進(jìn)行修改,有的是直接改,配置文件,那么這三種不同來源的參數(shù)修改,都是如何生效的,以及他們

    2024年02月09日
    瀏覽(18)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包