31 設計模式——策略模式(策略設計模式)詳解

在現(xiàn)實生活中常常遇到實現(xiàn)某種目標存在多種策略可供選擇的情況,例如,出行旅游可以乘坐飛機、乘坐火車、騎自行車或自己開私家車等,超市促銷可以釆用打折、送商品、送積分等方法。

在軟件開發(fā)中也常常遇到類似的情況,當實現(xiàn)某一個功能存在多種算法或者策略,我們可以根據(jù)環(huán)境或者條件的不同選擇不同的算法或者策略來完成該功能,如數(shù)據(jù)排序策略有冒泡排序、選擇排序、插入排序、二叉樹排序等。

如果使用多重條件轉移語句實現(xiàn)(即硬編碼),不但使條件語句變得很復雜,而且增加、刪除或更換算法要修改原代碼,不易維護,違背開閉原則。如果采用策略模式就能很好解決該問題。

策略模式的定義與特點

策略(Strategy)模式的定義:該模式定義了一系列算法,并將每個算法封裝起來,使它們可以相互替換,且算法的變化不會影響使用算法的客戶。策略模式屬于對象行為模式,它通過對算法進行封裝,把使用算法的責任和算法的實現(xiàn)分割開來,并委派給不同的對象對這些算法進行管理。

策略模式的主要優(yōu)點如下。

  1. 多重條件語句不易維護,而使用策略模式可以避免使用多重條件語句,如 if...else 語句、switch...case 語句。
  2. 策略模式提供了一系列的可供重用的算法族,恰當使用繼承可以把算法族的公共代碼轉移到父類里面,從而避免重復的代碼。
  3. 策略模式可以提供相同行為的不同實現(xiàn),客戶可以根據(jù)不同時間或空間要求選擇不同的。
  4. 策略模式提供了對開閉原則的完美支持,可以在不修改原代碼的情況下,靈活增加新算法。
  5. 策略模式把算法的使用放到環(huán)境類中,而算法的實現(xiàn)移到具體策略類中,實現(xiàn)了二者的分離。

其主要缺點如下。

  1. 客戶端必須理解所有策略算法的區(qū)別,以便適時選擇恰當?shù)乃惴悺?/li>
  2. 策略模式造成很多的策略類,增加維護難度。

策略模式的結構與實現(xiàn)

策略模式是準備一組算法,并將這組算法封裝到一系列的策略類里面,作為一個抽象策略類的子類。策略模式的重心不是如何實現(xiàn)算法,而是如何組織這些算法,從而讓程序結構更加靈活,具有更好的維護性和擴展性,現(xiàn)在我們來分析其基本結構和實現(xiàn)方法。

1. 模式的結構

策略模式的主要角色如下。

  1. 抽象策略(Strategy)類:定義了一個公共接口,各種不同的算法以不同的方式實現(xiàn)這個接口,環(huán)境角色使用這個接口調用不同的算法,一般使用接口或抽象類實現(xiàn)。
  2. 具體策略(Concrete Strategy)類:實現(xiàn)了抽象策略定義的接口,提供具體的算法實現(xiàn)。
  3. 環(huán)境(Context)類:持有一個策略類的引用,最終給客戶端調用。

其結構圖如圖 1 所示。

3-1Q116103K1205.gif

圖1 策略模式的結構圖

2. 模式的實現(xiàn)

策略模式的實現(xiàn)代碼如下:

public class StrategyPattern {
    public static void main(String[] args) {
        Context c = new Context();
        Strategy s = new ConcreteStrategyA();
        c.setStrategy(s);
        c.strategyMethod();
        System.out.println("-----------------");
        s = new ConcreteStrategyB();
        c.setStrategy(s);
        c.strategyMethod();
    }
}
//抽象策略類
interface Strategy {
    public void strategyMethod();    //策略方法
}
//具體策略類A
class ConcreteStrategyA implements Strategy {
    public void strategyMethod() {
        System.out.println("具體策略A的策略方法被訪問!");
    }
}
//具體策略類B
class ConcreteStrategyB implements Strategy {
    public void strategyMethod() {
        System.out.println("具體策略B的策略方法被訪問!");
    }
}
//環(huán)境類
class Context {
    private Strategy strategy;
    public Strategy getStrategy() {
        return strategy;
    }
    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }
    public void strategyMethod() {
        strategy.strategyMethod();
    }
}

程序運行結果如下:

具體策略A的策略方法被訪問!
-----------------
具體策略B的策略方法被訪問!

策略模式的應用實例

【例1】策略模式在“大閘蟹”做菜中的應用。

分析:關于大閘蟹的做法有很多種,我們以清蒸大閘蟹和紅燒大閘蟹兩種方法為例,介紹策略模式的應用。

首先,定義一個大閘蟹加工的抽象策略類(CrabCooking),里面包含了一個做菜的抽象方法 CookingMethod();然后,定義清蒸大閘蟹(SteamedCrabs)和紅燒大閘蟹(BraisedCrabs)的具體策略類,它們實現(xiàn)了抽象策略類中的抽象方法;由于本程序要顯示做好的結果圖,所以將具體策略類定義成 JLabel 的子類;最后,定義一個廚房(Kitchen)環(huán)境類,它具有設置和選擇做菜策略的方法;客戶類通過廚房類獲取做菜策略,并把做菜結果圖在窗體中顯示出來,圖 2 所示是其結構圖。

3-1Q116103R5638.gif

圖2 大閘蟹做菜策略的結構圖

程序代碼如下:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class CrabCookingStrategy implements ItemListener {
    private JFrame f;
    private JRadioButton qz, hs;
    private JPanel CenterJP, SouthJP;
    private Kitchen cf;    //廚房
    private CrabCooking qzx, hsx;    //大閘蟹加工者  
    CrabCookingStrategy() {
        f = new JFrame("策略模式在大閘蟹做菜中的應用");
        f.setBounds(100, 100, 500, 400);
        f.setVisible(true);
        f.setResizable(false);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        SouthJP = new JPanel();
        CenterJP = new JPanel();
        f.add("South", SouthJP);
        f.add("Center", CenterJP);
        qz = new JRadioButton("清蒸大閘蟹");
        hs = new JRadioButton("紅燒大閘蟹");
        qz.addItemListener(this);
        hs.addItemListener(this);
        ButtonGroup group = new ButtonGroup();
        group.add(qz);
        group.add(hs);
        SouthJP.add(qz);
        SouthJP.add(hs);
        //---------------------------------
        cf = new Kitchen();    //廚房
        qzx = new SteamedCrabs();    //清蒸大閘蟹類
        hsx = new BraisedCrabs();    //紅燒大閘蟹類
    }
    public void itemStateChanged(ItemEvent e) {
        JRadioButton jc = (JRadioButton) e.getSource();
        if (jc == qz) {
            cf.setStrategy(qzx);
            cf.CookingMethod(); //清蒸
        } else if (jc == hs) {
            cf.setStrategy(hsx);
            cf.CookingMethod(); //紅燒
        }
        CenterJP.removeAll();
        CenterJP.repaint();
        CenterJP.add((Component) cf.getStrategy());
        f.setVisible(true);
    }
    public static void main(String[] args) {
        new CrabCookingStrategy();
    }
}
//抽象策略類:大閘蟹加工類
interface CrabCooking {
    public void CookingMethod();    //做菜方法
}
//具體策略類:清蒸大閘蟹
class SteamedCrabs extends JLabel implements CrabCooking {
    private static final long serialVersionUID = 1L;
    public void CookingMethod() {
        this.setIcon(new ImageIcon("src/strategy/SteamedCrabs.jpg"));
        this.setHorizontalAlignment(CENTER);
    }
}
//具體策略類:紅燒大閘蟹
class BraisedCrabs extends JLabel implements CrabCooking {
    private static final long serialVersionUID = 1L;
    public void CookingMethod() {
        this.setIcon(new ImageIcon("src/strategy/BraisedCrabs.jpg"));
        this.setHorizontalAlignment(CENTER);
    }
}
//環(huán)境類:廚房
class Kitchen {
    private CrabCooking strategy;    //抽象策略
    public void setStrategy(CrabCooking strategy) {
        this.strategy = strategy;
    }
    public CrabCooking getStrategy() {
        return strategy;
    }
    public void CookingMethod() {
        strategy.CookingMethod();    //做菜  
    }
}

程序運行結果如圖 3 所示。

3-1Q116103Z1315.jpg

圖3 大閘蟹做菜結果

【例2】用策略模式實現(xiàn)從韶關去婺源旅游的出行方式。

分析:從韶關去婺源旅游有以下幾種出行方式:坐火車、坐汽車和自駕車,所以該實例用策略模式比較適合,圖 4 所示是其結構圖。

3-1Q11610393E92.gif

圖4 婺源旅游結構圖

策略模式的應用場景

策略模式在很多地方用到,如 Java SE 中的容器布局管理就是一個典型的實例,Java SE 中的每個容器都存在多種布局供用戶選擇。在程序設計中,通常在以下幾種情況中使用策略模式較多。

  1. 一個系統(tǒng)需要動態(tài)地在幾種算法中選擇一種時,可將每個算法封裝到策略類中。
  2. 一個類定義了多種行為,并且這些行為在這個類的操作中以多個條件語句的形式出現(xiàn),可將每個條件分支移入它們各自的策略類中以代替這些條件語句。
  3. 系統(tǒng)中各算法彼此完全獨立,且要求對客戶隱藏具體算法的實現(xiàn)細節(jié)時。
  4. 系統(tǒng)要求使用算法的客戶不應該知道其操作的數(shù)據(jù)時,可使用策略模式來隱藏與算法相關的數(shù)據(jù)結構。
  5. 多個類只區(qū)別在表現(xiàn)行為不同,可以使用策略模式,在運行時動態(tài)選擇具體要執(zhí)行的行為。

策略模式的擴展

在一個使用策略模式的系統(tǒng)中,當存在的策略很多時,客戶端管理所有策略算法將變得很復雜,如果在環(huán)境類中使用策略工廠模式來管理這些策略類將大大減少客戶端的工作復雜度,其結構圖如圖 5 所示。

3-1Q116104010550.gif

圖5 策略工廠模式的結構圖

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 在現(xiàn)實生活中常常遇到實現(xiàn)某種目標存在多種策略可供選擇的情況,例如,出行旅游可以乘坐飛機、乘坐火車、騎自行車或自己開...
    Zal哥哥閱讀 985評論 0 0
  • 在現(xiàn)實生活中常常遇到實現(xiàn)某種目標存在多種策略可供選擇的情況,例如,出行旅游可以乘坐飛機、乘坐火車、騎自行車或自己開...
    迷心迷閱讀 146評論 0 0
  • 1、設計模式分類 參考 創(chuàng)建型(5種):將對象的創(chuàng)建與使用分離,使用者不需要關注對象的創(chuàng)建細節(jié) 單例(Singl...
    與搬磚有關的日子閱讀 2,586評論 2 69
  • 從本篇文章開始將開始一些列對Java設計模式的學習。Java設計模式是前任總結的優(yōu)秀的經(jīng)驗,是編寫高質量代碼的學習...
    Ruheng閱讀 2,051評論 2 9
  • 概述 我們知道,OOP三個基本特征是:封裝、繼承、多態(tài)。通過繼承,我們可以基于差異編程,也就是說,對于一個滿足我們...
    陳HHH閱讀 644評論 0 1

友情鏈接更多精彩內(nèi)容