一、定义
在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。吹了一大段概念,用一句白话来说就是解决使用 if...else 所带来的复杂和难以维护
二、策略模式的应用场景
1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
2、一个系统需要动态地在几种算法中选择一种。
3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
注意事项:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。
以我前两天做的项目为例吧,前两天做的一个系统中正好有一个场景,就是各种类型的文档转换,每一大类的文档选择的策略不一样。
/** * 转换策略 */ public interface TransitionStrategy { //转换 void transform(); }
/** * DOC文档转换 */ public class DocTransition implements TransitionStrategy{ @Override public void transform() { System.out.println("使用DOC文档转换"); } }
/** * dwg转换 */ public class DwgTransition implements TransitionStrategy{ @Override public void transform() { System.out.println("使用dwg转换"); } }
public class Test { public static void main(String[] args) { DocTransition docTransition=new DocTransition(); DwgTransition dwgTransition=new DwgTransition(); boolean flag=true; if (flag){ docTransition.transform(); }else{ dwgTransition.transform(); } } }
上面代码看起来没啥子问题,但是,一当转换类型多了,那就有大量的if判断,这时代码的美观度就没那么好了,这时就可以考虑用策略进行优化下,增加一个枚举类,在枚举类内部提供newInstance方法,根据传入code值进行转换方式的初始化。
//转换策略 public enum TransitionEnum { DOC("doc"){ public TransitionStrategy create(){ return new DocTransition(); } }, DWG("dwg"){ public TransitionStrategy create(){ return new DwgTransition(); } }; /** * 根据枚举值获取bean实例 * @param transitionEnum * @return */ public static TransitionStrategy newInstance(TransitionEnum transitionEnum) { return transitionEnum.create(); } public abstract TransitionStrategy create(); private String code; public String getCode() { return code; } public void setCode(String code) { this.code = code; } TransitionEnum(String code) { this.code = code; } /** * 根据code获取枚举 * @param code * @return */ public static TransitionEnum getEnum(String code) { for (TransitionEnum value : values()) { if (value.getCode().equals(code)) { return value; } } return null; } }
然后改一下调用者的代码
public class Test { public static void main(String[] args) { TransitionStrategy transitionStrategy=TransitionEnum.newInstance(TransitionEnum.getEnum("doc")); transitionStrategy.transform(); } }
通过上面的转换后,代码就变得非常简洁了。
三、源码应用
在JDK源码中比较器 Comparator 接口,我们看到的一个大家常用的compare()方法,就是一个策略抽象实现;Comparator 抽象下面有非常多的实现类,我们经常会把 Comparator 作为参数传入作为排序策略,例如 Arrays 类的 parallelSort 方法等:
public static <T> void parallelSort(T[] a, int fromIndex, int toIndex, Comparator<? super T> cmp) { rangeCheck(a.length, fromIndex, toIndex); if (cmp == null) cmp = NaturalOrder.INSTANCE; int n = toIndex - fromIndex, p, g; if (n <= MIN_ARRAY_SORT_GRAN || (p = ForkJoinPool.getCommonPoolParallelism()) == 1) TimSort.sort(a, fromIndex, toIndex, cmp, null, 0, 0); else new ArraysParallelSortHelpers.FJObject.Sorter<T> (null, a, (T[])Array.newInstance(a.getClass().getComponentType(), n), fromIndex, n, 0, ((g = n / (p << 2)) <= MIN_ARRAY_SORT_GRAN) ? MIN_ARRAY_SORT_GRAN : g, cmp).invoke(); }
还有 TreeMap 的构造方法:
public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, java.io.Serializable { /** * The comparator used to maintain order in this tree map, or * null if it uses the natural ordering of its keys. * * @serial */ private final Comparator<? super K> comparator; private transient Entry<K,V> root; /** * The number of entries in the tree */ private transient int size = 0; /** * The number of structural modifications to the tree. */ private transient int modCount = 0; /** * Constructs a new, empty tree map, using the natural ordering of its * keys. All keys inserted into the map must implement the {@link * Comparable} interface. Furthermore, all such keys must be * <em>mutually comparable</em>: {@code k1.compareTo(k2)} must not throw * a {@code ClassCastException} for any keys {@code k1} and * {@code k2} in the map. If the user attempts to put a key into the * map that violates this constraint (for example, the user attempts to * put a string key into a map whose keys are integers), the * {@code put(Object key, Object value)} call will throw a * {@code ClassCastException}. */ public TreeMap() { comparator = null; } /** * Constructs a new, empty tree map, ordered according to the given * comparator. All keys inserted into the map must be <em>mutually * comparable</em> by the given comparator: {@code comparator.compare(k1, * k2)} must not throw a {@code ClassCastException} for any keys * {@code k1} and {@code k2} in the map. If the user attempts to put * a key into the map that violates this constraint, the {@code put(Object * key, Object value)} call will throw a * {@code ClassCastException}. * * @param comparator the comparator that will be used to order this map. * If {@code null}, the {@linkplain Comparable natural * ordering} of the keys will be used. */ public TreeMap(Comparator<? super K> comparator) { this.comparator = comparator; } /** * Constructs a new tree map containing the same mappings as the given * map, ordered according to the <em>natural ordering</em> of its keys. * All keys inserted into the new map must implement the {@link * Comparable} interface. Furthermore, all such keys must be * <em>mutually comparable</em>: {@code k1.compareTo(k2)} must not throw * a {@code ClassCastException} for any keys {@code k1} and * {@code k2} in the map. This method runs in n*log(n) time. * * @param m the map whose mappings are to be placed in this map * @throws ClassCastException if the keys in m are not {@link Comparable}, * or are not mutually comparable * @throws NullPointerException if the specified map is null */ public TreeMap(Map<? extends K, ? extends V> m) { comparator = null; putAll(m); } /** * Constructs a new tree map containing the same mappings and * using the same ordering as the specified sorted map. This * method runs in linear time. * * @param m the sorted map whose mappings are to be placed in this map, * and whose comparator is to be used to sort this map * @throws NullPointerException if the specified map is null */ public TreeMap(SortedMap<K, ? extends V> m) { comparator = m.comparator(); try { buildFromSorted(m.size(), m.entrySet().iterator(), null, null); } catch (java.io.IOException cannotHappen) { } catch (ClassNotFoundException cannotHappen) { } }
四、总结
优点:
1、策略模式符合开闭原则。
2、避免使用多重条件转移语句,如 if...else...语句、switch 语句
3、使用策略模式可以提高算法的保密性和安全性。
缺点:
1、客户端必须知道所有的策略,并且自行决定使用哪一个策略类。
2、代码中会产生非常多策略类,增加维护难度。
补充:在这里说明下策略和委派的区别,委派他不一定用的到策略,但策略一定用的到委派