原創文章,同步發自做者我的博客,http://www.jasongj.com/design_pattern/strategy/java
策略模式(Strategy Pattern),將各類算法封裝到具體的類中,做爲一個抽象策略類的子類,使得它們能夠互換。客戶端能夠自行決定使用哪一種算法。git
策略模式類圖以下
github
本文代碼可從做者Github下載算法
策略接口,定義策略執行接口apache
package com.jasongj.strategy; public interface Strategy { void strategy(String input); }
具體策略類,實現策略接口,提供具體算法設計模式
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); } }
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類,持有具體策略類的實例,負責調用具體算法ide
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統一調用具體算法oop
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"); } }
上面的實現中,客戶端須要顯示決定具體使用何種策略,而且一旦須要換用其它策略,須要修改客戶端的代碼。解決這個問題,一個比較好的方式是使用簡單工廠,使得客戶端都不須要知道策略類的實例化過程,甚至都不須要具體哪一種策略被使用。this
如《Java設計模式(一) 簡單工廠模式不簡單》所述,簡單工廠的實現方式比較多,能夠結合《Java系列(一)Annotation(註解)》中介紹的Annotation方法。設計
使用Annotation和簡單工廠模式的Context類以下
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); } }
從上面的實現能夠看出,雖然並無單首創建一個簡單工廠類,但它已經融入了簡單工廠模式的設計思想和實現方法。
客戶端調用方式以下
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"); } }
從上面代碼能夠看出,引入簡單工廠模式後,客戶端再也不須要直接實例化具體的策略類,也不須要判斷應該使用何種策略,能夠方便應對策略的切換。