跳到主要内容

02 软件的设计原理

08丨软件设计的方法论:软件为什么要建模?

  • 软件开发中的两个客观存在:
    1. 要解决的领域问题,对客观的领域问题的抽象就是各种功能及其关系、各种模型对象及其关系、各种业务处理流程
    2. 最终开发出来的软件系统
      软件建模
  • 4+1视图模型(建模方法论):
    4+1视图模型
    1. 逻辑视图: 描述软件的功能逻辑,组成模块、模块中的类及其依赖关系
    2. 开发视图: 包括系统架构层面的层次划分,包的管理,依赖的系统和第三方程序包
    3. 过程视图: 描述程序运行期的进程、线程、对象实例,以及与此相关的并发、同步、通信等问题
    4. 物理视图: 描述软件如何安装并部署到物理的服务上,以及不同服务器之间如何关联、通信
    5. 场景视图: 针对具体用例场景,关联上述四个视图,一方面从业务角度描述功能流程如何完成,一方面从软件角度描述相关组成部分如何互相依赖、调用

09丨软件设计实践:如何使用UML完成一个设计文档?

  • 类图: 最常见的UML图形,用于描述类的特性和类之间的静态关系
    类图
    • 类的六种静态关系: 关联、依赖、组合、聚合、继承、泛化
  • 序列图: 用于描述参与者之间的动态调用关系
    序列图
  • 组件图: 组件是比类粒度更大的设计元素,一个组件中通常包含多个类,主要用于描述组件之间的静态关系(可以使用组件序列图,以组件作为参与者,描述组件之间的消息调用关系)
    组件图
  • 部署图: 描述软件系统的最终部署情况
    部署图
  • 用例图: 主要用在需求分析阶段,通过反映用户和软件的交互,描述系统的功能需求,可配以文字说明形成需求文档
    用例图
  • 状态图: 用于展示单个对象生命周期的状态变迁
    状态图
  • 活动图: 主要用来描述过程逻辑和业务流程
    活动图
    泳道: 根据活动的范围,将活动根据领域、系统和角色等划分到不同的泳道中,使流程边界更加清晰

10丨软件设计的目的:糟糕的程序员比优秀的程序员差在哪里?

11丨软件设计的开闭原则:如何不修改代码却能实现需求变更?

  • 开闭原则: 软件实体(模块、类、函数等等)应该对扩展是开放的,对修改是关闭的,通俗的说,软件功能可以被扩展,但是软件实体不可以被修改
  • 例子(按钮和拨号器): 按钮和拨号器
    1. 策略模式: 一种行为模式,多个策略实现同一个策略接口,编程是client程序依赖策略接口,运行期根据不同上下文向client程序传入不同的策略实现
      策略模式
      Button 和 Dailer 之间增加了一个抽象接口 ButtonServer,Button 依赖ButtonServer,而 Dailer 实现 ButtonServer
    2. 适配器模式: 一种结构模式,用于将两个不匹配的接口适配起来,使其能够正常工作
      适配器模式
      不要由 Dailer 类直接实现 ButtonServer 接口,而是增加两个适配器DigitButtonDailerAdepter、SendButtonDailerAdepter,由适配器实现ButtonServer接口,在适配器的 buttonPressed 方法中调用 Dailer 的enterDigit 方法和 dail 方法,而Dailer 类保持不变,Dailer 类实现开闭原则
    3. 观察者模式: 一种行为模式,解决一对多的对象依赖关系,将被观察者对象的行为通知到多个观察者,也就是监听者对象
      观察者模式
      ButtonServer 被改名为 ButtonListener,表示这是一个监听者接口,其实这个改名不重要,仅仅是为了便于识别。因为接口方法 buttonPressed 不变,ButtonListener 和ButtonServer 本质上是一样的
    4. 模板方法: 在父类中用抽象方法定义计算的骨架和过程,而抽象方法的实现则留在子类中
      模板方法
      在 Button 类中定义抽象方法 onPress,具体类型的按钮,比如 SendButton 实现这个方法。Button 类中增加抽象方法 onPress,并在 press 方法中调用 onPress 方法
  • 实现开闭原则的关键是抽象,开闭原则可以说是软件设计原则的原则,是软件设计的核心原则,其他的设计原则更偏向于技术性,具有技术性的指导意义,而开闭原则是方向性的

12丨软件设计的依赖倒置原则:如何不依赖代码却可以复用它的功能?

  • 依赖倒置原则:
    • 高层模块不应该依赖低层模块,二者都应该依赖抽象
    • 抽象不应该依赖具体实现,具体实现应该依赖抽象
  • 依赖倒置的关键是接口所有权的倒置
    • 习惯上的层次依赖
      习惯上的层次依赖
      利用依赖倒置的设计原则,每个高层模块都为它所需要的服务声明一个抽象接口,而低层模块则实现这些抽象接口,高层模块通过抽象接口使用低层模块的功能
      使用依赖倒置
  • 贫血模型(Anemic Domain Model): 将数据与操作分离,只包含数据,不包含业务逻辑的类,破坏了面向对象的封装特性,是一种典型的面向过程的编程风格
  • 充血模型(Rich Domain Model): 数据和对应业务封装到同一个类中,满足面向对象的的封装特性,是一种典型的面向对象的编程风格,DDD开发模式便基于充血模型

13丨软件设计的里氏替换原则:正方形可以继承长方形吗?

  • 里氏替换原则: 子类型必须能够替换掉它们的基类型,也就是程序中所有使用基类的地方,都应该可以用子类代替
  • 子类不能比父类更严格,通常来说,子类比父类契约更严格,都是违反里氏替换原则的
  • 当继承是为了复用父类方法时,可能就离错误继承不远了,如果一个类不是为了被继承而设计的,就最好不要继承它,粗暴一点的讲,如果不是抽象类或者接口,最好就不要继承它
  • 如果确实需要使用一个类的方法,最好的办法是组合这个类而不是继承这个类,也就是组合优于继承

14丨软件设计的单一职责原则:为什么说一个类文件打开最好不要超过一屏?

  • 软件设计的两个基本准则: 低耦合和高内聚
  • 单一职责原则: 设计类的时候,我们应该把强相关的元素放在一起,弱相关的元素放在类的外面,保持类的高内聚行,一个类应该只有一个引起它变化的原因

15丨软件设计的接口隔离原则:如何对类的调用者隐藏类的公有方法?

  • 接口隔离原则: 不应该强迫用户依赖他们不需要的方法,可以将一个实现类的不同方法包装在不同的接口中对外暴露

16丨设计模式基础:不会灵活应用设计模式,你就没有掌握面向对象编程

  • 面向对象编程的本质是多态

17丨设计模式应用:编程框架中的设计模式

  • 框架是对某一类架构方案可复用的设计和实现,框架一般应该满足开闭原则和依赖倒置原则
  • Web容器中的设计模式:
    • Web容器: 主要使用了策略模式,多个策略实现同一个策略接口,编程时Tomcat依赖策略接口,在运行期根据不同上下文装载不同的策略实现
    • HttpServlet: 实现了Servlet接口,使用了模板方法,在父类中用抽象方法定义计算的骨架和过程,抽象方法的实现则留在子类中
  • JUnit中的设计模式:
    • 使用了模板方法模式,测试用例的方法执行顺序被固定在JUnit框架的模板方法里

18丨反应式编程框架设计:如何使程序调用不阻塞等待,立即响应?

19丨组件设计原则:组件的边界在哪里?

20丨领域驱动设计:35岁的程序员应该写什么样的代码?

  • 领域模型模式和事务脚本模式
  • 六边形架构
    六边形架构