策略模式
</br>
前言
很多人在阅读第三方框架(例如Retrofit)的时候,不太懂或者比较吃力,是因为我们缺少一些写代码的思维,看代码的设计的模式,今天,小菜我说下对策略模式的理解
</br>
介绍
- 专业说法:定义了一系列的算法,并将每个算法封装起来,而且使它们还可以相互替换,策略模式让算法独立于使用它的客户而独立变化
- 例子说法:假设,今天,我要为舍友煮一道菜,小菜我买了一块猪肉,但是,舍友开心的时候,是喜欢吃红烧肉,则我要做红烧肉;难过的时候,是喜欢吃焖猪肉,则我要做焖猪肉:
1.难过(策略)->做红烧肉(算法)
2.开心(策略)->做焖猪肉(算法)
</br>
改善前的代码
/**
* Created by Jenchar on 2016/7/30.
*/
public class Meat {
public static final String HAPPY="开心";
public static final String SAD="难过";
public static final String REDMEAT="红烧肉";
public static final String BRAISEMEAT="焖猪肉";
public static void main(String[] args) {
Meat meat=new Meat();
meat.chooseFood(SAD,BRAISEMEAT);
}
private void chooseFood(String fellings,String type) {
if(fellings.equals(HAPPY)){
codeRedMeat(type);
}else{
cookBraiseMeat(type);
}
}
private void cookBraiseMeat(String meat) {
System.out.println("他"+HAPPY+"的时候,"+"我要为他煮"+meat);
}
private void codeRedMeat(String meat) {
System.out.println("他"+SAD+"的时候,"+"我要为他煮"+meat);
}
}
输出结果:
他难过的时候,我要为他煮红烧肉
分析:
Meat类很明显的问题是并不是单一职责(简单来说,就是一个类只有一种功能),首先它承担了做红烧肉和做焖猪肉的职责,另一个问题就是通过if-else的形式来判断做哪一种。小菜想,当我舍友他发神经病,兴奋的时候,会喜欢吃炸猪肉呢,那么我就需要在Meat类中增加一个方法去做炸猪肉,并且要在 chooseFood(String fellings,String type)方法里又增加一个判断,代码如下:
</br>
/**
* Created by Jenchar on 2016/7/30.
*/
public class Meat {
public static final String HAPPY="开心";
public static final String SAD="难过";
public static final String REDMEAT="红烧肉";
public static final String BRAISEMEAT="焖猪肉";
/**
* 增加的
*/
public static final String EXCITED="兴奋";
public static final String FLYINGMEAT="炸猪肉";
public static void main(String[] args) {
Meat meat=new Meat();
meat.chooseFood(EXCITED,FLYINGMEAT);
}
private void chooseFood(String fellings,String type) {
if(fellings.equals(HAPPY)){
codeRedMeat(type);
}else if(fellings.equals(SAD)){
cookBraiseMeat(type);
/**
* 增加的
*/
}else if(fellings.equals(EXCITED)){
cookFlyingMeat(type);
}
}
/**
*增加的
*/
private void cookFlyingMeat(String type) {
System.out.println("他"+EXCITED+"的时候,"+"我要为他煮"+type);
}
private void cookBraiseMeat(String meat) {
System.out.println("他"+HAPPY+"的时候,"+"我要为他煮"+meat);
}
private void codeRedMeat(String meat) {
System.out.println("他"+SAD+"的时候,"+"我要为他煮"+meat);
}
}
输出结果:
他兴奋的时候,我要为他煮炸猪肉
再分析:
看看 chooseFood(String fellings,String type)这个方法,现在代码是不是很乱了,各种if-else语句缠绕在其中?当小菜的舍友,因为精神分裂,又想吃另外一种,那么,我又要在chooseFood增加if-else。当if-else语句多了,就会很容易看错,写错,而且,代码臃肿,小的项目,小的类虽然过得去,但是到了大的项目,必然会很乱,难以维护。所以,因这种情况 ,小菜我开始引进了一种设计模式,“策略模式”
</br>
改善后的代码
首先小菜我需要定义一个抽象的做肉接口,命名CookStrategy,代码如下:
/**
* 做肉的接品
*/
public interface CookStrategy {
/**
* 根据舍友的情感来做肉
* @param feelings
*/
void cook(String feelings);
}
接着对于做红烧肉还是焖猪肉,小菜我觉得应该都有一个独立的做肉策略类,这些类都实现上面说的CookStrategy接口,代码如下:
CookRedMeat.java:
/**
* 做红烧肉的类
*/
public class CookRedMeat implements CookStrategy{
public static final String REDMEAT="红烧肉";
/**
* 开始做他喜欢的红烧肉
* @param feelings
*/
@Override
public void cook(String feelings) {
System.out.println("他"+feelings+"的时候,"+"我要为他煮"+REDMEAT);
}
}
CookBraiseMeat.java:
/**
* 做焖肉的类
*/
public class CookBraiseMeat implements CookStrategy {
public static final String BRAISEMEAT="焖猪肉";
@Override
/**
* 开始做他喜欢的焖猪肉
* @param feelings
*/
public void cook(String feelings) {
System.out.println("他"+feelings+"的时候,"+"我要为他煮"+BRAISEMEAT);
}
}
最后,我们再创建一个的Meat类。
public class Meat {
public static final String HAPPY="开心";
public static final String SAD="难过";
/**
* 持有CookStrategy对象
*/
private CookStrategy mCookStrategy;
public static void main(String[] args) {
Meat meat=new Meat();
meat.setCookStrategy(new CookBraiseMeat());
meat.cookFood(SAD);
}
/**
* 把子类的引用赋值到mCookStrategy,就可以调用子类的方法。
* @param cookStrategy
*/
private void setCookStrategy(CookStrategy cookStrategy) {
mCookStrategy=cookStrategy;
}
private void cookFood(String feelings){
mCookStrategy.cook(feelings);
}
}
最后分析
通过改善前后的代码,可以看出:
- 前者:通过if-else解决问题,代码臃肿,逻辑复杂,难以升级与维护,没有结构可言
- 后者:通过建立接口,将不同的策略构建成一个具体的策略实现 ,通过不同的策略实现算法替换,从而更好地增加扩展性,直观性。
</br>
结论与使用场景
- 针对同一类型的问题,多种处理,仅仅是具体为行有差别(做红烧肉,做焖猪肉)
- 出现同一抽象类有多个子类,虽然使用if-else或者switch-case来选择子类的(根据舍友的心情来做肉)
- 需要安全地封装多种同一类型的操作时(做红烧肉,做焖猪肉这些操作时)
</br>
喜欢我的朋友,可以与我一起讨论问题,我也是学习者,希望与大学一起学习。