策略模式的學習之道

策略模式

1.需求分析:

一個考試系統,當考生的成績經過後(成績大於60分)會經過各類方式通知用戶。java

  • 通知方式有:APP消息推送、短信、郵件、站內消息四種方式;
  • 可是每種方式是否進行通知是要進行在表中配置的;
  • 假設咱們從表中查詢後的對象以下:
/**
 * 成績對象
 */
public class Score {
    //成績
    private int score;
    //是否使用app消息通知,使用0表示否,1表示是。下同
    private int sendAPPMsg;
    //是否使用短信通知
    private int sendSms;
    //是否使用郵件通知
    private int sendMail;
    //是否使用站內消息通知
    private int siteMsg;
    
    //其餘的一些非主要屬性再也不此加入,好比手機號,郵箱號等
    
    //get and set method
}

2.常規操做

  • 最簡單的就是使用if-else進行判斷了。對每一個配置條件進行判斷,若是須要進行通知就進行通知
/**
 * 使用常規的if-else操做
 */
public class IfDemo {

    /**
     * 使用if-else進行通知
     *
     * @param score 傳入的分數對象
     */
    public void sendNotice(Score score) {

        if (score.getScore() >= 60) {
             if (score.getSendAPPMsg() == 1) {
                //這裏是進行通知的具體實現,爲了和後邊造成更直接的對比,我加了好多代碼
                System.out.println("進行app消息推送1");
                System.out.println("進行app消息推送2");
                System.out.println("進行app消息推送3");
                System.out.println("進行app消息推送4");
            }
            if (score.getSendSms() == 1) {
                System.out.println("進行短信通知1");
                System.out.println("進行短信通知2");
                System.out.println("進行短信通知3");
                System.out.println("進行短信通知4");
            }
            if (score.getSendMail() == 1) {
                System.out.println("進行郵件通知1");
                System.out.println("進行郵件通知2");
                System.out.println("進行郵件通知3");
                System.out.println("進行郵件通知4");
            }
            if (score.getSiteMsg() == 1) {
                System.out.println("進行站內消息通知1");
                System.out.println("進行站內消息通知2");
                System.out.println("進行站內消息通知3");
                System.out.println("進行站內消息通知4");
            }
        }
    }
}

對於每種通知方法,我只進行了一句簡單的打印,實際中就會寫上進行通知的方法算法

  • Junit測試代碼(與業務代碼無關)
public class NoticeTest {

    private Score score;

    @BeforeEach
    public void beforeTest(){
        score = new Score();
        score.setScore(95);
        score.setSendAPPMsg(1);
        score.setSendMail(1);
        score.setSendSms(1);
        score.setSiteMsg(1);
    }

    @Test
    public void ifDemoTest() {
        IfDemo ifDemo = new IfDemo();
        ifDemo.sendNotice(score);
    }
}

這樣寫沒有任何問題,代碼確定是能跑起來的。可是,咱們是直掛將每一個通知的方法(也就是每種通知方法的具體實現)寫在了上邊代碼中,若是要改的話咱們就要對這裏進行修改。因此咱們進行一次優化,就是將上邊的每一種通知代碼進行一次方法抽取。設計模式

3.使用方法抽取進行優化

將上邊的各個通知方法進行一次抽取,能夠放在本類下,也能夠將通知方法單獨放在一個類中,我是放在了一個單獨的類中。通知類有以下方法:微信

/**
 * 通知類,
 */
public class NoticeService {
    
    public void sendAppNotice(){
        System.out.println("進行app推送通知1");
        System.out.println("進行app推送通知2");
        System.out.println("進行app推送通知3");
        System.out.println("進行app推送通知4");
    }

    public void sendSms(){
        System.out.println("進行短信通知1");
        System.out.println("進行短信通知2");
        System.out.println("進行短信通知3");
        System.out.println("進行短信通知4");
    }

    public void sendMail(){
        System.out.println("進行郵件通知1");
        System.out.println("進行郵件通知2");
        System.out.println("進行郵件通知3");
        System.out.println("進行郵件通知4");
    }

    public void sendSiteMsg(){
        System.out.println("進行站內消息通知1");
        System.out.println("進行站內消息通知2");
        System.out.println("進行站內消息通知3");
        System.out.println("進行站內消息通知4");
    }
}
  • 這時的這個socre判斷通知類以下:
package cn.lyn4ever.v2;

import cn.lyn4ever.bean.Score;

/**
 * 進行分數判斷是否通知的類
 */
public class NoticeDemo {
    /**
     * 處理是否進行通知的主要方法
     *
     * @param score
     */
    public void sendNotice(Score score) {
        NoticeService noticeService = new NoticeService();

        if (score.getScore() >= 60) {
            if (score.getSendAPPMsg() == 1) {
                //這裏只須要寫這一句就能夠進行通知,而不像剛纔那樣,下同
                noticeService.sendAppNotice();
            }
            if (score.getSendSms() == 1) {
                noticeService.sendSms();
            }
            if (score.getSendMail() == 1) {
                noticeService.sendMail();
            }
            if (score.getSiteMsg() == 1) {
                noticeService.sendSiteMsg();
            }
        }
    }
}
  • 進行測試的方法
@Test
public void noticeDemoTest(){
    NoticeDemo noticeDemo = new NoticeDemo();
    noticeDemo.sendNotice(score);
}

有小夥伴會問了,這樣寫的方法和上邊沒有任何區別啊?爲何要這麼作?app

  • 這樣的話就將整個通知方法的細節放在幾個單獨的方法中,只須要一句就能夠調用通知方法了
  • 並且當咱們要修改通知方法的代碼時,不用修改原來的方法啊

這種方式雖然將通知方法進行了單獨的方法抽取,下降了必定的耦合。可是呢?ide

若是,咱們有一個或多個新的通知方式要添加,好比微信、QQ。那你要改的地方是哪兒?學習

1.在Score的bean中添加兩個字段,這個是必須的測試

2.在NoticeService中添加兩個新的通知方法優化

public void sendQQ(){
        System.out.println("進行QQ消息通知1");
        System.out.println("進行QQ消息通知2");
        System.out.println("進行QQ消息通知3");
        System.out.println("進行QQ消息通知4");
}
public void sendWX(){
        System.out.println("進行WX消息通知1");
        System.out.println("進行WX消息通知2");
        System.out.println("進行WX消息通知3");
        System.out.println("進行WX消息通知4");
}

3.在NoticeDemo中添加兩個if判斷設計

if (score.getSendQQ() == 1) {
    noticeService.sendQQ();
}
if (score.getSendWX() == 1) {
    noticeService.sendWX();
}

3.使用策略模式進行優化

策略模式(Strategy),定義了一組算法,將每一個算法都封裝起來,而且使它們之間能夠互換

  • 定義一個通知的接口
package cn.lyn4ever.v3;

public interface INotice {
    void notice();
}
  • 寫出它不一樣通知方式的不一樣實現(只寫一個)
public class AppNoticeImpl implements INotice {

    @Override
    public void notice() {
        System.out.println("進行app消息推送");
    }
}
  • 通知的service方法以下:
public class NoticeService3 {
    /**
     * 只提供一個通知方法,參數爲通知接口
     * @param iNotice
     */
    public void notice(INotice iNotice) {
        iNotice.notice();
    }
}
  • 利用分數判斷的通知方法以下:
public class NoticeDemo3 {

    public void sendNotice(Score score) {
        NoticeService3 noticeService = new NoticeService3();

        if (score.getScore() >= 60) {
            if (score.getSendAPPMsg() == 1) {
                noticeService.notice(new AppNoticeImpl());
            }
            if (score.getSendSms() == 1) {
                noticeService.notice(new SmsNoticeImpl());
            }
            if (score.getSendMail() == 1) {
                noticeService.notice(new MailNoticeImpl());
            }
            if (score.getSiteMsg() == 1) {
                noticeService.notice(new SiteNoticeImpl());
            }
        }
    }
}
  • 有的小夥伴可能會認爲這不是在重複造輪子嗎?這和方法1、方法二讓人更難理解

  • 其實並非在重複地造輪子,這樣作就體現了面向對象開發的「封裝」特性,相比第一種方法,更好地「面向對象」開發,並且封裝了整個每個通知方法的實現,下降了通知這個功能的耦合度,使得每一種通知方法有本身的實現。

  • 好比上邊的一個新須要,當咱們須要新增「QQ」和「微信」通知的時候,這時只須要添加兩個新的INotice的實現類,並在if條件中加兩句就能夠了,NoticeService類不用再次修改

    更多的設計模式學習,請關注個人微信公衆號「小魚與Java」

    更多的設計模式學習

相關文章
相關標籤/搜索