设计模式——策略模式和状态模式

策略模式

定义:通过定义一系列的算法,并将每一个算法封装起来,可以使他们相互替换,并让算法可以在不影响到客户端的情况下变化

针对同一类型问题的多种处理方式,需要安全的封装同一类型操作时,可以使用策略模式

我们在开发中,自己曾经写过或者看到其他的开发者写过超级多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();

我们大概能看出来策略模式是可以传入我们自己想传入的策略子类,策略模式根据自己的决策来更改行为的,是主动的

而状态模式是已经定好的,只能有那几个状态,对象的行为是根据状态而定的,所以状态模式是被动的

总结

无论是策略模式还是状态模式,都是一个多态的体现,都是为了能有更好的代码结构和更好的扩展性,我们不必刻意去追求某一个模式,当我们的经验丰富了以后,遵循设计原则,就会不知不觉的写出更好的代码。

在我们平时的开发中,常用到这种多态的可以举一些例子:

  1. 比如登录用户和未登录用户点击转发和评论,登录用户是可以正常使用对应的功能,而未登录用户是跳转到登录界面
  2. 同样,如果系统涉及到多种角色,不同角色也会有不同的行为,同样界面的显示也有一些差别
  3. 再比如我们常见的详情页有多个来源,有可能是列表点进来的,有可能是“我的”里面点进来,或者是什么什么,这些界面某些地方或者某些功能有不同
  4. 再比如某一个列表界面,有四种不同类型的同时复用这个界面,这四种列表的接口名不同,点击事件不同,界面某些地方不同,也是可以来用多态实现的

在我们平时的开发中很可能会根据接口返回来的type来搞不同的事情,即使是多态,我们可能仍需要在确定对象的时候使用if/else或者switch,但是仅限于在创建对象的时候,尽管如此,也比那种恶心的全局if/else整洁性和扩展性强多了

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值