设计模式十七之策略模式
在现实生活中常常遇到实现某种目标存在多种策略可供选择的情况,例如,出行旅游可以乘坐飞机、乘坐火车、骑自行车或自己开私家车等,超市促销可以釆用打折、送商品、送积分等方法。
在软件开发中也常常遇到类似的情况,当实现某一个功能存在多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能,如数据排序策略有冒泡排序、选择排序、插入排序、二叉树排序等。
如果使用多重条件转移语句实现(即硬编码),不但使条件语句变得很复杂,而且增加、删除或更换算法要修改原代码,不易维护,违背开闭原则。如果采用策略模式就能很好解决该问题。
1. 模式的定义与特点
1.1 模式的定义
策略模式(Strategy):定义一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的替换不会影响使用的客户。策略模式通过对算法进行封装,把算法的实现和算法的责任分割开来,并委派给不同的对象对这些算法进行管理。
1.2 模式的特点
策略模式的优点有:
1. 多重条件语句不易维护,而使用策略模式可以避免多重条件语句;
2. 策略模式提供了一系列可供重用的算法族,恰当的使用继承可以把算法族的公共部分转移到父类里面,从而避免代码的重复;
3. 策略模式可以提供相同行为的不同实现,客户可以根据不同的时间和空间要求选择不同的;
4. 策略模式提供了对开闭原则的完美支持,可以在不修改原有代码的情况下,灵活增加新算法;
5. 策略模式把算法的使用外放到环境类中,而算法的实现移到具体策略类中,实现了两者的分离。
策略模式的缺点有:
1. 客户端必须理解所以策略算法的区别,以便适时的选择恰当的算法类;
2. 策略模式造成过多的策略类。
1.3 模式的使用场景
1. 系统有很多类,而他们的区别就仅仅是行为的不同;
2. 一个系统需要在多种算法中选择一种。
2. 模式的结构与实现
策略模式是准备一组算法,并将这组算法封装到一系列的策略类里面,作为抽象策略类的子类。策略模式的重心不是如何实现算法,而是如何组织这些算法,从而让程序结构更加灵活,具有更好的扩展性和灵活性。
2.1 模式的结构
策略模式的主要角色如下:
1. 抽象策略类(Strategy):定义了公共接口,各种不同的算法以不同的方式实现接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现;
2. 具体策略类(Concrete Strategy):实现了抽象策略类定义的接口,实现了具体的算法实现;
3. 环境类(Context):持有一个算法类的引用,最终给客户端调用。
2.2 模式的实现
抽象策略类
/**
* 抽象策略类 - 促销
*/
public interface PromotionStrategy {
void doPromotion();
}
具体策略类
/**
* 立减促销
*/
public class LiJianPromotionStrategy implements PromotionStrategy {
@Override
public void doPromotion() {
System.out.println("促销立减,商品价格减去促销价格...");
}
}
/**
* 返现促销
*/
public class FanXianPromotionStrategy implements PromotionStrategy {
@Override
public void doPromotion() {
System.out.println("返现促销,购买商品返现50元...");
}
}
/**
* 满减促销
*/
public class ManJianPromotionStrategy implements PromotionStrategy {
@Override
public void doPromotion() {
System.out.println("满减促销,满200减20...");
}
}
/**
* 无促销
*/
public class EmptyPromotionStrategy implements PromotionStrategy {
@Override
public void doPromotion() {
System.out.println("无促销活动");
}
}
环境类
/**
* 环境类 - 促销工厂
*/
public class PromotionStrategyFactory {
private static Map<String, PromotionStrategy> promotionStrategyMap = new HashMap<>();
private final static EmptyPromotionStrategy EMPTY_PROMOTION_STRATEGY = new EmptyPromotionStrategy();
static {
promotionStrategyMap.put(promotionEnum.FAN_XIAN.type, new FanXianPromotionStrategy());
promotionStrategyMap.put(promotionEnum.MAN_JIAN.type, new ManJianPromotionStrategy());
promotionStrategyMap.put(promotionEnum.LI_JIAN.type, new LiJianPromotionStrategy());
}
private PromotionStrategyFactory() {
}
public static PromotionStrategy getPromotionStrategy(String promotionType) {
PromotionStrategy promotionStrategy = promotionStrategyMap.get(promotionType);
return promotionStrategy == null ? EMPTY_PROMOTION_STRATEGY : promotionStrategy;
}
private enum promotionEnum {
MAN_JIAN("manjian", "满减"),
FAN_XIAN("fanxian", "返现"),
LI_JIAN("lijian", "立减");
private String type;
private String desc;
promotionEnum(String type, String desc) {
this.type = type;
this.desc = desc;
}
}
}
客户端
public class Client {
public static void main(String[] args) {
String promotionType = "lijian";
PromotionStrategy promotionStrategy = PromotionStrategyFactory.getPromotionStrategy(promotionType);
promotionStrategy.doPromotion();
promotionType = "manjian";
promotionStrategy = PromotionStrategyFactory.getPromotionStrategy(promotionType);
promotionStrategy.doPromotion();
}
}
# 运行结果如下:
促销立减,商品价格减去促销价格...
满减促销,满200减20...
3. 模式在开源软件中的应用
3.1 java.util.Comparator 类
public interface Comparator<T> {
int compare(T o1, T o2);
}
Comparator 就是作为一个抽象策略接口,拥有众多实现 compare 方法的具体策略,如 Arrays 类和 TreeMap 类。
public class Arrays {
public static <T> void sort(T[] a, int fromIndex, int toIndex,
Comparator<? super T> c) {
if (c == null) {
sort(a, fromIndex, toIndex);
} else {
rangeCheck(a.length, fromIndex, toIndex);
if (LegacyMergeSort.userRequested)
legacyMergeSort(a, fromIndex, toIndex, c);
else
TimSort.sort(a, fromIndex, toIndex, c, null, 0, 0);
}
}
}
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;
final int compare(Object k1, Object k2) {
return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
: comparator.compare((K)k1, (K)k2);
}
}
3.2 org.springframework.core.io.Resource 类
在 Spring 中提供资源访问的 Resource 接口,就是一个抽象策略类,实现该接口的众多具体策略类如:ClassPathContextResource、FileSystemContextResource等。
public interface Resource extends InputStreamSource {
boolean exists();
boolean isReadable();
boolean isOpen();
URL getURL() throws IOException;
URI getURI() throws IOException;
File getFile() throws IOException;
long contentLength() throws IOException;
long lastModified() throws IOException;
Resource createRelative(String var1) throws IOException;
String getFilename();
String getDescription();
}