• <menu id="gyiem"><menu id="gyiem"></menu></menu>
  • <menu id="gyiem"><code id="gyiem"></code></menu>

    Java設計模式(十二) 策略模式

    原創文章,轉載請務必將下面這段話置于文章開頭處(保留超鏈接)。
    本文轉發自技術世界原文鏈接 http://www.luozeyang.com/design_pattern/strategy/

    策略模式介紹

    策略模式定義

    策略模式(Strategy Pattern),將各種算法封裝到具體的類中,作為一個抽象策略類的子類,使得它們可以互換。客戶端可以自行決定使用哪種算法。

    策略模式類圖

    策略模式類圖如下
    Strategy Pattern Class Diagram

    策略模式角色劃分

    • Strategy 策略接口或者(抽象策略類),定義策略執行接口
    • ConcreteStrategy 具體策略類
    • Context 上下文類,持有具體策略類的實例,并負責調用相關的算法

    策略模式實例解析

    本文代碼可從作者Github下載

    典型策略模式實現

    策略接口,定義策略執行接口

    1
    2
    3
    4
    5
    6
    7
    package com.jasongj.strategy;

    public interface Strategy {

    void strategy(String input);

    }

    具體策略類,實現策略接口,提供具體算法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    package com.jasongj.strategy;

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;

    @com.jasongj.annotation.Strategy(name="StrategyA")
    public class ConcreteStrategyA implements Strategy {

    private static final Logger LOG = LoggerFactory.getLogger(ConcreteStrategyB.class);

    @Override
    public void strategy(String input) {
    LOG.info("Strategy A for input : {}", input);
    }

    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    package com.jasongj.strategy;

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;

    @com.jasongj.annotation.Strategy(name="StrategyB")
    public class ConcreteStrategyB implements Strategy {

    private static final Logger LOG = LoggerFactory.getLogger(ConcreteStrategyB.class);

    @Override
    public void strategy(String input) {
    LOG.info("Strategy B for input : {}", input);
    }

    }

    Context類,持有具體策略類的實例,負責調用具體算法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    package com.jasongj.context;

    import com.jasongj.strategy.Strategy;

    public class SimpleContext {

    private Strategy strategy;

    public SimpleContext(Strategy strategy) {
    this.strategy = strategy;
    }

    public void action(String input) {
    strategy.strategy(input);
    }

    }

    客戶端可以實例化具體策略類,并傳給Context類,通過Context統一調用具體算法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    package com.jasongj.client;

    import com.jasongj.context.SimpleContext;
    import com.jasongj.strategy.ConcreteStrategyA;
    import com.jasongj.strategy.Strategy;

    public class SimpleClient {

    public static void main(String[] args) {
    Strategy strategy = new ConcreteStrategyA();
    SimpleContext context = new SimpleContext(strategy);
    context.action("Hellow, world");
    }

    }

    使用Annotation和簡單工廠模式增強策略模式

    上面的實現中,客戶端需要顯示決定具體使用何種策略,并且一旦需要換用其它策略,需要修改客戶端的代碼。解決這個問題,一個比較好的方式是使用簡單工廠,使得客戶端都不需要知道策略類的實例化過程,甚至都不需要具體哪種策略被使用。

    如《Java設計模式(一) 簡單工廠模式不簡單》所述,簡單工廠的實現方式比較多,可以結合《Java系列(一)Annotation(注解)》中介紹的Annotation方法。

    使用Annotation和簡單工廠模式的Context類如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    package com.jasongj.context;

    import java.util.Collections;
    import java.util.Map;
    import java.util.Set;
    import java.util.concurrent.ConcurrentHashMap;

    import org.apache.commons.configuration.ConfigurationException;
    import org.apache.commons.configuration.XMLConfiguration;
    import org.reflections.Reflections;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;

    import com.jasongj.strategy.Strategy;

    public class SimpleFactoryContext {

    private static final Logger LOG = LoggerFactory.getLogger(SimpleFactoryContext.class);
    private static Map<String, Class> allStrategies;

    static {
    Reflections reflections = new Reflections("com.jasongj.strategy");
    Set<Class<?>> annotatedClasses =
    reflections.getTypesAnnotatedWith(com.jasongj.annotation.Strategy.class);
    allStrategies = new ConcurrentHashMap<String, Class>();
    for (Class<?> classObject : annotatedClasses) {
    com.jasongj.annotation.Strategy strategy = (com.jasongj.annotation.Strategy) classObject
    .getAnnotation(com.jasongj.annotation.Strategy.class);
    allStrategies.put(strategy.name(), classObject);
    }
    allStrategies = Collections.unmodifiableMap(allStrategies);
    }

    private Strategy strategy;

    public SimpleFactoryContext() {
    String name = null;
    try {
    XMLConfiguration config = new XMLConfiguration("strategy.xml");
    name = config.getString("strategy.name");
    LOG.info("strategy name is {}", name);
    } catch (ConfigurationException ex) {
    LOG.error("Parsing xml configuration file failed", ex);
    }

    if (allStrategies.containsKey(name)) {
    LOG.info("Created strategy name is {}", name);
    try {
    strategy = (Strategy) allStrategies.get(name).newInstance();
    } catch (InstantiationException | IllegalAccessException ex) {
    LOG.error("Instantiate Strategy failed", ex);
    }
    } else {
    LOG.error("Specified Strategy name {} does not exist", name);
    }

    }

    public void action(String input) {
    strategy.strategy(input);
    }

    }

    從上面的實現可以看出,雖然并沒有單獨創建一個簡單工廠類,但它已經融入了簡單工廠模式的設計思想和實現方法。

    客戶端調用方式如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    package com.jasongj.client;

    import com.jasongj.context.SimpleFactoryContext;

    public class SimpleFactoryClient {

    public static void main(String[] args) {
    SimpleFactoryContext context = new SimpleFactoryContext();
    context.action("Hellow, world");
    }

    }

    從上面代碼可以看出,引入簡單工廠模式后,客戶端不再需要直接實例化具體的策略類,也不需要判斷應該使用何種策略,可以方便應對策略的切換。

    策略模式分析

    策略模式優點

    • 策略模式提供了對“開閉原則”的完美支持,用戶可以在不修改原有系統的基礎上選擇算法(策略),并且可以靈活地增加新的算法(策略)。
    • 策略模式通過Context類提供了管理具體策略類(算法族)的辦法。
    • 結合簡單工廠模式和Annotation,策略模式可以方便的在不修改客戶端代碼的前提下切換算法(策略)。

    策略模式缺點

    • 傳統的策略模式實現方式中,客戶端必須知道所有的具體策略類,并須自行顯示決定使用哪一個策略類。但通過本文介紹的通過和Annotation和簡單工廠模式結合,可以有效避免該問題
    • 如果使用不當,策略模式可能創建很多具體策略類的實例,但可以通過使用上文《Java設計模式(十一) 享元模式》介紹的享元模式有效減少對象的數量。

    策略模式已(未)遵循的OOP原則

    已遵循的OOP原則

    • 依賴倒置原則
    • 迪米特法則
    • 里氏替換原則
    • 接口隔離原則
    • 單一職責原則
    • 開閉原則

    未遵循的OOP原則

    • NA

    Java設計模式系列

    郭俊 Jason wechat
    歡迎關注作者微信公眾號【大數據架構】
    您的贊賞將支持作者繼續原創分享
    速赢彩app 铜川 | 商洛 | 江门 | 邳州 | 宜昌 | 嘉善 | 酒泉 | 巴彦淖尔市 | 绵阳 | 琼海 | 新疆乌鲁木齐 | 青海西宁 | 大连 | 遵义 | 东莞 | 来宾 | 中卫 | 山南 | 晋城 | 惠东 | 吉林长春 | 阿克苏 | 郴州 | 赵县 | 金华 | 保亭 | 曲靖 | 邹平 | 安吉 | 三明 | 襄阳 | 喀什 | 驻马店 | 赵县 | 迪庆 | 宁波 | 金坛 | 金坛 | 长治 | 焦作 | 淮南 | 泸州 | 襄阳 | 永新 | 达州 | 中卫 | 承德 | 抚顺 | 承德 | 高密 | 邯郸 | 宿迁 | 上饶 | 钦州 | 巴音郭楞 | 宜都 | 滨州 | 铁岭 | 海南海口 | 吐鲁番 | 泗洪 | 揭阳 | 任丘 | 永新 | 南充 | 平潭 | 靖江 | 乐山 | 包头 | 章丘 | 莆田 | 承德 | 金坛 | 江门 | 兴化 | 商洛 | 亳州 | 瑞安 | 宝鸡 | 朝阳 | 益阳 | 鹤岗 | 邢台 | 陵水 | 如皋 | 临海 | 金昌 | 盐城 | 宜昌 | 阿拉善盟 | 安徽合肥 | 珠海 | 沭阳 | 徐州 | 泗阳 | 巴彦淖尔市 | 新乡 | 秦皇岛 | 巢湖 | 张掖 | 张家口 | 燕郊 | 晋中 | 莒县 | 聊城 | 茂名 | 包头 | 嘉善 | 常州 | 玉环 | 泰州 | 吴忠 | 河源 |