精讲---设计原则与UML类图
设计模式
1 UML类图
1.1 什么是类图?
- 在软件工程中,类图是一种静态的结构图,描述了系统的类的集合,类的属性和类之间的关系,可以简化了人们对系统的理解;
- 类图是系统分析和设计阶段的重要产物,是系统编码和测试的重要模型。简单来说类图是用来表示一个类里面有什么和类之间的关系的图
1.2 表示一个类
类图展示的是最核心的三个部分:类名,属性,方法
用下面的三个符号表示关键字
| 符号 | 可见性名称 | 对应 Java 关键字 | 说明 |
|---|---|---|---|
+ | Public | public | 对所有类可见 |
- | Private | private | 仅对本类可见 |
# | Protected | protected | 对子类和同包类可见 |
下面我来写一个类展示一下
public class Employee {
// 私有属性(对应图中的 - 符号)
private String name;
private int age;
private String address;
// 公有方法(对应图中的 + 符号)
public void work() {
// 方法体可以根据实际需求编写
System.out.println(name + " 正在工作...");
}
// 通常在 Java 中,私有属性会搭配 Getter 和 Setter 方法,
// 虽然类图中未显式画出,但这是开发中的标准做法。
protected void rest() {
// 方法体可以根据实际需求编写
System.out.println(name + " 正在休息...");
}
}

上到下的三个部分分别是:类名,属性,方法
属性的完整表示方式是: 可见性 名称 :类型 [ = 缺省值]
方法的完整表示方式是: 可见性 名称(参数列表) [ : 返回类型]
注意:
1,中括号中的内容表示是可选的
2,也有将类型放在变量名前面,返回值类型放在方法名前面
1.3 表示类与类之间关系
1.3.1关联关系
关联关系是对象之间的一种引用关系,用于表示一类对象与另一类对象之间的联系,如老师和学生、师傅和徒弟、丈夫和妻子等。关联关系是类与类之间最常用的一种关系,分为一般关联关系、聚合关系和组合关系。我们先介绍一般关联。
1.3.1.1 单向关联

一方持有另一方的类型作为成员变量,单项关联用一个带箭头的实现表示
上图表示每一个人有一张车票
1.3.1.2 双向关联

双向关联就是双方各自持有对方类型的成员变量。
双向关联用一个不带箭头的直线表示。
上图中在Customer类中维护一个List,表示一个顾客可以购买多个商品;在Product类中维护一个Customer类型的成员变量表示这个产品被哪个顾客所购买。
1.3.1.3 自关联

用一个带有箭头且指向自身的线表示。
上图的意思就是Point类包含类型为Point的成员变量,也就是“自己包含自己”。
1.3.2聚合关系
聚合关系是整体和部分之间的关系
聚合关系也是通过成员对象来实现的,其中成员对象是整体对象的一部分,但是成员对象可以脱离整体
对象而独立存在。例如,学校与老师的关系,学校包含老师,但如果学校停办了,老师依然存在。

使用空心菱形的实线来表示,菱形指向整体
1.3.3 组合关系
组合关系表示的也是类之间的整体和部分的关系,但他是一种更强烈的聚合关系.一旦整体对象不存在,部分对象也会消失
头和嘴的关系,没有了头,嘴也就不存在了。

使用带实心菱形的实线来表示,菱形指向整体
1.3.4 依赖关系
它是对象之间耦合度最弱的一种关联方式,是临时性的关联。在代码中,某个类的方法通过局部变量、方法的参数或者对静态方法的调用来访问另一个类(被依赖类)中的某些方法来完成一些职责。

使用带箭头的虚线来表示,箭头从使用类指向被依赖的类
1.3.5 继承关系
继承关系是对象之间耦合度最大的一种关系,表示一般与特殊的关系,是父类与子类之间的关系,是一种继承关系。

使用用带空心三角箭头的实线来表示,箭头从子类指向父类
1.3.6 实现关系
实现关系是接口与实现类之间的关系。在这种关系中,类实现了接口,类中的操作实现了接口中所声明的所有的抽象操作。

使用实现关系使用带空心三角箭头的虚线来表示,箭头从实现类指向接口。
2 设计原则
在软件开发中,为了提高软件系统的可维护性和可复用性,增加软件的可扩展性和灵活性,程序员要尽量根据6条原则来开发程序,从而提高软件开发效率、节约软件开发成本和维护成本。
| 标记 | 设计模式原则名称 | 简单定义 |
|---|---|---|
| OCP | 开闭原则 | 对扩展开放,对修改关闭 |
| SRP | 单一职责原则 | 一个类只负责一个功能领域中的相应职责 |
| LSP | 里氏代换原则 | 所有引用基类的地方必须能透明地使用其子类的对象 |
| DIP | 依赖倒转原则 | 依赖于抽象,不能依赖于具体实现 |
| ISP | 接口隔离原则 | 类之间的依赖关系应该建立在最小的接口上 |
| CARP | 合成复用原则 | 尽量使用合成/聚合,而不是通过继承达到复用的目的 |
| LOD | 迪米特法则 | 一个软件实体应当尽可能少的与其他实体发生相互作用 |
单一职责原则(SRP):
一个类应该只有一个引起它变化的原因,即一个类应该只负责一项职责。例子:考虑一个员工类,它应该只负责管理员工信息,而不应负责其他无关工作。
开放封闭原则(OCP):
软件实体应该对扩展开放,对修改封闭。例子:通过制定接口来实现这一原则,比如定义一个图形类,然后让不同类型的图形继承这个类,而不需要修改图形类本身。
里氏替换原则(LSP):
子类对象应该能够替换掉所有父类对象。例子:正方形是一种特殊的长方形,正方形(子)不能继承长方形(父),因为正方形无法在不破坏逻辑的前提下“替换”长方形。这就违背了里氏替换原则
接口隔离原则(ISP):
接口的设计应该尽量精简,不应该强迫实现类去依赖它们不需要的方法。它的核心在于按需定制,避免接口污染。理由: 如果一个接口过于臃肿,即使实现类只用到了其中一个功能,也不得不实现其他无关方法。这会导致不必要的耦合——一旦那些无关方法发生变动,这个实现类也得跟着改动。通过拆分细粒度的接口,我们可以让系统变得更灵活,实现真正的“职责清晰”。
依赖倒置原则(DIP):
要面向接口编程,而不是面向实现编程。 高层模块(业务逻辑)不应该直接依赖底层实现(具体类),否则底层一变,高层就崩了。
应用: 核心是高层和底层都应该依赖于同一个抽象接口。结合依赖注入,高层模块只需要声明它依赖的那个“抽象接口”,我们就可以在运行时动态地把底层实现注入进去。这样一来,即便底层逻辑怎么变,只要接口协议不变,高层代码就稳如泰山,大大提升了系统的可维护性和扩展性。
迪米特法则 (LOD)(又叫: 最少知识原则):
一个对象应当对其他对象有最少的了解,只与其直接的朋友交互。
核心思想: 减少对象之间的依赖,也就是所谓的“不要跟陌生人说话”。如果一个方法需要调用好几个对象才能拿到结果(比如 a.getB().getC().do()),就说明耦合度太高了。
实际应用: 我们应该在中间类(朋友)里封装好逻辑,不是让朋友返回朋友让你去调用,对外只暴露一个简单的方法。这样即使“陌生人”类的内部结构发生了变化,也不会影响到高层代码,从而保证了系统的独立性和安全性。
合成复用原则(CARP)
合成复用原则(Composite Reuse Principle,CRP)又叫组合/聚合复用原则(Composition/Aggregate Reuse Principle,CARP)。它要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。 如果要使用继承关系,则必须严格遵循里氏替换原则。
为什么优先使用合成复用而不是继承复用?
继承复用虽然有简单和易实现的优点,但它也存在以下缺点。
- 继承复用破坏了类的封装性。因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为**“白箱”复用**。
- 子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护。
- 它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化。
采用组合或聚合复用时,可以将已有对象纳入新对象中,使之成为新对象的一部分,新对象可以调用已有对象的功能,它有以下优点。
- 它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称为**“黑箱”复用**。
- 新旧类之间的耦合度低。这种复用所需的依赖较少,新对象存取成分对象的唯一方法是通过成分对象的接口。
- 复用的灵活性高。这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象。
eg:汽车分类管理程序
汽车按“动力源”划分可分为汽油汽车、电动汽车等;按“颜色”划分可分为白色汽车、黑色汽车和红色汽车等。如果同时考虑这两种分类,其组合就很多。
一:采用继承关系

这里的颜色是可以提取的部分,我们使用合成的方式来降低新旧类之间的耦合度:

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)