第六章抽象工厂模式实在是不好理解,我觉得也不能似懂非懂就算了,毕竟这次决定第一遍通读起码要搞懂各种设计模式的基本思想,在源码中如何体现都可以等到阅读代码的能力上去了慢慢消化,但是概念什么的,还是不能就这么跳过了,既然不太好理解,就先放一放,没准什么时候就豁然开朗了,先读读后面的模式换换脑子。
第七章 时势造英雄——策略模式
在开发过程中经常会实现某个功能而需要加入多种算法或策略,然后根据具体情况来选择不同的算法来实现该功能。针对这种情况,一般的常规方法是把多种算法写在一个类里面,然后根据if...else...或者switch...case...条件判断语句来决定具体使用哪种算法。但是这样做的话,这个类就会显得太臃肿,维护成本相应变高,维护时发生错误的几率也会相应增高,而且如果我们需要增加一种新的算法的时候,就得修改源代码,还要增加新的if...else...判断或者新的case...语句。这很明显违背了开闭原则和单一职责原则,所以,才产生了策略模式。
策略模式将这些算法或者策略抽象出来,提供一个统一的接口,不同的算法或者策略有不同的实现类,这样客户端就可以通过注入不同的实现类来实现算法或者策略的动态替换了,可扩展性和可维护性也就增强了。
1.定义
策略模式定义了一系列的算法,并将每一个算法封装起来,它们直接可以相互替换。策略模式让算法独立于使用它的客户,并可以独立变化。
2.使用场景
1).针对同一类型问题的多种处理方式,仅仅是具体行为有差别时。
2).需要安全的封装多种同一类型的操作时。
3).出现同一抽象类有多个子类,而又需要使用if...else...或者switch...case...来选择具体子类时。
3.简单实现
以乘坐交通工具的费用计算为例,乘坐公交车、地铁、出租车的费用计算方式肯定是不一样的,但是它们的功能都是一样的,都是计算出行成本的。
我们可以先定义一个价格计算接口:
public interface CalculateStrategy {
/**
* Description:根据路程来计算价格,
* Date:2017/5/11
* @param km: 路程
* @return :计算出来的价格
*/
int calculatePrice(int km);
}
然后是各种交通工具的具体计算策略类:
public class BusStrategy implements CalculateStrategy {
/**
* Description:以北京公交车为例,10公里之内1块钱,超过10公里后每5公里1块钱
* Date:2017/5/11
*/
@Override
public int calculatePrice(int km) {
if (km <= 10) {
//10公里以内
return 1;
} else {
//超过10公里的距离
int beyondKm = km - 10;
//超过的距离是5公里的几倍
int multiple = beyondKm / 5;
//超过的距离对5公里取余
int fraction = beyondKm % 5;
int price = 1 + multiple * 1;
//如果最后取余大于5公里,也按5公里算,如果刚好是5公里的倍数则不用.
return fraction > 0 ? ++price : price;
}
}
}
public class SubwayStrategy implements CalculateStrategy {
/**
* Description:6公里(含)内3块钱,6-12公里(含)4块钱,12-22公里(含)5块钱,22-32公里(含)6块钱,再增加距离则统一简化为7块钱
* Date:2017/5/11
*/
@Override
public int calculatePrice(int km) {
if (km <= 6) {
return 3;
} else if (km > 6 && km <= 12) {
return 4;
} else if (km > 12 && km < 22) {
return 5;
} else if (km > 22 && km < 32) {
return 6;
} else {
return 7;
}
}
}
然后增加一个扮演客户端的类:
public class TrafficCalculator {
private static TrafficCalculator sTrafficCalculator = new TrafficCalculator();
public static TrafficCalculator getInstance() {
return sTrafficCalculator;
}
private CalculateStrategy mCalculateStrategy;
public void setmCalculateStrategy(CalculateStrategy mCalculateStrategy) {
this.mCalculateStrategy = mCalculateStrategy;
}
public int calculatePrice(int km) {
return mCalculateStrategy.calculatePrice(km);
}
}
这样一来,如果要加入新的策略,不用去修改源代码了,比如要增加出租车的计价策略,只需要新增一个出租车价格计算策略类:
public class TaxiStrategy implements CalculateStrategy {
/**
* Description:简化为价格为路程的2倍,不要在意这些细节
* Date:2017/5/11
*/
@Override
public int calculatePrice(int km) {
return km * 2;
}
}
计算价格时:
TrafficCalculator mTrafficCalculator = TrafficCalculator.getInstance();
mTrafficCalculator.setmCalculateStrategy(new BusStrategy());
System.out.println("Bus Price:" + mTrafficCalculator.calculatePrice(20));
mTrafficCalculator.setmCalculateStrategy(new SubwayStrategy());
System.out.println("Subway Price:" + mTrafficCalculator.calculatePrice(20));
mTrafficCalculator.setmCalculateStrategy(new TaxiStrategy());
System.out.println("Taxi Price:" + mTrafficCalculator.calculatePrice(20));
通过这个示例可以很明显的看出来,虽然使用if...else...判断的话可能只需要一个类甚至一个方法就可以了,看起来比较简单,但是确实显得很臃肿,而且逻辑复杂,难以维护,几乎没有什么结构可言。但是使用策略模式的话则是建立抽象,将每种策略变成一个具体的策略实现类,通过不同的算法替换,在简化逻辑,优化结构的同时,增强了代码的可读性、可扩展性和可维护性。
4.总结
策略模式主要是用来分离算法,在相同的策略抽象下有不同的具体实现策略。策略模式很好的诠释了开闭原则,也就是定义抽象,注入不同的实现,以达到增强可扩展性的目的。
优点:
1).结构清晰,使用简单。
2).耦合度降低,扩展方便。
3).封装更彻底,数据也更安全。
缺点:
1).随着策略的增加,子类会相应增多。