设计模式之美

设计模式之美

[toc]

01 | 为什么说每个程序员都要尽早地学习并掌握设计模式相关知识?

  • 为什么要学习设计模式相关的知识?
    • 应对面试中的设计模式相关问题;
    • 告别写被人吐槽的烂代码;
    • 提高复杂代码的设计和开发能力;
    • 让读源码、学框架事半功倍;
    • 为你的职场发展做铺垫。

02 | 从哪些维度评判代码质量的好坏?如何具备写出高质量代码的能力?

    1. 如何评价代码质量的高低?
    • 主观性
    • 可读性、可维护性、灵活、优雅、简洁等
    1. 最常用的评价标准有哪几个?
    • 可维护性、可读性、可扩展性、灵活性、简洁性、可复用性、可测试性。
    1. 如何才能写出高质量的代码?
    • 面向对象设计思想
    • 设计原则
    • 设计模式
    • 编码规范
    • 重构技巧

03 | 面向对象、设计原则、设计模式、编程规范、重构,这五者有何关系?

04 | 理论一:当谈论面向对象的时候,我们到底在谈论什么?

    1. 什么是面向对象编程?
    • 面向对象编程是一种编程范式或编程风格。它以类或对象作为组织代码的基本单元,并将封装、抽象、继承、多态四个特性,作为代码设计和实现的基石 。
    1. 什么是面向对象编程语言?
    • 支持类或对象的语法机制,并有现成的语法机制,能方便地实现面向对象编程四大特性(封装、抽象、继承、多态)的编程语言
    1. 如何判定一个编程语言是否是面向对象编程语言?
    • 严格:支持类、对象、四大特性
    • 宽泛:类、对象
    1. 面向对象编程和面向对象编程语言之间有何关系?
    • 面向对象编程不一定要用面向对象语言,使用面向对象语言写出的代码也不一定是面向对象语言的
    1. 什么是面向对象分析和面向对象设计?
    • 面向对象分析就是要搞清楚做什么,面向对象设计就是要搞清楚怎么做。
    • 产出是类图

05 | 理论二:封装、抽象、继承、多态分别可以解决哪些编程问题?

    1. 关于封装特性
    • private、protected、public
    • 意义:
      • 保护数据不被随意修改,提高代码的可维护性
      • 仅暴露有限的必要接口,提高类的易用性。
    1. 关于抽象特性
    • 讲如何隐藏方法的具体实现
    • 意义:
      • 提高代码的可扩展性、维护性,修改实现不需要改变定义,减少代码的改动范围
      • 处理复杂系统的有效手段,能有效地过滤掉不必要关注的信息。
    1. 关于继承特性
    • 表示类之间的 is-a 关系
    • 单继承和多继承
    • 意义:解决代码复用的问题。
    1. 关于多态特性
    • 多态是指子类可以替换父类
    • 意义:提高代码的扩展性和复用性

06 | 理论三:面向对象相比面向过程有哪些优势?面向过程真的过时了吗?

    1. 什么是面向过程编程?什么是面向过程编程语言?
    • 面向对象编程以类为组织代码的基本单元,面向过程编程则是以过程(或方法)作为组织代码的基本单元
    • 数据和方法相分离
    • 不支持丰富的面向对象编程特性
    1. 面向对象编程相比面向过程编程有哪些优势?
    • 更能应对这种复杂类型的程序开发
    • 更加丰富的特性(封装、抽象、继承、多态)
    • 易扩展、易复用、易维护。
    • 更加人性化、更加高级、更加智能。

07 | 理论四:哪些代码设计看似是面向对象,实际是面向过程的?

  • 三种违反面向对象编程风格的典型代码设计
    • 滥用 getter、setter 方法
    • Constants 类、Utils 类的设计问题
    • 基于贫血模型的开发模式
      • 贫血模型:是指领域对象里只有get和set方法,或者包含少量的CRUD方法,所有的业务逻辑都不包含在内而是放在Business Logic层。

08 | 理论五:接口vs抽象类的区别?如何用普通的类模拟抽象类和接口?

    1. 抽象类和接口的语法特性
    • 抽象类不允许被实例化,只能被继承。
    • 它可以包含属性和方法。
    • 子类继承抽象类,必须实现抽象类中的所有抽象方法。
    • 接口只能声明方法。
    • 类实现接口的时候,必须实现接口中声明的所有方法。
    1. 抽象类和接口存在的意义
    • 抽象类:代码复用问题
    • 接口:解耦问题,隔离接口和具体的实现,提高代码的扩展性。
    1. 抽象类和接口的应用场景区别
    • 抽象类:is-a,代码复用问题
    • 接口:has-a,抽象

09 | 理论六:为什么基于接口而非实现编程?有必要为每个类都定义接口吗?

  • “接口”就是一组“协议”或者“约定”,是功能提供者提供给使用者的一个“功能列表”。
  • 原则:可以将接口和实现相分离,封装不稳定的实现,暴露稳定的接口
  • 别名:基于抽象而非实现编程
  • 越抽象、越顶层、越脱离具体某一实现的设计,越能提高代码的灵活性,越能应对未来的需求变化。好的代码设计,不仅能应对当下的需求,而且在将来需求发生变化的时候,仍然能够在不破坏原有代码设计的情况下灵活应对。
  • 作用提高代码扩展性、灵活性、可维护性最有效的手段之一
  • 具体做法:
    • 函数的命名不能暴露任何实现细节。
    • 封装具体的实现细节
    • 为实现类定义抽象的接口。

10 | 理论七:为何说要多用组合少用继承?如何决定该用组合还是继承?

    1. 为什么不推荐使用继承?
    • 继承层次过深、过复杂,也会影响到代码的可维护性
    1. 组合相比继承有哪些优势?
    • 继承主要有三个作用:表示 is-a 关系,支持多态特性,代码复用。而这三个作用都可以通过组合、接口、委托三个技术手段来达成
    • 组合还能解决层次过深、过复杂的继承关系影响代码可维护性的问题。
    1. 如何判断该用组合还是继承?
    • 多用组合少用继承
    • 类之间的继承结构稳定,层次比较浅,关系不复杂,我们就可以大胆地使用继承

11 | 实战一(上):业务开发常用的基于贫血模型的MVC架构违背OOP吗?

  • 基于贫血模型的传统开发模式,是典型的面向过程的编程风格。
  • 基于充血模型的 DDD 开发模式,是典型的面向对象的编程风格。

15 | 理论一:对于单一职责原则,如何判定某个类的职责是否够“单一”?

    1. 如何理解单一职责原则(SRP)?
    • 一个类只负责完成一个职责或者功能。不要设计大而全的类,要设计粒度小、功能单一的类。单一职责原则是为了实现代码高内聚、低耦合,提高代码的复用性、可读性、可维护性
    1. 如何判断类的职责是否足够单一?
    • 类中的代码行数、函数或者属性过多;
    • 类依赖的其他类过多,或者依赖类的其他类过多;
    • 私有方法过多;
    • 比较难给类起一个合适的名字;
    • 类中大量的方法都是集中操作类中的某几个属性。
    1. 类的职责是否设计得越单一越好?
    • 会降低内聚性,也会影响代码的可维护性。

16 | 理论二:如何做到“对扩展开放、修改关闭”?扩展和修改各指什么?

    1. 如何理解“对扩展开放、对修改关闭”?
    • 添加一个新的功能,应该是通过在已有代码基础上扩展代码(新增模块、类、方法、属性等),而非修改已有代码(修改模块、类、方法、属性等)的方式来完成。
    • 开闭原则并不是说完全杜绝修改,而是以最小的修改代码的代价来完成新功能的开发。
    1. 如何做到“对扩展开放、修改关闭”?
    • 扩展意识、抽象意识、封装意识。
    • 高代码扩展性的方法有:多态、依赖注入、基于接口而非实现编程,以及大部分的设计模式(比如,装饰、策略、模板、职责链、状态)。

17 | 理论三:里式替换(LSP)跟多态有何区别?哪些代码违背了LSP?

  • 子类可以改变函数的内部实现逻辑,但不能改变函数原有的“约定”
  • 约定包括:函数声明要实现的功能;对输入、输出、异常的约定;甚至包括注释中所罗列的任何特殊说明。
  • 里式替换原则跟多态的区别:
    • 里式替换:子类的设计要保证在替换父类的时候,不改变原有程序的逻辑及不破坏原有程序的正确性。
    • 多态:一种代码实现的思路

18 | 理论四:接口隔离原则有哪三种应用?原则中的“接口”该如何理解?

    1. 如何理解“接口隔离原则”?
    • 如果部分接口只被部分调用者使用,我们就需要将这部分接口隔离出来,单独给这部分调用者使用,而不强迫其他调用者也依赖这部分不会被用到的接口。
    • 那接口的设计要尽量单一,不要让接口的实现类和调用者,依赖不需要的接口函数。
    1. 接口隔离原则与单一职责原则的区别
    • 单一职责原则针对的是模块、类、接口的设计
    • 接口隔离原则:
      • 更侧重于接口的设计
      • 站在使用者的角度考虑职责是否单一

19 | 理论五:控制反转、依赖反转、依赖注入,这三者有何区别和联系?

    1. 控制反转
    • 流程的控制权从程序员“反转”给了框架。
    1. 依赖注入
    • 我们不通过 new 的方式在类内部创建依赖类的对象,而是将依赖的类对象在外部创建好之后,通过构造函数、函数参数等方式传递(或注入)给类来使用。
    1. 依赖注入框架
    • 由框架来自动创建对象、管理对象的生命周期、依赖注入等原本需要程序员来做的事情。
    1. 依赖反转原则
    • 高层模块不依赖低层模块

20 | 理论六:我为何说KISS、YAGNI原则看似简单,却经常被用错?

  • KISS: 尽量保持简单。
  • YAGNI:你不会需要它。
  • 如何写出满足 KISS 原则的代码
    • 不要使用同事可能不懂的技术来实现代码;
    • 不要重复造轮子,要善于使用已经有的工具类库;
    • 不要过度优化。

21 | 理论七:重复的代码就一定违背DRY吗?如何提高代码的复用性?

    1. DRY 原则
    • 三种典型的代码重复:实现逻辑重复、功能语义重复和代码执行重复
    1. 代码复用性
    • 减少代码耦合
    • 满足单一职责原则
    • 模块化
    • 业务与非业务逻辑分离
    • 通用代码下沉
    • 继承、多态、抽象、封装
    • 应用模板等设计模式
    1. 我们可以不写可复用的代码,但一定不能写重复的代码。

22 | 理论八:如何用迪米特法则(LOD)实现“高内聚、松耦合”?

    1. 如何理解“高内聚、松耦合”?
    • 高内聚: 就是指相近的功能应该放到同一个类中,不相近的功能不要放到同一类中
    • 松耦合: 在代码中,类与类之间的依赖关系简单清晰。
    1. 如何理解“迪米特法则”?
    • 不该有直接依赖关系的类之间,不要有依赖;有依赖关系的类之间,尽量只依赖必要的接口。

27 | 理论一:什么情况下要重构?到底重构什么?又该如何重构?

    1. 重构的目的:为什么重构(why)?
    • 保持项目质量、锻炼代码能力、提升成就感。
    1. 重构的对象:重构什么(what)?
    • 大规模高层次的重构:
      • 代码分层、模块化、解耦、梳理类之间的交互关系、抽象复用组件等等。
    • 小规模低层次的重构:
      • 规范命名、注释、修正函数参数过多、消除超大类、提取重复代码等等
    1. 重构的时机:什么时候重构(when)?
    • 持续重构
    1. 重构的方法:如何重构(how)?
    • 大规模:有组织、有计划、分阶段
    • 小规模:立即、持续

28 | 理论二:为了保证重构不出错,有哪些非常能落地的技术手段?

    1. 什么是单元测试?
    • 单元测试是代码层面的类或函数的测试
    1. 为什么要写单元测试?
    • 发现 bug
    • 集成测试的补充,覆盖边界情况
    • TDD
    1. 如何编写单元测试?
    • 测试用例
    • 覆盖各种输入、异常、边界情况
    • 单元测试的认知:
      • 编写单元测试尽管繁琐,但并不是太耗时;
      • 我们可以稍微放低对单元测试代码质量的要求;
      • 覆盖率作为衡量单元测试质量的唯一标准是不合理的;
      • 单元测试不要依赖被测代码的具体实现逻辑;
      • 单元测试框架无法测试,多半是因为代码的可测试性不好。
    1. 单元测试为何难落地执行?
    • 为编写单元测试预留时间
    • 正确认识单元测试

29 | 理论三:什么是代码的可测试性?如何写出可测试性好的代码?

    1. 什么是代码的可测试性?
    • 针对代码编写单元测试的难易程度
    1. 编写可测试性代码的最有效手段:依赖注入
    1. 常见的 Anti-Patterns
    • 常见的测试不友好的代码有下面这 5 种:
      • 代码中包含未决行为逻辑
      • 滥用可变全局变量
      • 滥用静态方法
      • 使用复杂的继承关系
      • 高度耦合的代码

30 | 理论四:如何通过封装、抽象、模块化、中间层等解耦代码?

    1. “解耦”为何如此重要?
    • 保证代码松耦合、高内聚,是控制代码复杂度的有效手段。
    • 代码高内聚、松耦合,也就是意味着,代码结构清晰、分层模块化合理、依赖关系简单、模块或类之间的耦合小,那代码整体的质量就不会差。
    1. 代码是否需要“解耦”?
    • 衡量标准:
      • 看修改代码是否牵一发而动全身。
      • 依赖关系图的复杂性来判断是否需要解耦重构。
    1. 如何给代码“解耦”?
    • 封装与抽象、中间层、模块化
    • 单一职责原则、基于接口而非实现编程、依赖注入、多用组合少用继承、迪米特法则等
    • 观察者模式

31 | 理论五:让你最快速地改善代码质量的20条编程规范(上)

    1. 关于命名
    • 命名的关键是能准确达意
    • 借助小下文简化命名:类的信息来简化属性、函数的命名,利用函数的信息来简化函数参数的命名。
    • 命名要可读、可搜索。不要使用生僻的、不好读的英文单词来命名
    • 接口命名:前缀“I”;后缀“Impl”
    • 抽象类的命名:前缀“Abstract”;不带
    1. 关于注释
    • 注释的目的就是让代码更容易看懂
    • 做什么、为什么、怎么做、如何用
    • 类和函数一定要写注释

32 | 理论五:让你最快速地改善代码质量的20条编程规范(中)

    1. 函数、类多大才合适?
    • 函数的代码行数不要超过 50 行的大小
    1. 一行代码多长最合适?
    • 最好不要超过 IDE 显示的宽度
    1. 善用空行分割单元块
    1. 四格缩进还是两格缩进?
    • 两格缩进
    • 一定不要用 tab 键缩进。
    1. 大括号是否要另起一行?
    • 同一行
    1. 类中成员的排列顺序
    • 字母序从小到大排列
    • 先写成员变量后写函数
    • 先静态后普通
    1. 最好能跟业内推荐的风格、开源项目的代码风格相一致

33 | 理论五:让你最快速地改善代码质量的20条编程规范(下)

    1. 关于编码技巧
    • 将复杂的逻辑提炼拆分成函数和类。
    • 通过拆分成多个函数或将参数封装为对象的方式,来处理参数过多的情况。
    • 函数中不要使用参数来做代码执行逻辑的控制。
    • 函数设计要职责单一。
    • 移除过深的嵌套层次,方法包括:去掉多余的 if 或 else 语句,使用 continue、break、return 关键字提前退出嵌套,调整执行顺序来减少嵌套,将部分嵌套逻辑抽象成函数。
    • 用字面常量取代魔法数。
    • 用解释性变量来解释复杂表达式,以此提高代码可读性。
    1. 统一编码规范
    • 统一的编码规范

38 | 总结回顾面向对象、设计原则、编程规范、重构技巧等知识点

74 | 总结回顾23种经典设计模式的原理、背后的思想、应用场景等

    1. 单例模式
    • 创建全局唯一的对象
    • 实现方式:饿汉式、懒汉式、双重检测、静态内部类、枚举。
    • 进程唯一单例、线程唯一单例、集群唯一单例、多例
    • 缺点:
      • 单例对 OOP 特性的支持不友好
      • 单例会隐藏类之间的依赖关系
      • 单例对代码的扩展性不友好
      • 单例对代码的可测试性不友好
      • 单例不支持有参数的构造函数
    1. 工厂模式
    • 简单工厂、工厂方法、抽象工厂

75 | 在实际的项目开发中,如何避免过度设计?又如何避免设计不足?

78 | 开源实战二(上):从Unix开源开发学习应对大型复杂项目开发

  • 如何应对复杂软件开发?
    1. 封装与抽象
    2. 分层与模块化
    3. 基于接口通信
    4. 高内聚、松耦合
    5. 为扩展而设计
    6. KISS 首要原则
    7. 最小惊奇原则

79 | 开源实战二(中):从Unix开源开发学习应对大型复杂项目开发

  • 如何长期保证代码质量,让代码长期可维护?
    1. 吹毛求疵般地执行编码规范
    2. 编写高质量的单元测试
    3. 不流于形式的 Code Review
    4. 开发未动、文档先行
    5. 持续重构、重构、重构
    6. 对项目与团队进行拆分

80 | 开源实战二(下):从Unix开源开发学习应对大型复杂项目开发

  • 为什么要 Code Review?
    1. Code Review 践行“三人行必有我师”
    2. Code Review 能摒弃“个人英雄主义”
    3. Code Review 能有效提高代码可读性
    4. Code Review 是技术传帮带的有效途径
    5. Code Review 保证代码不止一个人熟悉
    6. Code Review 能打造良好的技术氛围
    7. Code Review 是一种技术沟通方式
    8. Code Review 能提高团队的自律性
  • 怎么做 Code Review?

99 | 总结回顾:在实际软件开发中常用的设计思想、原则和模式

  • 对象设计分为四个环节:
    • 划分职责并识别出有哪些类
    • 定义类及其属性和方法
    • 定义类之间的交互关系
    • 组装类并提供执行入口
  • 两个设计思想:
    • 基于接口而非实现的设计思想
    • 多用组合少用继承的设计思想

100 | 如何将设计思想、原则、模式等理论知识应用到项目中?

  • 建议你把专栏中讲到的经典的设计思想、原则、模式,打印出来贴在电脑旁
1 Star2 Stars3 Stars4 Stars5 Stars (尚未评分)
Loading...

发表评论

邮箱地址不会被公开。 必填项已用*标注