软件设计与体系结构
概述
UML
统一建模语言,通过它构造系统结构的蓝图。
用例图
对应用户视图,表示多个外部执行者与系统用例之间,以及用例之间的关系。
类图
对应结构视图,使用类描述系统的静态结构,并包含类和他们之间的关系。
绘制元素
类:类名、属性、方法
属性写法:
可见性 名称:类型 [ = 默认值]方法写法:
可见性 名称(参数列表) [:返回值类型]可见性:
+: public#: protected-: private
类与类之间的关系

面向对象设计原则
| 原则 | 解释 |
|---|---|
| 单一职责原则 | 一个类只做一件事情。 |
| 开闭原则 | 一个类对扩展开放,对修改关闭。实现方法是抽象化。 |
| 里氏代换原则 | 接受基类的地方,可以用子类替换。 |
| 依赖倒置原则 | 一个类应该依赖于接口而不依赖于具体实现;细节依赖抽象,抽象不依赖细节。 |
| 接口隔离原则 | 一个接口如果太大,则应该使用多个专门的接口来取代。但接口的粒度要合适。 |
| 合成复用原则 | 多使用组合和聚合,尽量少用继承。 |
| 迪米特法则(最少知识原则) | 一个软件实体对其他实体的引用越少越好。 |
依赖注入
程序运行过程中,如果需要调用另一个对象时,无须在代码中创建被调用者,而是依赖于外部的注入。
- 构造注入:在构造函数中注入实例变量
- 设值注入:通过
setXxxx方法注入实例变量 - 接口注入:通过接口定义的方法,在接口实现中注入实例变量
Java EE
C/S 和 B/S 架构
C/S 架构(客户端-服务器)
优点:
- 安全性好:C/S 程序部署在特定的客户端,系统的操作用户通常比较确定
- 效率高:客户端和服务器直接相连,数据传输比较快,系统运行效率比较高
- 个性化:可以根据需要对不同客户端上的程序进行界面和功能方面的定制,满足客户的个性化要求
- 稳定性强:C/S 结构比较稳定,有较强的事务处理能力,可以实现较复杂的业务逻辑
缺点:
- 适用面窄:C/S 程序通常部署于局域网中,能够使用的业务场景较少,适用范围较窄
- 用户群固定:C/S 架构系统需要安装客户端程序才可以使用,不适合面向不可知的用户
- 维护成本高:C/S 程序升级时需要对所有客户端程序进行升级,维护成本较高
B/S 架构(浏览器-服务器)
优点:
- 客户端免安装
- 交互性强
- 维护成本低
缺点:
- 浏览器兼容性差
- 效率低
- 安全风险高
- 实时性差
三层架构

创建型设计模式
工厂:对象的创建和使用分离
简单工厂模式
动机
用户希望通过不同的参数得到不同的对象,而不需要知道具体的创建过程。
角色
- 工厂:用来产生类
- 抽象接口:这些产生的类需要继承一个父类
- 具体类:需要被产生的类
关系:
- 具体类和抽象接口具有 接口实现关系
- 具体类和工厂具有 依赖关系
改进:工厂和抽象类合二为一
类图

核心代码
1 | public class SimpleFactory { |
优点
- 创建和使用分离
- 客户无需知道需要具体创建的类,只需要提供参数
- 通过配置类可以在不修改代码的同时增加新的类
缺点
- 工厂类集中的所有的产品创建逻辑
- 增加了系统中类的个数
- 当产品类型过多时,工厂的逻辑会比较复杂
- 简单工厂使用了静态的工厂方法,无法被继承
- 增加产品时需要修改工厂类,违反开闭原则
使用场景
- 工厂类需要创建的对象很少
- 客户端只需要知道工厂类的参数
工厂方法模式
动机
简单工厂模式中,当产品类型过多时,工厂的逻辑会比较复杂。当需要增加新的产品类型时,需要修改工厂类,违反了开闭原则。
角色
- 抽象工厂:定义了工厂方法
- 具体工厂:返回一个具体类的实例
- 抽象产品:这些被产生的类需要继承一个父产品
- 具体产品:需要被产生的产品
关系:
- 抽象工厂和具体工厂是接口实现关系
- 抽象产品和具体产品是接口实现关系
- 具体工厂和具体产品是依赖关系
类图

核心代码
1 | //抽象工厂 |
优点
- 工厂可以自主确定创建什么对象
- 客户只需关心所需产品对应的工厂,不需要关心细节
- 增加新的产品时,只需要增加一个具体工厂,不需要修改原有工厂类
缺点
- 增加新产品时,需要编写具体的产品类,又要编写工厂类。
- 增加了系统的抽象层次和理解难度。
使用场景
- 客户端不知道具体产品类,只知道对应类的工厂类
- 一个类通过子类来确定创建什么对象
- 将创建对象的任务委托给众多工厂子类中的一个,此时可以通过配置文件来确定
抽象工厂模式
动机
一个工厂需要提供多个产品对象
概念
- 产品等级结构:产品的继承结构
- 产品族:一个工厂生产的不同产品
角色
- 抽象工厂:定义了工厂方法
- 具体工厂:返回一个具体类的实例
- 抽象产品:这些被产生的类需要继承一个父产品
- 具体产品:需要被产生的产品
关系:
- 抽象工厂和具体工厂是接口实现关系
- 抽象产品和具体产品是接口实现关系
- 具体工厂和具体产品是依赖关系
一个具体工厂对应了多个具体产品。
类图
假设有产品族有Windows、Linux、Mac 三种,有Button、Label两种产品等级结构:

优点
- 隔离了具体类的生成,客户不知道具体类被创建
- 当一个产品族的多个对象被设计在一起工作时,它能够保证客户端始终只使用同一个产品族的对象
- 容易增加新的工厂和产品族
缺点
- 难以增加新的产品
适用场景
- 系统存在多个产品族,每次只使用一个产品族
- 仅同一个产品族的产品在一起使用
单例模式
动机
一个类只有一个实例
类图

核心代码
注意将构造函数设置成私有的。
1 | // 饿汉式单例模式 |
优点
- 提供了对唯一对象的受控访问
- 节省系统资源
缺点
- 难以扩展
- 单例类的职责过重
- 可能带来一些负面问题
适用场景
- 一个类只需要一个对象
结构型设计模式
适配器模式
动机
将一个接口转换成用户期望的接口,使得这些不兼容的类能够一起工作。
角色
- 目标抽象类 Target:针对用户提供的接口
- 适配器类 Adapter: 将不兼容的接口转换成兼容的接口
- 适配者类 Adaptee: 原先不兼容的接口和类
两种适配模式
- 对象适配器:通过关联Adaptee对象的方式进行适配
- 类适配器:通过继承Adaptee和Target的方式进行适配。若不支持多继承,则通过接口实现的方式来完成。
类图

核心代码
1 | // 对象适配器 |
优点
- Target和Adaptee解耦
- 增加了类的透明性和复用性
- 灵活性和可扩展性好
- 对象适配器支持适配Adaptee及其子类
缺点
- 类适配器在不支持多重继承的语言上有限制
- 对象适配器难以置换Adaptee的方法。
适用场景
- 系统中需要使用现有的类,但接口设计不满足需求
- 建立一个可以重复使用的类,用于一些没有太大关联的类
拓展
- 缺省适配器:有时候不需要实现某个接口提供的全部方法,子类可以有选择的覆盖父类的方法。可以先设计一个父类,为这些方法提供一个默认操作,在通过子类继承的方式有选择的覆盖这些方法。
- 双向适配器:适配器同时包含了对Target和Adaptee的引用,Adaptee也可以通过Adapter调用Target的方法。
装饰器模式
动机
通过对用户透明的方式,动态地为对象增加一些新的功能。
角色
- 抽象组件 Component
- 具体组件 ConcreteComponent
- 抽象装饰器 Decorator
- 具体装饰器 ConcreteDecorator
关系:
- 具体组件和抽象组件是继承关系
- 具体装饰器和抽象装饰器是继承关系
- 抽象装饰器和抽象组件是继承关系
类图

核心代码
1 | // 抽象装饰器 |
优点
- 装饰模式非常灵活,可以动态扩展
- 可以实现不同组合的装饰器,从而为对象增加不同的功能。
缺点
- 产生很多小对象和具体装饰类
- 更容易出错
适用场景
- 动态增加类的功能,增加对象职责
- 不能通过继承的方式来扩充系统
代理模式
动机
给一个对象提供一个代理,并通过代理对象控制对原对象的访问。
角色
- 抽象主题 Subject,被代理主题和代理的抽象接口
- 具体主题 RealSubject,被代理的主题
- 代理 Proxy,控制对RealSubject的访问
类图

核心代码
1 | // 代理 |
优点
- 协调调用者和被调用者
- 远程代理:客户端可以访问远程对象
- 虚拟代理:使用小对象代表大对象,减小系统资源消耗
- 保护代理:控制真实对象的使用权限
缺点
- 可能会降低请求速度
- 有些额外的工作的实现比较··复杂
适用场景
- 远程代理:为不同地址空间的对象提供一个本地的代理
- 虚拟代理:小对象代表大对象
- 保护代理:控制对象的访问
- 缓冲代理:为操作结果提供临时的存储空间,以便多个客户端可以共享这些结果
- 防火墙代理:保护目标不让恶意用户接近
- 同步化代理:使用户能够同时使用某个对象,且不出现冲突
- 智能引用代理:当一个对象被引用时,提供一些额外的操作
行为型设计模式
命令模式
动机
将一个请求封装为一个对象,从而使得我们可用不同的请求对客户进行参数化;对请求排队或者记录日志,以及支持可撤销的操作。
角色
- 抽象命令 Command:所有命令的抽象
- 具体命令 ConcreteCommand:实现抽象命令
- 调用者 Invoker:设置并调用命令
- 接收者 Receiver:执行与请求有关的操作
类图

核心代码
1 | // 命令 |
优点
- 降低系统的耦合度
- 新的命令很容易加入系统中
- 容易设计命令队列和宏命令
- 可以方便的实现Undo和Redo
缺点
- 系统会出现很多的具体命令类
适用场景
- 请求的调用者和接收者解耦
- 请求排队执行
- 需要支持撤销和重做
- 系统需要将一组操作组合在一起
观察者模式
动机
对象间的一对多依赖关系,使得一个对象状态改变时,其相关依赖能得到通知并被自动更新。
角色
- 目标 Subject:被观察的对象,它的状态变化就需要通知观察者
- 具体目标 ConcreteSubject:实现Subject接口,实现相关操作
- 观察者 Observer:观察者需要关注的对象,它需要接收以及处理目标对象的通知
- 具体观察者 ConcreteObserver:实现Observer接口,实现相关操作
类图

核心代码
1 | // 观察者 |
优点
- 表示层和数据逻辑层的分离
- 在目标和观察者之间建立了一个抽象的耦合
- 支持广播
- 符合开闭原则
缺点
- 如果目标对象有很多的观察者,那么通知观察者会耗费较多时间。
- 如果存在循环依赖,可能会崩溃
- 只能观察到目标发生了变化,但不指定具体的变化
适用场景
- 一个对象需要通知其他对象,但不知道具体的对象
- 需要在系统中创建触发链
- 一个对象改变导致很多其他对象发生改变
- 一个抽象模型有两个方面,其中一个方面依赖于另一个方面
- 标题: 软件设计与体系结构
- 作者: ObjectKaz
- 创建于: 2022-06-05 03:16:29
- 更新于: 2022-06-05 13:04:30
- 链接: https://www.objectkaz.cn/93709eeba98f.html
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。