设计模式系列_策略模式入门

对于技术领域的知识点,我个人喜欢简单地划分为2类:

1.基础类2.工具类

我判断一个知识点属于哪一类的主要依据有2点:

1.这个知识点是否经久不衰;2.这个知识点是否没有替代品;

如果上述2点都满足,则我会认为这是基础类知识,属于可以长期投资的价值股; 典型的例如操作系统、数据结构、Linux环境编程、软件模式(设计模式、架构模式...)等我会归类为基础类知识;

而例如 Qt、Git、Docker、甚至各种编程语言(C、C++、Java、Python)等知识点我都会暂时归类为工具类。不要误会,这些都只是我个人的喜好,并没有要贬低你心爱的技术的意思,只要是你在工作里需要重度使用的技术,你都应该把它归类为基础类,以便提醒自己需要深耕该技术。

说了这么多,无非是想告诉你,我认为设计模式很重要,仅此而已。即便你从事的是底层软件相关的工作,你只用 C 语言进行开发,情况也是一样的。

下面是正文(策略模式入门)

相关参考:

•Head First Design Pattern•Android 源码设计模式解析与实战•Android Design Patterns and Best Practice关注并且后台回复“设计模式”即可下载

需求:
模拟现实生活中的鸭子,鸭子会游泳,鸣叫,飞。

1. 利用继承?

阶段1

一个父类 Duck 定义 鸭子 的一些特性,子类继承它的特性,并覆盖(override) 父类的部分特性。至此没什么问题,子类共同拥有父类的特性,消除了代码的重复性。

新的需求:
一些鸭子(例如野鸭)是会飞的,我们要让 Duck 具有 fly() 的特性。

阶段2

在 Duck 类中加上 fly() 方法:

引入新的问题:

在父类 Duck 类中增加 fly() 后,导致所有的子类鸭子都会 fly 了。

而真实的情况是:橡皮鸭子 RubberDuck 不应该会 fly。

思考:

1.对代码的局部修改,影响层面不只是局部;

2.继承虽然能复用代码,但是它并不完美;

阶段3

在 RubberDuck 中 override fly() 方法,方法内什么都不做。

利用继承来提供Duck的行为的做法,存在什么缺点?

1.大量的代码在多个 Duck 子类中冗余,例如 RubberDuck 的 fly();

2.父类Duck的改动会牵连所有子类Duck也要跟着改动

2. 使用接口效果如何?

阶段4

让部分而非全部鸭子可飞或者可叫。

把 fly() 从父类中提取出来定义为 Flyable 接口,只有会飞的鸭子才实现此接口 。quack() 也类似地定义为 Quackable 接口。

例如橡皮鸭 RubberDuck 不会飞,所有它不实现 Flyable 接口。

思考:
1.这样避免了 “阶段3” 中类似 RubberDuck->fly() 的冗余代码,但是又造成了 fly() 毫无复用性的问题( Java 接口内不能有代码实现);

2.设计原则:把会变化的部分独立出来,不要和不需要变化的代码混在一起(Identify the aspects of your application that vary and separate them from what stays the same.);

3.各种设计模式都有一样的目的:把会变化的部分取出并封装起来,以便以后轻易的改动和扩展此部分,而不影响不需要变化的其他部分;

4.软件开发的不变真理: 软件总是要变化的;

3. 划分变化与不变的部分

总结前面的做法:
1.阶段2: 行为 ( fly 和 quake ) 来自于 Duck 类内具体实现 ( concrete implementation );

2.阶段4: 行为来自于继承某个接口的子类内的专属实现 ( specialized implementation );

3.无论是阶段2还是阶段4,都是针对具体实现编程。即每一种会飞的鸭子,都必须要有自己专属的关于 fly 的具体实现(例如 MallarDuck->fly() / RedheadDuck->fly()),而无法共用同一类 fly 的方式(例如FlyWithWings / FlyWithRocket / FlyNoWay);

提取出变化的部分:
将会变化的鸭子的行为 ( 包括fly行为和quake行为 ) 从 Duck 类中提取出来。

4. 设计鸭子的行为

阶段5

让鸭子的行为可以动态改变:
用接口代表 ( represent ) 行为,定义2个接口:FlyBehavior and QuackBehavior。

行为的每一个具体实现(implementation)都会实现(implement) 对应的接口:

前人的经验:

1.设计原则:要针对接口编程,不要针对具体实现(implementation)编程 / Program to an interface, not an implementation;

2.这里的接口是一个“抽象概念”,并不是专门指Java 里的interface;

3.针对接口编程的另一个说法是针对超类型(supertype)编程,超类型在编程语法上一般是一个抽象类(abstract class)或者接口(interface);

实现鸭子的行为:

这样做有什么好处?

1.多个行为之间相互独立,可以轻松地添加更多的行为接口;

2.可以轻松地添加更多的行为实现;
例如添加一个用火箭来飞行的行为:添加了一个FlyRocketPowered类,它实现FlyBehavior接口即可

3.具体的行为实现都被封装在XXXBehavior接口内,使用者不用关心具体的行为细节;

4.行为接口 XXXBehavior 可以供其他 client 复用,例如鸡;

整合鸭子的行为:
1.鸭子将飞行和叫的行为委托 (delegate) 给别人处理,而不是在 Duck 类或者子类中自己来实现;

2.在 Duck 类中加入行为实例 xxxBehavior 和行为执行函数 performXxx();

3.实现 performQuack();

public class Duck {
    QuackBehavior quackBehavior;
}
public void performQuack() {
    quackBehavior.quack();
}

4.初始化行为实例变量,例如在 MallardDuck 类中:

public class MallardDuck extends Duck {
    public MallardDuck() {
        quackBehavior = new Quack();
        flyBehavior = new FlyWithWings();
}

这里的做法并不完美:因为 MallardDuck 的构造函数里使用了 Quack 类 这个具体实现,即 MallardDuck 和 具体实现类 Quack 绑定在了一起。

由于xxxBehavior是可以在运行时被改变的,所以目前的做法已有足够的弹性了,暂时不用理会构造函数里的瑕疵。

5.允许动态地设置鸭子的行为,添加setXxxBehavior():

public void setFlyBehavior(FlyBehavior fb) {
    flyBehavior = fb;
}

到这里,模拟鸭子的整个设计就已经完成了,整个设计框图如下:

5. 测试当前代码

测试代码:

public class MiniDuckSimulator {
    public static void main(String[] args) {
        Duck mallard = new MallardDuck();
        mallard.performQuack();
        mallard.performFly();
        mallard.setFlyBehavior(new FlyRocketPowered());
        mallard.performFly();
    }
}

测试效果:

%java MiniDuckSimulator
Quack
I’m flying!!
I'm flying with a rocker

根据测试结果总结一下:
1."有1个"比“是1个”更好,鸭子的行为不是继承来的,而是和行为对象组合而来;

2.设计原则:多用组合,少用继承(Favor composition over inheritance);

3.模拟鸭子使用的设计模式:策略模式;

什么是策略模式?

指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。

策略模式三要素:
1.定义了一族算法(业务规则);

2.封装了每个算法;

3.这族的算法可互换代替(interchangeable);

再举一个例子:
在一款游戏里,有不同的角色(国王、皇后、骑士...),角色有不同的武器(斧头、剑、刀),该怎么设计?

6. 最后的总结

懒人们专用:

7. 更多实践

有哪些开源项目使用或者借鉴了策略模式?

•Android

还等什么?赶紧分析起来吧~~

你和我各有一个苹果,如果我们交换苹果的话,我们还是只有一个苹果。但当你和我各有一个想法,我们交换想法的话,我们就都有两个想法了。如果你也对嵌入式系统开发有兴趣,并且想和更多人互相交流学习的话,请关注我的公众号:ESexpert,一起来学习吧,欢迎各种收藏/转发/批评。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值