ID:嵌入式Hacker

作者: 可爱的东东

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

1.基础类2.工具类

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

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

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

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

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

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

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

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,一起来学习吧,欢迎各种收藏/转发/批评。

推荐阅读:

嵌入式编程专辑Linux 学习专辑C/C++编程专辑
Qt进阶学习专辑关注微信公众号『技术让梦想更伟大』,后台回复“m”查看更多内容,回复“加群”加入技术交流群。
长按前往图中包含的公众号关注

从“策略模式”聊聊“设计模式”有多重要?相关推荐

  1. 从java多态到策略模式_设计模式中的多态——策略模式详解

    2. 策略模式详解 2.1 策略模式定义 策略模式定义了一系列算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户端而独立的变化. 可以使用多态进行类比来理解策略模 ...

  2. python策略模式_设计模式(python实现):策略模式

    策略模式简单说和小时候我们玩的玩具差不多,一堆零部件通过不同的拼凑构成几个不同的机器人. 1.举个栗子 我们买了一个机器人,同时这个机器人配了三把武器,三把武器可以替换使用 2.Show in Cod ...

  3. java计数器策略模式_java设计模式(二十一)--策略模式

    对于策略模式,我在很多面试题上看到过考察这一类的问题,这种模式也的确比较好用. 我感觉这种模式就是将不同实现的方法放到一个接口中,然后通过实现这个接口来实现不同的运行结果,这种模式有三部分构成: 策略 ...

  4. 策略模式-大话设计模式

    策略模式(Strategy):它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户. 一.需求 实现一个商场收银软件,满足满减.打折.正常收费. 二.代 ...

  5. 商场促销——策略模式(设计模式)

    商场收银时,如何促销,用打折还是返利,其实都是一些算法,用工厂来生成算法对象,这没有错,但算法本身只是一种策略,最重要的是这些算法是随时都可能互相替换的,就这点变化,而封装变化点是我们面向对象的一种很 ...

  6. java策略模式_Java设计模式之策略模式详解

    本文实例为大家分享了Java策略模式,供大家参考,具体内容如下 1.策略模式(Strategy Pattern)是一种比较简单的模式,也叫做政策模式(PolicyPattern). 定义如下: Def ...

  7. 代理模式 委派模式 策略模式_设计模式 - 委派模式

    理解 首先委派模式不属于23种设计模式. 所谓委派,个人理解是:将为达到最终结果的事情交给其他人或中间人来干,我只要最终结果,其他的事情,由我委派的人来安排. 更直白的表达就是,比如,我们想要盖一栋楼 ...

  8. Java设计模式之行为型:策略模式

    一.背景: 在开发中经常遇到这种情况,实现某个功能有多种算法策略,我们可以根据不同环境或者条件选择不同的算法策略来完成该功能,比如查找.排序等,一种常用方式是硬编码在一个类中,如需要提供多种查找算法, ...

  9. 大话设计模式笔记(二)——商品促销 策略模式

    第二章商品促销--策略模式 需求:商品价格计算,并增加折扣条件 工厂模式UML图 在工厂模式中,调用端代码会通过折扣工厂类生成折扣对象,折扣对象调用折扣方法.这里关联了两个类,工厂类和抽象折扣类. 策 ...

最新文章

  1. 动态数组 allocator
  2. android中ADT和SDK的关系(转)
  3. 图片二进制编码_python3从零学习-5.7.4、quopri编码与解码经过MIME转码打印数据
  4. Atitit.数据索引 的种类以及原理实现机制 索引常用的存储结构
  5. matlab 子图title的位置_Plotly_多个子图
  6. Python中的eval(),exec()以及其相关函数
  7. 2021年信息系统项目管理师案例分析第二题讲解
  8. CG CTF WEB 变量覆盖
  9. mysql 数据库 额外_mysql – 拥有“额外”数据库查询有多糟糕?
  10. SQL注入——SQLmap的进阶使用(十三)
  11. c++对数函数_DS-K1T105M-C 海康威视考勤门禁一体机 支持刷卡+密码 DS-K1T105E/M/C-C
  12. DOM属性用法速查手册
  13. Qt学习之路(54): 自定义拖放数据对象
  14. 固定资产中计算机软件类型,国税局固定资产的分类
  15. ajax教程初始模板,ajax实战入门模板
  16. linux更改jdk版本
  17. 普通话-命题说话11-20
  18. Packet Data Convergence Protocol (PDCP)阅读笔记
  19. asp.net909-大型社区包裹代收与分发系统
  20. chart.js使用学习——散点图

热门文章

  1. Xmap的NullPointerException
  2. matlab滤波器滤除低频直流信号,极低频滤波器MATLAB
  3. 【Trie图】Hiho4_Hihocoder
  4. (arduino基于W5100实现TCP客户端
  5. 从LCN的两阶段提交到TCC补偿事务方案
  6. 0x80073712_更新系统发生0x80073712错误,怎么解决?
  7. git fork 远端仓库协作开发
  8. vim中指定字符串的替换和删除
  9. 【GoLang】Map的遍历
  10. shell命令查阅端口信息_Linux服务器管理Shell经典命令