设计模式之美
王争
前Google工程师,《数据结构与算法之美》专栏作者
立即订阅
21545 人已学习
课程目录
已更新 70 讲 / 共 102 讲
0/6登录后,你可以任选6讲全文学习。
开篇词 (1讲)
开篇词 | 一对一的设计与编码集训,让你告别没有成长的烂代码!
免费
设计模式学习导读 (3讲)
01 | 为什么说每个程序员都要尽早地学习并掌握设计模式相关知识?
02 | 从哪些维度评判代码质量的好坏?如何具备写出高质量代码的能力?
03 | 面向对象、设计原则、设计模式、编程规范、重构,这五者有何关系?
设计原则与思想:面向对象 (11讲)
04 | 理论一:当谈论面向对象的时候,我们到底在谈论什么?
05 | 理论二:封装、抽象、继承、多态分别可以解决哪些编程问题?
06 | 理论三:面向对象相比面向过程有哪些优势?面向过程真的过时了吗?
07 | 理论四:哪些代码设计看似是面向对象,实际是面向过程的?
08 | 理论五:接口vs抽象类的区别?如何用普通的类模拟抽象类和接口?
09 | 理论六:为什么基于接口而非实现编程?有必要为每个类都定义接口吗?
10 | 理论七:为何说要多用组合少用继承?如何决定该用组合还是继承?
11 | 实战一(上):业务开发常用的基于贫血模型的MVC架构违背OOP吗?
12 | 实战一(下):如何利用基于充血模型的DDD开发一个虚拟钱包系统?
13 | 实战二(上):如何对接口鉴权这样一个功能开发做面向对象分析?
14 | 实战二(下):如何利用面向对象设计和编程开发接口鉴权功能?
设计原则与思想:设计原则 (12讲)
15 | 理论一:对于单一职责原则,如何判定某个类的职责是否够“单一”?
16 | 理论二:如何做到“对扩展开放、修改关闭”?扩展和修改各指什么?
17 | 理论三:里式替换(LSP)跟多态有何区别?哪些代码违背了LSP?
18 | 理论四:接口隔离原则有哪三种应用?原则中的“接口”该如何理解?
19 | 理论五:控制反转、依赖反转、依赖注入,这三者有何区别和联系?
20 | 理论六:我为何说KISS、YAGNI原则看似简单,却经常被用错?
21 | 理论七:重复的代码就一定违背DRY吗?如何提高代码的复用性?
22 | 理论八:如何用迪米特法则(LOD)实现“高内聚、松耦合”?
23 | 实战一(上):针对业务系统的开发,如何做需求分析和设计?
24 | 实战一(下):如何实现一个遵从设计原则的积分兑换系统?
25 | 实战二(上):针对非业务的通用框架开发,如何做需求分析和设计?
26 | 实战二(下):如何实现一个支持各种统计规则的性能计数器?
设计原则与思想:规范与重构 (11讲)
27 | 理论一:什么情况下要重构?到底重构什么?又该如何重构?
28 | 理论二:为了保证重构不出错,有哪些非常能落地的技术手段?
29 | 理论三:什么是代码的可测试性?如何写出可测试性好的代码?
30 | 理论四:如何通过封装、抽象、模块化、中间层等解耦代码?
31 | 理论五:让你最快速地改善代码质量的20条编程规范(上)
32 | 理论五:让你最快速地改善代码质量的20条编程规范(中)
33 | 理论五:让你最快速地改善代码质量的20条编程规范(下)
34 | 实战一(上):通过一段ID生成器代码,学习如何发现代码质量问题
35 | 实战一(下):手把手带你将ID生成器代码从“能用”重构为“好用”
36 | 实战二(上):程序出错该返回啥?NULL、异常、错误码、空对象?
37 | 实战二(下):重构ID生成器项目中各函数的异常处理代码
设计原则与思想:总结课 (3讲)
38 | 总结回顾面向对象、设计原则、编程规范、重构技巧等知识点
39 | 运用学过的设计原则和思想完善之前讲的性能计数器项目(上)
40 | 运用学过的设计原则和思想完善之前讲的性能计数器项目(下)
设计模式与范式:创建型 (7讲)
41 | 单例模式(上):为什么说支持懒加载的双重检测不比饿汉式更优?
42 | 单例模式(中):我为什么不推荐使用单例模式?又有何替代方案?
43 | 单例模式(下):如何设计实现一个集群环境下的分布式单例模式?
44 | 工厂模式(上):我为什么说没事不要随便用工厂模式创建对象?
45 | 工厂模式(下):如何设计实现一个Dependency Injection框架?
46 | 建造者模式:详解构造函数、set方法、建造者模式三种对象创建方式
47 | 原型模式:如何最快速地clone一个HashMap散列表?
设计模式与范式:结构型 (8讲)
48 | 代理模式:代理在RPC、缓存、监控等场景中的应用
49 | 桥接模式:如何实现支持不同类型和渠道的消息推送系统?
50 | 装饰器模式:通过剖析Java IO类库源码学习装饰器模式
51 | 适配器模式:代理、适配器、桥接、装饰,这四个模式有何区别?
52 | 门面模式:如何设计合理的接口粒度以兼顾接口的易用性和通用性?
53 | 组合模式:如何设计实现支持递归遍历的文件系统目录树结构?
54 | 享元模式(上):如何利用享元模式优化文本编辑器的内存占用?
55 | 享元模式(下):剖析享元模式在Java Integer、String中的应用
设计模式与范式:行为型 (11讲)
56 | 观察者模式(上):详解各种应用场景下观察者模式的不同实现方式
57 | 观察者模式(下):如何实现一个异步非阻塞的EventBus框架?
58 | 模板模式(上):剖析模板模式在JDK、Servlet、JUnit等中的应用
59 | 模板模式(下):模板模式与Callback回调函数有何区别和联系?
60 | 策略模式(上):如何避免冗长的if-else/switch分支判断代码?
61 | 策略模式(下):如何实现一个支持给不同大小文件排序的小程序?
62 | 职责链模式(上):如何实现可灵活扩展算法的敏感信息过滤框架?
63 | 职责链模式(下):框架中常用的过滤器、拦截器是如何实现的?
64 | 状态模式:游戏、工作流引擎中常用的状态机是如何实现的?
65 | 迭代器模式(上):相比直接遍历集合数据,使用迭代器有哪些优势?
66 | 迭代器模式(中):遍历集合的同时,为什么不能增删集合元素?
不定期加餐 (3讲)
加餐一 | 用一篇文章带你了解专栏中用到的所有Java语法
加餐二 | 设计模式、重构、编程规范等相关书籍推荐
春节特别加餐 | 王争:如何学习《设计模式之美》专栏?
免费
设计模式之美
登录|注册

61 | 策略模式(下):如何实现一个支持给不同大小文件排序的小程序?

王争 2020-03-23
上一节课,我们主要介绍了策略模式的原理和实现,以及如何利用策略模式来移除 if-else 或者 switch-case 分支判断逻辑。今天,我们结合“给文件排序”这样一个具体的例子,来详细讲一讲策略模式的设计意图和应用场景。
除此之外,在今天的讲解中,我还会通过一步一步地分析、重构,给你展示一个设计模式是如何“创造”出来的。通过今天的学习,你会发现,设计原则和思想其实比设计模式更加普适和重要,掌握了代码的设计原则和思想,我们甚至可以自己创造出来新的设计模式
话不多说,让我们正式开始今天的学习吧!

问题与解决思路

假设有这样一个需求,希望写一个小程序,实现对一个文件进行排序的功能。文件中只包含整型数,并且,相邻的数字通过逗号来区隔。如果由你来编写这样一个小程序,你会如何来实现呢?你可以把它当作面试题,先自己思考一下,再来看我下面的讲解。
你可能会说,这不是很简单嘛,只需要将文件中的内容读取出来,并且通过逗号分割成一个一个的数字,放到内存数组中,然后编写某种排序算法(比如快排),或者直接使用编程语言提供的排序函数,对数组进行排序,最后再将数组中的数据写入文件就可以了。
但是,如果文件很大呢?比如有 10GB 大小,因为内存有限(比如只有 8GB 大小),我们没办法一次性加载文件中的所有数据到内存中,这个时候,我们就要利用外部排序算法(具体怎么做,可以参看我的另一个专栏《数据结构与算法之美》中的“排序”相关章节)了。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《设计模式之美》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(39)

  • 唔多志
    "设计原则和思想比设计模式更加普适和重要",被这句话一下子点醒了。可以这样说,设计原则和思想是更高层次的理论和指导原则,设计模式只是这些理论和指导原则下,根据经验和场景,总结出来的编程范式。
    2020-03-23
    1
    31
  • 业余爱好者
    策略集合的信息也可以定义成枚举,可以放在数据库,可以放kv配置中心,等等。都是一样的道理。

    策略模式是对策略的定义,创建和使用解藕。定义和创建的过程与业务逻辑关系不大,写在一起会影响可读性。

    创建型模式是对创建和使用的解藕。

    做什么和怎么做是应该解藕的,使用者并不关心具体的细节。

    在业务逻辑中,与业务逻辑不大的代码应该放在外部。就像mvc的三层架构是业界的最佳实践。在service层调用dao层,而不是直接jdbc,因为如何操作数据库是个复杂的过程,却又与业务逻辑无关,所以单独抽出一层,代码结构变得更加清晰。

    声明式编程很火。它就是把使用和内部实现原理解藕。java,spring的各种注解,声明式事务等等。使用者一个注解解决所有问题,无需关心底层。

    业务逻辑无关的放在一起影响可读性,即使自己过一段时间看自己的代码也会迷惑。写代码要有用户思维,不光是提供的api满足kiss原则,内部的实现也是一样。能放在外面单位代码尽量怎么放在外面。只要能表达逻辑即可。
    2020-03-23
    7
  • 王飞洋
    GB = 1024 * 1024 * 1024吧
    2020-03-23
    3
    4
  • Jxin
    1.为了让调度的代码更优雅时使用。(就调度策略的代码而言,可读性高。理解结构后,阅读的心智负担低,因为调度的原理已经抽象成了共同的type查表,无需逐行检阅分支判断。像一些与持久数据相关的策略,有时为了兼容老数据或则平滑过度,无法全采用type查表,这时就需要结合if来 实现。所以采用if会让我误以为是这种场景,进而逐行检阅)。

    2.感觉问反了,应该是什么场景下采用ifelse和witch。毕竟这两个的场景少些。答案是,在逻辑不复杂又不好用卫语句时采用。能用卫语句就不要ifelse。因为个人而言,看到retuern或conti能很明确跳出逻辑,else脑袋得转一下,当然这可能是个人习惯影响。但ifelse还是只在逻辑简单,没啥嵌套(一个函数内部不宜嵌套过多),且语义符合的场景用好些。

    3.原则和思想毕竟是指导核心,是地图和尺子,自然是最重要的。但是不等于设计模式就比其不重要了。硬要说的话,我认为两个其实一样重要。设计模式是场景的积累,拉近了普通人与天才的差距,对我这种菜鸡来说可能比设计原则还重要。毕竟从设计原则到设计模式的出现,需要丰富经验,扎实知识和妙手偶得。如果没有设计模式的铺垫,普通人拿着设计模式的知识点要一路走上高质量代码的层级,实属不易。
    2020-03-23
    3
  • 攻城拔寨
    1. 奖励系统,奖励用户不同奖励是通过后台配置,从多个奖励中选择一种或者多种。
    后端代码根据不同的奖励类型找到对应的奖励策略类,执行对应的代码。
    2. 我觉得在 if-else 每个分支代码比较多的时候就应该拆分了。但是拆分不一定就能通过设计模式拆分,很多时候把分支逻辑抽成另一个方法就就够了,代码简洁,方便维护。
    我相当同意老师 “设计原则和思想比设计模式更加普适和重要。” 这句话。
    2020-03-23
    3
  • 韵呀
    最近在做公司的告警模块,对接收方式用了策略模式,以前也不知道这就是策略模式,只是觉得模块和思想解藕了。
    还有接收方式的选择策略和升级策略都用了策略模式。

    非要去掉if-else,我觉得如果分支不多,简单,以后可扩展性不强可以不去掉。否则就换成map缓存策略对象或者class。找到if-else条件和策略的对应关系。
    2020-03-23
    3
  • Jackey
    想想以前都没用过策略模式,一会到公司看看我的if-else有没有必要优化
    2020-03-23
    2
  • 黄林晴
    设计原则和思想比设计模式更加普适和重要,就像掌握算法思想比死扣每种算法的实现更重要!
    虽说实践是检验真理的唯一标准,但没有理论何来实践呢
    2020-03-23
    2
  • 忆水寒
    1、用过策略模式。我们在自研的rpc框架中为了灵活的设置线程数量(类似于netty里面的策略模式,默认情况下线程数量为CPU两倍,也可以指定)。
    2、在每个if else里面的代码都有相似功能,可以抽取出来。
    2020-03-23
    2
  • 南山
    1:随处可见,比如根据不同的配置选择不同的分配或者负载均衡策略。
    2:策略可预期的经常变更或者选择策略的条件判定方式比较复杂时可以考虑不使用if else 方式实现,反过来则if else 没什么毛病
    2020-03-23
    2
  • 徐旭
    要是不知道为什么,也是读不懂精心设计的代码
    2020-03-24
    1
  • 空白昵称
    很早前使用了类似策略模式的代码做了一个从各种渠道上传下载文件。(当时大学毕业,没看过策略模式,但是学过C++的多态)其实也就是基于接口而非实现编程思想。

    if-else使用,如果判断较少,且未来几乎不会有新需求要再加一个else时,保持if-else即可。其他的都可以使用策略模式。不过本节demo的实例,使用range来匹配,目前看是没问题。但是如果需求变化,需要增加一个根据数据特征的动态分配排序算法,那么这里实现就麻烦了。所以需要根据具体需求来看使用什么模式...

    2020-03-24
    1
  • iamjohnnyzhuang
    策略模式平时开发用的比较多,主要目的还是解耦策略的定义和使用。在Java中,比较喜欢用枚举策略模式。可以定义一个枚举类,提供静态方法根据传入的Type动态返回对应的枚举类。然后在枚举类中定义执行策略的抽象方法,这样迫使每个枚举都得去实现。对于策略不是非常复杂的情况下,这样可以集中管理这一批策略,新增策略的时候也只要在这个枚举类中添加。但是如果策略很复杂会导致这个类非常庞大,还是用传统的方法不同类对应不同逻辑更加优雅。总之,遵循KISS原则,怎么简单怎么来;同时尽量遵循开闭原则,添加策略的时候尽量少去改动代码。其实对于什么时候去掉IF/ELSE这个问题原理也是一样的。
    2020-03-24
    1
  • 朱晋君
    1.策略模式的使用还是挺多的,比如贷款系统中,业务系统需要查询不同渠道的征信报告,但是各渠道查询方式是不一样的,就用策略模式做了处理,客户端传入渠道编号来获取查询的渠道策略实现征信查询
    2.if-else或switch中每个分支的实现业务上是由共性的,可以抽象出来,这样可以用策略模式来取代分支。但是没有必要所有的都去掉

    注解方式的策略模式没有用过,动手实现了一下,同时总结了这2节内容:https://blog.csdn.net/zjj2006/article/details/105064557
    2020-03-24
    1
  • 辣么大
    思考题1:很久没写工程代码,但接触的科研算法对比性能时一般采用策略模式。通过输入不同的参数调用不同的算法。
    思考题2:通过策略模式的学习,可以看出实现简单的话一开始是不用使用策略模式的。if else中实现的逻辑都差不多,就是差不多都是干一件事(上节课例子中的购物,本节的文件排序)短的 if else相对易读,代码好理解。使用了策略模式要小心过度设计和影响了代码易读性。(对于不熟悉的项目刚看到或许会懵逼或者不好调试)
    2020-03-24
    1
  • jaryoung
    在过去的项目开发中,你有没有用过策略模式,都是为了解决什么问题才使用的?
    答:业务类型过多,未来还可能继续扩展
    你可以说一说,在什么情况下,我们才有必要去掉代码中的 if-else 或者 switch-case 分支逻辑呢?
    答:业务逻辑过于复杂(大部分都是条件判断),后期需求不断来。
    2020-03-23
    1
  • 潇潇雨歇
    设计原则和思想是道
    2020-03-23
    2
    1
  • Frank
    1,最近在做规则引擎,前端用户通过页面设置出业务执行的流程图,而流程图中包含数据源节点、条件判断节点、业务分发节点、业务事件执行节点。每一种节点执行的业务校验规则都不同,这个时候适合策略模式。使用策略模式的好处是:以后随着业务的发展很有可能出现其他类型的节点,所以这个时候采用策略模式非常合适,易扩展易维护。另外在整个流程流转的规则上采用了模板方法。
    2,当出现比较多的等值匹配的时候,这个时候使用switch case 更合适,结构清晰,可读性强,其他则使用if-else,当然if-else也可以进行等值比较。
    2020-03-23
    1
  • Heaven
    1.在之前,有一个用户拥有不同的用户组,对不同的用户组拥有这不同的处理逻辑,这就是一种策略模式,通过用户中的不同用户组名,来获取不同的用户组实例,执行其中的处理方式
    2.如果对于判断逻辑中,需要执行的逻辑较为复杂,可以抽取出相同的接口的话,就使用策略模式,将其抽取出接口,并利用策略工厂进行获取策略实例,从而执行对应处理
    2020-03-23
    1
  • 守拙
    在什么情况下,我们才有必要去掉代码中的 if-else 或者 switch-case 分支逻辑呢?

    根据KISS原则, 默认我们不会主动去优化if-else/switch-case.
    只有当if-else严重影响代码可读性与可维护性, 才考虑使用策略模式+工厂方法模式优化分支逻辑.
    2020-03-23
    1
收起评论
39
返回
顶部