设计模式笔记

目录

设计模式

基础概念

面向对象

面向对象的四大特性

面向对象和面向过程的区别

抽象类和接口的区别

设计原则

基于接口而非实现编程,或者说基于抽象而非实现编程

组合优于继承,多用组合少用继承

SOLID原则

单一职责原则(SRP,Single Responsibility Principle)

开闭原则(OCP,Open Closed Principle)

里式替换原则(LSP,Liskov Substitution Principle)

接口隔离原则(ISP,Interface Segregation Principle)

依赖反转原则(DIP,Dependency Inversion Principle)

KISS原则(Keep It Simple and Stupid )

YAGNI原则(You Ain’t Gonna Need It)

DRY原则(Don’t Repeat Yourself)

迪米特法则

怎么提升代码复用性

实战

代码重构

编程规范

设计模式

创建型

单例模式

工厂模式

结构型

行为型

项目实战

思考


设计模式

  • 基础概念

    • 代码质量指标
      • 可读性
      • 扩展性
      • 可维护性
      • 复用性
      • 可测试性
  • 面向对象

    • 是什么
      • 没有明确一定的定义,自己解释以类或对象作为组织代码的基本单元,并将封装、抽象、继承、多态四个特性,作为代码设计和实现的基石 。
      • 主流的编程范式
        • 面向过程
        • 面向对象
        • 函数式编程
    • 面向对象
      • 面向对象分析(OOA,Object Oriented Analysis)
      • 面向对象设计(OOD,Object Oriented Design)
      • 面向对象编程(OOP,Object Oriented Programming)
    • 面向对象的四大特性
      • 封装
        • 封装也叫作信息隐藏或者数据访问保护,说的是怎么信息影藏或保护访问数据;
      • 抽象
        • 抽象,说的是如何隐藏方法的具体实现;
      • 继承
        • 继承,用来表示类之间的is-a关系
        • 意义
          • 代码复用
          • 结构美感
      • 多态
        • 多态简单说就是指子类可以替换父类
        • 实现
          • 继承重写父函数
          • 接口类
          • duck-typing
            • 只要两个类具有相同的方法,就可以实现多态,并不要求两个类之间有任何关系;
        • 意义
          • 提高可扩展性和复用性;
    • 面向对象和面向过程的区别
      • 是什么
        • 面向过程编程
          • 以过程(可以理解为方法、函数、操作)作为组织代码的基本单元,以数据(可以理解为成员变量、属性)与方法相分离为最主要的特点。面向过程风格是一种流程化的编程风格,通过拼接一组顺序执行的方法来操作数据完成一项功能。
        • 面向过程编程语言
          • 最大的特点是不支持类和对象两个语法概念,不支持丰富的面向对象编程特性(比如继承、多态、封装),仅支持面向过程编程。
      • 面向对象编程相比面向过程编程有哪些优势?
        • OOP更容易模块化,更容易应对大规模复杂程序的开发;
        • OOP风格的代码更容易进行复用、扩展、维护;
      • 哪些代码设计看似是面向对象,实际是面向过程的?
        • 私有数据的修改暴露在外
          • 公有的方法可以直接修改私有的数据
          • 公有的方法返回私有数据的地址,通过地址依然可以修改私有数据;
        • 滥用全局变量和全局方法
          • 不加思考的添加全局变量和全局函数。添加的时候要想一下,是不是有必要加?如果要加能不能做好分类?
        • 定义数据和方法分离的类
        • 两者并不是对立的,面向对象的编程语言可以写出面向过程风格的代码,面向过程的编程语言也可以写出面向对象风格的代码,只是特性不一样,实现起来有容易和不容易的区别。他俩还是包容的,比如面向对象的每个类中的实现,那不就是面向过程的风格嘛。
    • 抽象类和接口的区别
      • 是什么
        • 抽象类
          • C++中,必须有一个纯虚函数,因为有个纯虚函数,所以不能实例化,所以子类必须重写纯虚函数。
        • 接口
          • (C++没有),接口不能包含属性(也就是成员变量),所有方法只能声明,类实现接口的时候必须实现接口中的所有函数;
      • 区别
        • 抽象类还是个类,和子类之前是继承的关系,是is-a的关系。接口就不是个类,和实现类是has-a的关系。
    • 实战
      • 基于贫血模型的MVC架构和基于充血模型的DDD架构
        • 是什么
          • 贫血模型
            • 数据和方法分离,一个类只包含数据,一个类只包含方法;
          • MVC架构
            • M表示Model,V表示View,C表示Controller。整个项目,分为三层,数据层,逻辑层,展示层(暴露对外的)
          • 充血模型
            • 数据和方法封装在同一个类中,和贫血模式相反。
          • DDD架构(领域驱动设计)
            • 用来指导如何解耦业务系统
          • 实际web和app开发
            • 现在很多Web或者App项目都是前后端分离的,后端负责暴露接口给前端调用。一般就将后端项目分为Repository层、Service层、Controller层。其中,Repository层负责数据访问,Service层负责业务逻辑,Controller层负责暴露接口。
            • 贫血模型
              • controller负责暴露接口,service和Bo负责核心业务逻辑,repository和entity负责数据存取。
            • 充血模型
              • controller,repository和贫血模型一样,但是原来service层的数据和方法分离和在一起,核心逻辑会合在Domain中,service还是会保留和repository层交互、跨领域模型的业务聚合功能、幂等事务等非功能性的工作。
        • 怎么选
          • 业务逻辑简单用贫血,复杂用充血。
      • 基于接口鉴权聊一聊面向需求分析,设计,编码
        • 需求分析
          • 需求->需求描述
          • 需求分析实际上是一个不断迭代优化的过程,先给出一个粗糙,基础的方案,然后在一轮一轮跌倒,建议4~5轮;
        • 面向对象设计
          • 需求描述->类的设计
          • 怎么做
            • 划分职责进而识别出有哪些类
              • 根据需求描述,把其中涉及的功能点,一个一个罗列出来,然后再去看哪些功能点职责相近,操作同样的属性,是否应该归为同一个类。(如果需求很复杂,就模块化的分析)
              • 拆解功能点
                • 拆解出来的功能点尽可能的小,每个功能点只做一件事。
            • 定义类及其属性和方法
              • 不应该属于这个类的属性和方法,不应该被放到这个类里,做好划分。
            • 定义类与类之间的交互关系
              • 四种关系:泛化,实现,组合,依赖。(知道就行完全不用管都是啥定义)
            • 将类组装起来并提供执行入口
              • 暴露给外部的执行接口。
  • 设计原则

    • 是什么
      • 经验总结的条款和建议;
    • 基于接口而非实现编程,或者说基于抽象而非实现编程

      • 是什么
        • 就是设计的时候尽可能的抽象,封装更多的东西而不暴露在外,这样以后要是修改只是在内部改实现,接口不动。
      • 为什么
        • 这条原则的设计初衷是,将接口和实现相分离,封装不稳定的实现,暴露稳定的接口。上游系统面向接口而非实现编程,不依赖不稳定的实现细节,这样当实现发生变化的时候,上游系统的代码基本上不需要做改动。
      • 怎么做
        • 1.函数的命名不能暴露任何实现细节。
        • 2.封装具体的实现细节,不暴露任何细节。
        • 3.实现类定义抽象的接口。使用者依赖接口,而不是具体的实现来编程。
    • 组合优于继承,多用组合少用继承

      • 为什么
        • 继承层次过深,继承关系过于复杂会影响到代码的可读性和可维护性。
      • 怎么选
        • 继承层次不太深,关系不是很复杂,就用继承,反之就用组合。
    • SOLID原则

      • 单一职责原则(SRP,Single Responsibility Principle)
        • 是什么
          • 一个类或者模块只负责完成一个职责(或者功能)。
        • 为什么
          • 复用性,扩展性,可读性
        • 怎么做
          • 判断类的职责是否足够单一
            • 类中的代码行数、函数或属性过多,看起来,用起来难受;
            • 类依赖的其他类过多,或者依赖类的其他类过多;
            • 私有方法过多;
            • 比较难给类起一个合适名字,名字太概括;
            • 类中大量的方法都是集中操作类中的某几个属性;
          • 类的职责是不是越单一越好?
            • 类的职责越单一,越会降低内聚性,导致后续修改麻烦,所以得根据具体业务场景来定。
      • 开闭原则(OCP,Open Closed Principle)
        • 是什么
          • 对扩展开放、对修改关闭
            • 衡量扩展性的最好指标
          • 添加一个新的功能应该是,在已有代码基础上扩展代码(新增模块、类、方法等),而非修改已有代码(修改模块、类、方法等)。
        • 为什么
          • 扩展性
        • 怎么做
          • 扩展还是修改大小不同的维度上看不一样,只要没有破环原有的代码的正常运行(原来的接口没变)和破坏原有的单元测试,就OK。
          • 识别未来可能变化的和不变化的部分,把可能变化的部分封装起来,隔离变化,提供抽象的不变接口。
      • 里式替换原则(LSP,Liskov Substitution Principle)
        • 是什么
          • 子类对象能够替换程序中父类对象出现的任何地方,并且保证原来程序的逻辑行为不变以及正确性不被破坏。
          • 里式替换原则和多态的区别
            • 里式替换原则是一种设计原则,指导子类如何设计;多态是一种语法,是代码实现;里式替换原则指导设计之后,可以用多态去实现具体代码。
        • 为什么
          • 用来指导继承关系中子类该如何设计。复用性,扩展性
        • 怎么做
          • 更落地的描述
            • 按照协议来设计,子类在设计的时候,要遵守父类的行为约定(或者叫协议),子类可以改变内部的实现逻辑,不能改变函数原有的行为约定。
            • 行为约定包括
              • 函数声明要实现的功能;
              • 对输入、输出、异常的约定;
              • 注释中所罗列的任何特殊说明;
      • 接口隔离原则(ISP,Interface Segregation Principle)
        • 是什么
          • 调用者不应该强迫依赖它不需要的接口,也就是说暴露给调用者的接口应该都是他会用到的。
        • 为什么
          • 复用性,扩展性
        • 怎么做
          • 接口的理解
            • 一组接口集合
              • 如果部分接口只是被部分用户使用,那就需要接口隔离,调用者用哪部分接口就给他那部分,用不到的不强迫暴露给他。
            • 单个接口或者函数
              • 部分调用者只需要接口的部分功能,那就继续拆分接口。
        • 接口隔离原则和单一原则的区别
          • 单一原则,针对的是模块,类,接口的设计。
          • 接口隔离原则,更侧重于接口的设计,从调用者使用的情况思考。
          • 接口隔离原则提供了一个判断接口是否满足单一原则的标准,那就是所有调用者是不是使用了接口的所有功能。
      • 依赖反转原则(DIP,Dependency Inversion Principle)
        • 是什么
          • 控制反转(IOC,Inversion Of Control)
            • 控制反转并不是一种具体的实现技巧,而是一个比较笼统的设计思想,一般用来指导框架层面的设计。“反转”指的是流程的控制权从程序员反转到了框架。
          • 依赖注入(DI,Dependency Injection)
            • 不通过new()的方式在类内部创建依赖类对象,而是将依赖的类对象在外部创建好之后,通过构造函数、函数参数等方式传递(或注入)给类使用。
          • 依赖注入框架(DI Framework)
            • 通过框架自动完成依赖注入的操作。
          • 依赖反转原则
            • 和控制反转类似,把流程的控制从程序员反转到框架,主要是指导架构层面的设计。
        • 为什么
          • 主要用来指导框架层面的设计。
    • KISS原则(Keep It Simple and Stupid )

      • 是什么
        • 尽量保持简单。
      • 为什么
        • 可读性
      • 怎么做
        • 不要使用同事可能不懂的技术来实现代码。
        • 不要重复造轮子,要善于使用已经有的工具类库。
        • 不要过度优化。不要过度使用一些奇技淫巧来优化代码,牺牲代码的可读性;比如,位运算代替算术运算、复杂的条件语句代替if-else、使用一些过于底层的函数等。
    • YAGNI原则(You Ain’t Gonna Need It)

      • 是什么
        • 不要去设计当前用不到的功能,不要去编写当前用不到的代码。可以为以后可能的功能预留扩展,但是别提前实现它。
      • 为什么
        • 指导要不要做的事情
      • KISS原则和YAGNI原则的区别
        • KISS原则讲的是如何做的问题,YAGNI原则讲的是要不要做的问题。
    • DRY原则(Don’t Repeat Yourself)

      • 是什么
        • 不要写重复的代码。
      • 为什么
        • 可读性,维护性,复用性
      • 怎么做
        • 三种典型的代码重复
          • 实现逻辑重复
            • 逻辑重复,但是功能语义不重复,其实不是真的重复,不违反DRY原则。可以简单理解为粗看起来重复,细一看不重复。
          • 功能语义重复
            • 功能语义重复,但是逻辑不重复,其实是真的重复,应该用统一的规则合二为一。
          • 代码执行重复
            • 代码执行重复,是违法DRY原则的,没必要还耗时。
          • 注:
            • 功能语义说的是一个意思,就是功能。
    • 迪米特法则

      • 是什么
        • 高内聚和松耦合
          • 高内聚
            • 用来指导类本身的设计
            • 就是指相近的功能应该放到同一个类中,不相近的功能不要放到同一个类中。
          • 松耦合
            • 用来指导类与类之间依赖关系的设计
            • 就是在代码中,类与类之间的依赖关系简单清晰,即使两个类有依赖关系,一个类的代码改动不会或者很少导致依赖类的代码改动。
        • 迪米特法则(最小知识原则,The Least Knowledge Principle)
          • 不该有直接依赖关系的类之间,不要有依赖;有依赖关系的类之间,尽量只依赖必要的接口。类越独立越好。
          • 说白了,就是你是你,我是我,你我很不一样,你我的关系很清楚,最好没有或者只有一种关系。
      • 为什么
        • 实现代码的“高内聚、松耦合”。
    • 怎么提升代码复用性

      • 减少代码耦合
      • 满足单一职责原则
      • 模块化
      • 业务与非业务逻辑分离
        • 越是跟业务无关的代码越是容易复用,越是针对特定业务的代码越难复用。
      • 通用代码下沉
        • 如果代码有分层,一般只允许上层代码调用下层代码及同层代码之间的调用,杜绝下层代码调用上层代码。所以,通用的代码我们尽量下沉到更下层。
      • 继承、多态、抽象、封装
      • 应用模板等设计模式
    • 实战

      • 业务开发
        • 几个理念
          • 一个人怎么想,都想不全的,要学会借鉴。
          • 写代码的过程本身就是一个修修改改,不停调整的过程,别老想一次就位,要善用迭代的方式。
          • 理论就在那里摆着,习惯,思维,设计和写代码的过程中时不时想想才是最重要的。
        • 业务开发包括哪些工作?
          • 接口设计
          • 数据库设计
          • 业务模型设计(业务逻辑)
        • 为什么要分MVC三层开发?
          • Controller层负责接口暴露,Repository层负责数据读写,Service层负责核心业务逻辑。
          • 分层能起到隔离变化的作用,也就是封装变化。
          • 分层能起到隔离关注点的作用,三层的关注点不同,职责分明,更符合单一职责原则。
          • 分层能提高代码的可测试性。
            • 当要测试包含核心业务逻辑的Service层代码的时候,我们可以用mock的数据源替代真实的数据库,注入到Service层代码中进行测试。
          • 分层能应对系统的复杂性。
        • VO、BO、Entity存在的意义是什么?
          • VO、BO、Entity三个类虽然代码重复,但功能语义不重复,从职责上讲是不一样的。
          • 为了尽量减少每层之间的耦合,把职责边界划分明确,每层都会维护自己的数据对象,层与层之间通过接口交互。
  • 代码重构

    • 是什么
      • 重构是一种对软件内部结构的改善,目的是在不改变软件的可见行为(对外部的)的情况下,使其更易理解,维护成本更低。
    • wwwh
      • 重构的目的(why)
        • 保证代码质量,不至于让代码腐化到无可救药的地步。
        • 优秀的代码或架构不是一开始就能完全设计好的。
        • 避免设计时过度设计。
      • 到底重构什么(what)
        • 大规模高层次重构
          • 是对顶层代码设计的重构,包括:系统、模块、代码结构、类与类之间的关系等的重构。
        • 小规模低层次重构
          • 是对代码细节的重构,主要是针对类、函数、变量等代码级别的重构。
      • 什么时候需要重构(when)
        • 持续重构,把持续重构作为开发的一部分,一种开发习惯。等到代码烂到不行了再重构其实已经晚了,特别容易搞出四不像。
      • 用什么方法进行重构(how)
        • 大规模高层次重构
          • 需要组织,划分小段,有计划的进行,小步快跑,完成一段测试一段,时刻让代码处于一个可运行的状态。
          • 具体操作见本模块最后内容
        • 小规模低层次重构
          • 只要看到了,有时间,就顺手做了最好。
    • 怎么保证重构不出错?单元测试
      • 是什么
        • 单元测试
          • 用来测试一个类和函数是否都按照预期的逻辑执行。这是代码层级的测试。说白了就是一小块一小块的测试。
        • 集成测试
          • 测试整个系统或者某个功能模块,是端到端的测试。
      • 为什么
        • 发现代码中的bug,交出bug free的代码。
        • 发现代码设计上的问题,判断代码的可测试性是不是OK。
        • 是对集成测试的有力补充。
          • 集成测试和黑盒测试很难穷举所有的可能性,单元测试就是很好的补充,先尽量保证每个单元的代码功能是没问题的。
        • 写单元测试的过程本身就是代码重构的过程。
        • 阅读单元测试能帮助快速熟悉代码。
        • 单元测试是TDD(测试驱动开发(Test-Driven Development)可落地执行的改进方案。
      • 怎么写
        • 就是把测试用例翻译成代码的过程。
        • 单元测试不要依赖被测代码的具体实现逻辑,只关注输入和输出就行。
    • 代码的可测试性
      • 是什么
        • 就是针对代码编写单元测试的难易程度。
      • 怎么做
        • 最有效手段就是依赖注入,通过mock(假的模拟输入)的方法写单元测试。
      • 常见的可测试性不好的代码
        • 代码中包含未决行为逻辑
        • 滥用可变全局变量
        • 滥用静态方法
        • 使用复杂的继承关系
        • 高度耦合的代码
    • 大型重构的方法:解耦
      • 解耦为啥这么重要?
        • 最主要的就是应对复杂性。
      • 代码是否需要解耦?
        • 就看代码是不是符合高内聚低耦合,有个可行的办法,把模块间和类间的关系图画出来,看是不是很复杂。
      • 如何给代码解耦?
        • 封装与抽象
        • 引入中间层
          • 大型重构引入中间层的可行方法
            • 第一阶段:引入一个中间层,包裹老的接口,提供新的接口定义。
            • 第二阶段:新开发的代码依赖中间层提供的新接口。
            • 第三阶段:将依赖老接口的代码改为调用新接口。
            • 第四阶段:确保所有的代码都调用新接口之后,删除掉老的接口。
        • 模块化
        • 满足设计思想和原则
  • 编程规范

    • 命名注释
      • 命名
        • 能准确达意,问自己一个问题,别人看的时候能不能容易理解?
        • 命名要可读、可搜索
          • 可读:好读出来,不影响交流。
          • 可搜索:命名习惯都一样,比如都用selectXX表示查询。
        • 命名接口和抽象类
          • 接口:加前缀“I”
          • 抽象类:加前缀"Abstract"
          • 注:以上是java的习惯,具体项目大家要统一。
      • 注释
        • 注释的目的就是让代码更容易看懂。
        • 注释的内容
          • 做什么、为什么、怎么做。可以已(what)(why) (how)开头。难用的可以写明如何用。
    • 代码风格
      • 不同语言和不同项目组不一样,一致就好。
    • 编程技巧
      • 把代码分割成更小的单元块,善于将大块的复杂逻辑提炼成类或者函数,屏蔽掉细节。
      • 避免函数参数过多,3~4个最好。
        • 职责是否单一,可以接着划分。
        • 把参数封装成对象,如果函数是暴露给外部的接口,这样以后修改参数也不用修改接口的调用。
      • 勿用函数参数来控制逻辑
        • 不符合单一职责原则,最好分两个函数。
      • 不要使用过深的嵌套层次
        • 最好不要超过两层,可以通过修改代码顺序逻辑,或者把深嵌套封装成函数。
      • 学会使用解释性变量
        • 常量取代魔法数字
          • 比如用PI代替3.1415。
        • 使用解释性变量来解释复杂表达式
          • 比如,使用一个变量来表示复杂的逻辑表达式。
    • 实战
      • 如何发现代码质量问题
        • 常规checklist
          • 目录设置是否合理、模块划分是否清晰、代码结构是否满足“高内聚、松耦合”?
          • 是否遵循经典的设计原则和设计思想(SOLID、DRY、KISS、YAGNI、LOD等)?
          • 设计模式是否应用得当?是否有过度设计?
          • 代码是否容易扩展?如果要添加新功能,是否容易实现?
          • 代码是否可以复用?是否可以复用已有的项目代码或类库?是否有重复造轮子?
          • 代码是否容易测试?单元测试是否全面覆盖了各种正常和异常的情况?
          • 代码是否易读?是否符合编码规范(比如命名和注释是否恰当、代码风格是否一致等)?
        • 业务需求checklist
          • 代码是否实现了预期的业务需求?
          • 逻辑是否正确?是否处理了各种异常情况?
          • 日志打印是否得当?是否方便debug排查问题?
          • 接口是否易用?是否支持幂等、事务等?
          • 代码是否存在并发问题?是否线程安全?
          • 性能是否有优化空间,比如,SQL、算法是否可以优化?
          • 是否有安全漏洞?比如输入输出校验是否全面?
      • 程序出错应该返回啥?
        • 返回错误码
          • 就是自定义的错误信息,比如XXERROR, -1等。尽量不使用。
        • 返回NULL值
          • 尽量不使用,想想指针,想想空指针。
        • 返回空对象
          • 函数返回的数据是字符串类型或者集合类型的时候,我们可以用空字符串或空集合替代NULL值,来表示不存在的情况。
        • 抛出异常对象
          • 最常用的函数出错处理方式,因为异常可以携带更多的信息,比如函数调用栈,而且异常也可以把正常逻辑和异常逻辑的处理分离开。
          • 如何处理异常对象
            • 直接吞掉
            • 直接往上抛出
            • 包裹成新的异常抛出
  • 设计模式

    • 是什么
      • 针对开发中经常遇到的一些设计问题,总结出来的一套解决方案或者设计思路;作用就是解耦。
    • 创建型

      • 创建型设计模式主要解决“对象的创建”问题。是对象创建和其余的解耦。
      • 单例模式
        • 是什么
          • 一个类只允许创建一个对象或者实例,提供一个全局访问点。
        • 为什么
          • 避免资源访问冲突、表示业务概念上的全局唯一类。
        • 怎么做
          • 如果有些数据在系统中只应该保存一份,那处理这个数据的类就适合设计成单列类。
          • 饿汉式
            • 类加载的时候,instance静态实例就创建并初始化好。
            • 不支持延迟加载,就是不支持用的时候再初始化,不过也不是啥缺点,因为总要初始化,初始化不在系统启动的时候进行,也会在运行到相关功能的时候初始化,难道在运行的时候再初始化好?不见得。
            • C++的实现线程不安全。
          • 双重检测
            • 就是在饿汉式的基础上加个锁,保证线程安全,就是在instance实例化的时候加个锁。
        • 单例模式的问题
          • 注:单例有点硬编码的样子。
          • 单例对OOP特性的支持不友好
          • 单例会隐藏类之间的依赖关系
          • 单例会隐藏类之间的依赖关系
            • 虽然设计的时候认为数据只有一份,但是很多项目到后面会有多份的需要,比如数据库连接池,线程池这类的资源池都不会用单列的设计。
          • 单例对代码的可测试性不友好
          • 单例不支持有参数的构造函数
            • 可以通过pulibc的成员函数修改,但是就又多加了一套东东。
        • 单例模式的替代方案
          • 工厂模式、IOC容器;
      • 工厂模式
        • 是什么
          • 封装对象的创建过程(对象的创建就是类的实例化),将对象的创建和使用相分离。说白了,就是把对象创建过程单拿出来,放到一个工厂函数里或者一个工厂类里。
          • 工厂模式是用来创建不同但是相关类型的对象(继承同一父类或者接口的一组子类),由给定的参数来决定创建哪种类型的对象。
        • 分类
          • 简单工厂
            • 将多个对象的创建逻辑放到一个工厂类中。
          • 工厂方法
            • 将创建逻辑拆分得更细,每个对象的创建逻辑独立到各自的工厂类中。
          • 抽象工厂
        • 什么时候使用
          • 当创建对象是一个“大工程”的时候
            • 创建过程涉及复杂的if-else分支判断。
            • 对象创建需要组装多个其他类对象或者需要复杂的初始化过程。
        • DI容器
          • 一个框架,实现项目所有对象的创建。
          • 核心功能
            • 配置文件解析,创建对象,对象生命周期管理
      • 建造者模式(Builder模式)
        • 是什么
          • 建造者模式是用来创建一种类型的复杂对象,通过设置不同的可选参数,“定制化”地创建不同的对象。
      • 不常用
        • 原型模式
    • 结构型

      • 结构型模式主要解决“类或对象组合”的问题。是不同功能代码的解耦。
      • 代理模式
        • 在不改变原始类接口的条件下,为原始类定义一个代理类,主要目的是控制访问,而非加强功能,这是它跟装饰器模式最大的不同。
      • 装饰器模式
        • 装饰器模式主要解决继承关系过于复杂的问题,通过组合来替代继承,给原始类添加增强功能。
      • 适配器模式
        • 适配器模式是用来做适配的,它将不兼容的接口转换为可兼容的接口,让原本由于接口不兼容而不能一起工作的类可以一起工作。
      • 以上三个的区别
        • 代理模式主要的目的是访问控制,不是加强功能,装饰器模式主要是加强功能。
        • 代理模式、装饰器模式提供的都是跟原始类相同的接口,而适配器提供的是跟原始类不同的接口。
      • 不常用
        • 桥接模式
        • 门面模式
        • 组合模式
        • 享元模式
    • 行为型

      • 行为型设计模式主要解决的就是“类或对象之间的交互”问题。是不同行为代码的解耦。
      • 观察者模式
        • 是什么
          • 将观察者和被观察者代码解耦。
          • 在对象之间定义一个一对多的依赖,当一个对象状态改变的时候,所有依赖的对象都会自动收到通知。被依赖的对象叫观察者(也就是上面的一),依赖的对象叫观察者(也就是上面的多)。
        • 作用
          • 解耦
        • 实现方式
          • 同步阻塞
            • 同步阻塞是最经典的实现方式,主要是为了代码解耦;
          • 异步非阻塞
            • 异步非阻塞除了能实现代码解耦之外,还能提高代码的执行效率;
          • 进程内的实现方式
            • 进程间的观察者模式解耦更加彻底,一般是基于消息队列来实现,用来实现不同进程间的被观察者和观察者之间的交互。
          • 跨进程的实现方式
      • 模板模式
        • 在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。
        • 作用
          • 复用和扩展
      • 策略模式
        • 定义一组算法类,将每个算法分别封装起来,让它们可以互相替换。
        • 作用
          • 解耦策略的定义、创建和使用,控制代码的复杂度,让每个部分都不至于过于复杂、代码量过多。
          • 最常见的是,避免冗长的if-else或switch分支判断。
      • 职责链模式
        • 多个处理器依次处理同一个请求。一个请求先经过A处理器处理,然后再把请求传递给B处理器,B处理器处理完后再传递给C处理器,以此类推,形成一个链条。链条上的每个处理器各自承担各自的处理职责,所以叫作职责链模式。
      • 迭代器模式
        • 用来遍历集合对象,集合对象可以理解为容器。就是C++ STL实现的那一堆容器。
        • 作用
          • 解耦容器代码和遍历代码
      • 状态模式
        • 一般用来实现状态机
        • 状态机
          • 组成
            • 状态,事件,动作;
          • 实现方式
            • 分支逻辑法
              • 利用if-else或者switch-case分支逻辑,参照状态转移图,将每一个状态转移原模原样地直译成代码。对于简单的状态机来说,这种实现方式最简单、最直接,是首选。
            • 查表法
              • 对于状态很多、状态转移比较复杂的状态机来说,查表法比较合适。通过二维数组来表示状态转移图,能极大地提高代码的可读性和可维护性。
            • 状态模式
              • 对于状态并不多、状态转移也比较简单,但事件触发执行的动作包含的业务逻辑可能比较复杂的状态机来说,我们首选这种实现方式。
      • 不常用
        • 访问者模式
        • 备忘录模式
        • 解释器模式
        • 中介模式
  • 项目实战

    • 如何应对庞大而复杂的项目开发
      • 从设计原则和思想的角度
        • 封装和抽象
        • 分层与模块化
          • 善于应用分层技术,把容易复用、跟具体业务关系不大的代码,尽量下沉到下层,把容易变动、跟具体业务强相关的代码,尽量上移到上层。
        • 基于接口通信
          • 比如open()函数
        • 高内聚、松耦合
          • 遵守了基本的设计原则,这一条就是满足的,可以通过这个衡量代码的设计是不是符合设计原则。
        • 为扩展而设计
        • KISS首要原则
        • 最小惊奇原则
          • 就是遵守编码规范
      • 从研发管理和开发技巧的角度
        • 吹毛求疵般地执行编码规范
        • 编写高质量的单元测试
        • 不流于形式的Code Review
        • 开发未动、文档先行
        • 持续重构、重构、重构
        • 对项目与团队进行拆分
          • 按照分层和模块进行拆封
      • Code review
        • checklist
          • 是否符合设计原则?
          • 是否准确实现了需求的功能?
          • 代码是不是比需要的更复杂?
          • 所有并行编程是否都是安全的?
          • 是否给未来的功能留了扩展?
          • 是否有单元测试?
          • 测试是否是精心设计的?
          • 是否给所有的内容都使用了清晰的命名?
          • 注释是否清晰有用?是不是主要解释为什么而不是是什么?
          • 代码是否有文档?
          • 是否符合编码规范?
          • 注:
            • 如果您理解代码,但感觉没有资格进行某些部分的审查,请确保CL上有一位合格的审查者。
            • 确保检查您被要求检查的每一行代码,查看上下文,确保您正在改善代码健康状况,并赞扬开发人员所做的好事。
        • 如何书写代码评审意见?
          • 注意礼貌
          • 解释为什么
          • 给予建议
          • 标签评论
  • 思考

    • 什么是编程能力
      • 能熟练使用编程语言、开发类库等工具,思路清晰,面对复杂的逻辑,能够编写出bug free的代码,能够合理地利用数据结构和算法编写高效的代码,能够灵活地使用设计思想、原则和模式,编写易读、易扩展、易维护、可复用的高质量代码。
      • 编程语言、库工具、数据结构和算法、设计思想和原则,可读性高,易扩展,易维护,bug free。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值