策略模式
定义:通过定义一系列的算法,并将每一个算法封装起来,可以使他们相互替换,并让算法可以在不影响到客户端的情况下变化
针对同一类型问题的多种处理方式,需要安全的封装同一类型操作时,可以使用策略模式
我们在开发中,自己曾经写过或者看到其他的开发者写过超级多if/else语句的情况,其实这是一种很烂的写法,接下来我们用一个简单的例子来说明一下,公共交通工具的计费计算,我们大部分的人很有可能就这样写了:
public class Calculator {
public static final int BUS = 1;
public static final int SUBWAY = 2;
public static final int TAXI = 3;
public int calculate(int km , int type) {
if (type == BUS) {
return busPrice(km);
} else if (type == SUBWAY) {
return subwayPrice(km);
} else if (type == TAXI) {
return taxiPrice(km);
}
return 0;
}
private int busPrice(int km) {
int price = 0;
// todo 计算坐公交的价格
return price;
}
private int subwayPrice(int km) {
int price = 0;
// todo 计算坐地铁的价格
return price;
}
private int taxiPrice(int km) {
int price = 0;
// todo 计算坐出租车的价格
return price;
}
}
// 调用
Calculator calculator = new Calculator();
int price = calculator.calculate(20, BUS);
这么一看好像还可以,代码写的挺整洁的,逻辑多么的的清晰。但是细细一琢磨,首先Calculator职责过重,干了好多事情,违背了单一职责;其次如果想要新增一种或多种方式,比如坐火车或飞机价格的计算方法,那么就得修改Calculator,违背了开闭原则。所以这种方法实现功能是很糟糕的。接下来我们用策略模式来重构:
interface CalculateStrategy {
int calculatePrice(int km);
}
public class BusStrategy implements CalculateStrategy{
@Override
public int calculatePrice(int km) {
int price = 0;
// todo 计算坐公交的价格
return 0;
}
}
public class SubwayStrategy implements CalculateStrategy{
@Override
public int calculatePrice(int km) {
int price = 0;
// todo 计算坐地铁的价格
return 0;
}
}
public class TaxiStrategy implements CalculateStrategy{
@Override
public int calculatePrice(int km) {
int price = 0;
// todo 计算坐出租车的价格
return 0;
}
}
class TrafficContext {
CalculateStrategy calculateStrategy;
public void setCalculateStrategy(CalculateStrategy calculateStrategy) {
this.calculateStrategy = calculateStrategy;
}
public int calculatePrice(int km) {
return calculateStrategy.calculatePrice(km);
}
}
// 调用
TrafficContext context = new TrafficContext();
context.setCalculateStrategy(new BusStrategy());
int price = context.calculatePrice(20);
上述策略模式的角色:
- Context——操作策略的上下文环境
- Stragety——策略的抽象
- BusStragety、SubwayStragety、TaxiStragety——具体的策略实现
可以很明显的看到上述示例通过建立抽象,在简化逻辑、结构的同时,增强了提供的可读性、稳定性、可扩展性
其实策略模式说简单了,就是多态的应用,将if/else变为抽象的一个过程,很好的诠释了开闭原则,给Context注入不同的实现,从而达到很好的扩展性,缺点就是随着策略的增多,子类也会变得繁多
状态模式
定义:当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类
一个对象的行为取决于它的状态,它必须根据状态改变它的行为
Android源码设计模式一书中举了一个电视机开关的例子,在这里我就不贴了,其实是和上面策略模式的结构是一模一样,只不过抽象类多了几个方法,我找了很多网友写的博客文章,大部分对于不同点描述的都是极其的晦涩难懂,根本看不明白,我甚至怀疑写文章的人自己都没明白。策略模式和状态模式的结构几乎完全一样,但是它们的目的、本质不一样。这句话其实并不好理解,我认为其实最关键的不同点用一句话概括就是:策略模式是开放的、暴露的,状态模式是封建的、内部的。打这么生动形象的个比方吧:
有两个小孩,一个小孩家庭普通,另一个小孩家庭富有。
家庭普通的那个小孩,他有固定的三个小玩伴,但是如果有碰到其它的小朋友,他也愿意去和新的小朋友玩,这个小孩就是策略模式。
家庭富有的那个小孩,他也有三个固定的小玩伴,但是这几个玩伴是他爸妈给他定好的,就是他家隔壁富豪家的孩子。这孩子很被动啊,自己决定不了,都是爸妈给定好的。
我们可以看一下策略模式和状态模式的上下文Context和调用时候的区别:
// 策略模式
class TrafficContext {
CalculateStrategy calculateStrategy;
public void setCalculateStrategy(CalculateStrategy calculateStrategy) {
this.calculateStrategy = calculateStrategy;
}
public int calculatePrice(int km) {
return calculateStrategy.calculatePrice(km);
}
}
// 调用
TrafficContext context = new TrafficContext();
context.setCalculateStrategy(new BusStrategy());
int price = context.calculatePrice(20);
// 状态模式
public class TvController {
TvState state;
private void setTvState(TvState tvState) {
state = tvState;
}
public void powerOn() {
setTvState(new PowerOnState());
}
public void powerOff() {
setTvState(new PowerOffState());
}
public void nextChannel() {
// todo 切换下一个频道
}
// 省略其他方法...
}
// 调用
TvController controller = new TvController();
controller.powerOn();
controller.nextChannel();
controller.powerOff();
controller.nextChannel();
我们大概能看出来策略模式是可以传入我们自己想传入的策略子类,策略模式根据自己的决策来更改行为的,是主动的
而状态模式是已经定好的,只能有那几个状态,对象的行为是根据状态而定的,所以状态模式是被动的
总结
无论是策略模式还是状态模式,都是一个多态的体现,都是为了能有更好的代码结构和更好的扩展性,我们不必刻意去追求某一个模式,当我们的经验丰富了以后,遵循设计原则,就会不知不觉的写出更好的代码。
在我们平时的开发中,常用到这种多态的可以举一些例子:
- 比如登录用户和未登录用户点击转发和评论,登录用户是可以正常使用对应的功能,而未登录用户是跳转到登录界面
- 同样,如果系统涉及到多种角色,不同角色也会有不同的行为,同样界面的显示也有一些差别
- 再比如我们常见的详情页有多个来源,有可能是列表点进来的,有可能是“我的”里面点进来,或者是什么什么,这些界面某些地方或者某些功能有不同
- 再比如某一个列表界面,有四种不同类型的同时复用这个界面,这四种列表的接口名不同,点击事件不同,界面某些地方不同,也是可以来用多态实现的
在我们平时的开发中很可能会根据接口返回来的type来搞不同的事情,即使是多态,我们可能仍需要在确定对象的时候使用if/else或者switch,但是仅限于在创建对象的时候,尽管如此,也比那种恶心的全局if/else整洁性和扩展性强多了