设计思想系列(一)--UML,时序图和设计模式六大原则详解

1,597 阅读10分钟
mp.weixin.qq.com/s?src=11&ti…

UML和时序图

统一建模语言(英语:Unified Modeling Language,缩写 UML)是非专利的第三代建模和规约语言。UML是一种开放的方法,用于说明、可视化、构建和编写一个正在开发的、面向对象的、软件密集系统的制品的开放方法。UML展现了一系列最佳工程实践,这些最佳实践在对大规模,复杂系统进行建模方面,特别是在软件架构层次已经被验证有效。

我们将从下图,一一进行讲解,对UML图构建有哪些部分组成:

类图:类图Class diagram通过显示出系统的类以及这些类之间的关系来表示系统。类图是静态的-它们显示出什么可以产生影响但不会告诉你什么时候产生影响。比如:动物,鸟,鸭,大雁,这些都是类图。类使用包含类名、属性(field) 和方法(method) 且带有分割线的矩形来表示。比如,动物下面有繁殖等方法。

那么属性/方法名称前加的加号和减号是什么意思呢?它们表示了这个属性或方法的可见性,UML类图中表示可见性的符号有三种:

+ :表示public

- :表示private

#:表示protected(friendly也归入这类)

因此,上图中的动物类具有2个公有方法和一个属性。实际上,属性的完整表示方式是这样的:

可见性 名称 :类型 [ = 缺省值]

中括号中的内容表示是可选的,比如spring项目中的Controller下面:


类与类之间关系的表示方式,又有哪些组成呢?


1.依赖关系

在UML类图中,依赖关系用一条带有箭头的虚线表示。比如上图的,动物依赖空气和水才能生活。


2.继承关系

继承关系对应的是extend关键字,在UML类图中用带空心三角形的直线表示。比如上图的,鸭继承鸟,鸟继承动物。


3.组合关系

组合关系与聚合关系见得最大不同在于:这里的“部分”脱离了“整体”便不复存在。在UML类图中,组合关系用一个带实心菱形和箭头的直线表示,并且箭头指向的是"部分"。比如上图中的,鸟是整体,翅膀是部分,翅膀脱离了鸟就不复存在。


4.聚合关系

由上图我们可以看到,UML中聚合关系用带空心菱形和箭头的直线表示。聚合关系强调是“整体”包含“部分”,但是“部分”可以脱离“整体”而单独存在。比如上图中,大雁群包含了大雁,没有大雁群,也可以说是一只大雁,大雁可以单独存在。


5.关联关系

在UML类图中单向关联用一个带箭头的直线表示。比如上面的,企鹅下蛋跟气候有关联关系,且是单向关联关系。


6.接口实现关系

这种关系对应implement关键字,在UML类图中用带空心三角形的虚线表示。比如上图中的,大雁实现飞翔这个接口功能。

实战:

IDEA编译器,可以自动生成UML图,如下操作:在具体类中,点击右键,然后选择Diagrams选项,然后选择Show Diagrams就可以了。



时序图(Sequence Diagram),又名序列图、循序图,是一种UML交互图。它通过描述对象之间发送消息的时间顺序显示多个对象之间的动态协作。我们在画时序图时会涉及7种元素:角色(Actor)、对象(Object)、生命线(LifeLine)、控制焦点(Activation)、消息(Message)、自关联消息、组合片段。下图,来自网上:

实战:

IDEA编译器需要安装,时序图生成工具:SequenceDiagram。详细使用文档,参考:

https://plugins.jetbrains.com/plugin/8286-sequencediagram

操作使用,也是点击右键,选择SequenceDiagram就可以了。



02

六大设计模式原则概述

设计模式六大原则

1、开闭原则OCP

开闭原则说的是,对扩展开放、对修改关闭。在程序需要进行扩展的时候,不能去修改原有的代码,这也是为了使程序的扩展性更好、易于升级和维护。

2、里氏代换原则LSP

在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立。如果一个软件实体使用的是一个子类对象的话,那么它一定不能够使用基类对象。里氏代换原则的程序表现就是:在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类。

3、控制反转原则IOC

针对接口编程,依赖于抽象而不依赖于具体

4、接口隔离原则ISP

使用多个隔离的接口,比使用单个接口要好

5、迪米特法则DP

一个实体应当尽量少地与其他实体间发生相互作用,使得系统功能模块相对独立

6、合成复用原则

尽量使用组合/聚合的方式,而不是使用继承

设计中的三个关键字

1、抽象化

在众多事物中提取出共同的、本质性的特征,舍弃非本质的特征,就是抽象化。抽象化的过程也是一个剪裁的过程,在抽象时,同于不同,取决于从什么角度上来抽象。抽象的角度取决于分析问题的目的。

2、实现化

抽象类给出的具体实现,就是实现化。

一个类的实例就是这个类的实例化,一个具体子类是它的抽象超类的实例化。

3、解耦

这就比较重要了,平时我们老说好的代码应该是"高内聚、低耦合",那么什么是耦合呢?

所谓耦合,就是两个实体的行为的某种强关联。而将它们之间的强关联去掉,就是解耦。解耦是指将抽象化和实现化之间的耦合解开,或者说是将它们之间的强关联改换成弱关联。

所谓强关联,指的是在编译时期已经确定的,无法在运行时期动态改变的关联;所谓弱关联,就是可以动态地确定并且在运行时期动态改变的关联。从这个定义看,继承关系是强关联,聚合关系是弱关联。

03

开闭原则/依赖倒置原则/单一职责原则

1.开闭原则

定义:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭用抽象构建框架,用实现扩展细节。

优点:提高软件系统的可复用性及可维护性。

1.1 实战

代码结构,如下:

诉说:ICourse是抽象(接口),JavaCourse 和 JavaDiscountCourse 是实现细节。Test是最上层,调用方。


具体代码参考:


2.依赖倒置原则

定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象,抽象不应该依赖细节;细节应该依赖抽象。

优点:针对接口编程,不要针对实现编程。

这个原则,类似spring中的依赖注入,service层之间的调用,不依赖实现的细节,只需要把service接口注入进来就行。该案例中,Geely为低层模块,该class要调用个课程,不依赖具体的实现,依赖于抽象(ICourse);Test是最上层,调用方。


3.单一职责原则

定义:不要存在多于一个导致类变更的原因。一个类/接口/方法只负责一项职责。

优点:降低类的复杂度、提高类的可读性,提高系统的可维护性、降低变更引起的风险。

缺点:导致类接口非常多,注重度和平衡。比如,可以事务和非事务的分开;或者吃(很多吃法放在一起),喝(很多喝法放在一起)。


04

接口隔离原则/迪米特原则/里氏替换原则

4.接口隔离原则

定义:用多个专门的接口,而不使用单一的总接口,客户端不应该依赖它不需要的接口。一个类对一个类的依赖应该建立在最小的接口上;建立单一接口,不要建立庞大臃肿的接口;尽量细化接口,接口中的方法尽量少。

由IAnimalAction接口的方法,拆分成三个不同的方法。这个原则跟单一职责原则有点像,但是单一职责讲的是职责,而这个注重的是隔离。这个原则缺点也会导致接口和实现暴增,所以也需要注重度和平衡,一般会破坏这个原则。

5.迪米特原则

定义:一个对象应该对其他对象保持最少的了解。又叫最少知道原则。尽量降低类与类之间的耦合。

优点:降低类之间的耦合。

Boss想要知道什么课程或者开课程,不需要自己去做,Boss下个命令给TeamLeader就可以了,由项目领导者具体实施。

6.里氏替换原则

定义:如果对每一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序p在所有的对象o1都替换成o2时,程序p的行为没有发生变化,那么类型T2是类型T1的子类型。

定义扩展:一个软件实体如果适用一个父类的话,那一定适用于其子类,所有引用父类的地方必须能透明地使用其子类的对象,子类对象能够替换父类对象,而逻辑不变。

引申意义:子类可以扩展父类的功能,但不能改变父类原有的功能。

含义1:子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法;

含义2:子类中可以增加自己特有的方法;

含义3:当子类的方法重载父类的方法时,方法的前置条件(即方法的输入/输出)要比父类方法的输入参数更宽松;

含义4:当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件(即方法的输出/返回值)要比父类更严格或相等。

注意点:方法入参和继承关系。

优点:约束继承泛滥,开闭原则的一种体现;加强程序的健壮性,同时变更时也可以做到非常好的兼容性提高程序的维护性、扩展性。降低需求变更时引入的风险。

使用场景:维护旧代码的时候,可以使用这个原则。


总结:

项目中实际运用讲究度和平衡,在写代码的时候,可以先思考一下,遵守这些原则。