成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

聊一聊模板方法模式

開發 前端
模板方法模式其實是一個比較簡單的設計模式,它有如下優點:1、封裝不變的邏輯,擴展差異化的邏輯;2、抽取公共代碼,提高代碼的復用性;3、父類控制行為,子類實現細節。

一、概述

模板方法模式,又叫模板模式,屬于23種設計模式中的行為型模式。在抽象類中公開定義了執行的方法,子類可以按需重寫其方法,但是要以抽象類中定義的方式調用方法。總結起來就是:定義一個操作的算法結構,而將一些步驟延遲到子類中。在不改變算法結構的情況下,子類能重定義該算法的特定步驟。

下面是模板模式的UML圖,抽象類(AbstractClass)定義公共的步驟和方法,依次調用實際的模板方法,當然每個方法可以是抽象方法(需交給子類實現),也可以是提供默認的方法。具體的類(ConcreteClass)可以重寫所有的方法,但是不能改變抽象類中定義的整體結構。

圖片

二、入門案例

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

圖片

基于這個場景,我們來寫一個案例,進一步了解下模板模式;創建三個類:Cake(蛋糕)、StrawberryCake(草莓蛋糕)、CherryCake(櫻桃蛋糕)。最后創建一個Client類,實現這個制作蛋糕的調用過程。

package com.wsrf.template;

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

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

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

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

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

}
package com.wsrf.template;

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

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

/**
 * @author 往事如風
 * @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();
    }
}
/**
輸出結果:
開始準備材料。
準備材料:面包
準備材料:奶油
準備材料:櫻桃
經過一系列的操作。
制作完成。
-------------------------------------
開始準備材料。
準備材料:面包
準備材料:奶油
準備材料:草莓
經過一系列的操作。
制作完成。
*/

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

三、運用場景

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

最近在開發中,遇到這樣的一個業務場景:需要給不同的管理人員計算各種不同的津貼,如區域總監有區域管理津貼、傭金、培養育成津貼等等。通過分析,每種不同類型的津貼,都是需要金額x比例x系數,比如每種津貼都有不同的計算方式,系數也是。所以,大致的想法就是:金額x比例x系數這個計算方式設置為統一的方法,系數和比例讓具體的津貼子類去實現。所以大致的偽代碼如下;

首先,我定義了一個抽象類AbstractManageAllowanceCalService,用于定義統一的計算方法,并預留了獲取比例和獲取系數的抽象方法。

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

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

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

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

然后,定義兩個具體的子類,用于計算區域管理津貼和傭金。

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

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

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

最后,定義一個controller類,用于接口調用,提供計算能力;接收兩個參數,金額和計算津貼類型。

/**
 * @author 往事如風
 * @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)) {
            // 區域管理津貼
             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;
    }
}

在這個controller類中,我通過分析“類型”這個參數,來判斷需要調用哪個service去實現具體的計算邏輯。這里用了if-else的方式去實現;其實也可以用到另一個設計模式——策略模式,這樣寫出來的代碼就會比較優雅,這里就不對策略模式展開贅述了。

四、源碼中運用

4.1、JDK源碼中的模板模式

在JDK中其實也有很多地方運用到了模板模式,這里咱挑一個講。并發包下的AbstractQueuedSynchronizer類,就是一個抽象類,也就是我們先前的文章中提到過的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這兩個方式直接拋了異常,用protected關鍵詞修飾,需要由子類去實現。然后在acquire和release方法中分別去調用這兩方法。也就是acquire方法定義了一個統一的結構,差異化的tryAcquire方法需要具體的子類去實現功能,實現了模板模式。

4.2、Spring源碼中的模板模式

說到源碼,Spring是一個繞不開的話題,那就來學習下Spring中的模板模式。其中,有一個類DefaultBeanDefinitionDocumentReader,它是BeanDefinitionDocumentReader的實現類,是提取spring配置文件中的bean信息,并轉化為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這個方法,從跟節點root出發,root下的每個bean注冊定義。

該方法中還調用了preProcessXml和postProcessXml這兩個方法,但是在DefaultBeanDefinitionDocumentReader類中,這兩個方法是未實現的,需要其子類去實現具體的邏輯。所以,這里也是一個很典型的模板模式的運用。

五、總結

模板方法模式其實是一個比較簡單的設計模式,它有如下優點:1、封裝不變的邏輯,擴展差異化的邏輯;2、抽取公共代碼,提高代碼的復用性;3、父類控制行為,子類實現細節。

其缺點就是不同的實現都需要一個子類去維護,會導致子類的個數不斷增加,造成系統更加龐大。

用一句話總結:將公用的方法抽取到父類,在父類中預留可變的方法,最后子類去實現可變的方法。

模板模式更多的是考察我們對于公用方法的提取;對于編程也是這樣,更多的是一種思維能力,不能只局限于代碼,要把格局打開。

六、參考源碼

編程文檔:
https://gitee.com/cicadasmile/butte-java-note

應用倉庫:
https://gitee.com/cicadasmile/butte-flyer-parent
責任編輯:武曉燕 來源: 知了一笑
相關推薦

2022-11-01 08:46:20

責任鏈模式對象

2022-11-26 00:00:06

裝飾者模式Component

2022-06-01 09:51:51

Golang方法接收者

2023-07-25 15:06:39

2023-12-14 11:35:32

.NET泄露模式

2023-02-09 10:39:15

gRPC通信模式

2022-09-26 08:03:25

VMware虛擬機

2023-09-22 17:36:37

2021-01-28 22:31:33

分組密碼算法

2020-05-22 08:16:07

PONGPONXG-PON

2018-06-07 13:17:12

契約測試單元測試API測試

2021-05-12 18:02:23

方法創建線程

2023-03-10 07:47:41

克隆jQuery

2019-02-13 14:15:59

Linux版本Fedora

2021-08-04 09:32:05

Typescript 技巧Partial

2021-01-29 08:32:21

數據結構數組

2021-02-06 08:34:49

函數memoize文檔

2022-08-08 08:25:21

Javajar 文件

2023-07-06 13:56:14

微軟Skype

2018-11-29 09:13:47

CPU中斷控制器
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 一二区视频 | 成人九色 | 国产激情91久久精品导航 | 91视频在线看| 视频二区 | 伊人久久麻豆 | 日韩伦理一区二区 | 亚洲天天干| 精品国产一区久久 | 视频一区二区三区在线观看 | 久久久免费毛片 | 成人二区| 亚洲aⅴ | www.中文字幕.com | 久久久久久久久久久国产 | 中文字幕中文字幕 | 亚洲精品一区二三区不卡 | 欧美一区二区在线 | 久久国 | 91久久精品一区二区二区 | 日韩欧美专区 | 亚洲高清在线观看 | 国产成人免费一区二区60岁 | 日本a视频 | 秋霞精品| 日韩一区二区三区视频在线观看 | 在线视频成人 | 中文字幕视频在线观看 | 岛国av免费看 | 91在线视频精品 | 久久久久久亚洲国产精品 | 久久丁香 | 久久99精品久久久久久 | 性网址| 久久精品欧美一区二区三区麻豆 | 亚洲精品毛片av | 亚洲综合资源 | 国产农村妇女毛片精品久久麻豆 | av毛片在线播放 | 亚洲美女在线视频 | 成年免费大片黄在线观看岛国 |