【设计模式】第一篇:概述、耦合、UML、七大原则,详细分析总结(基于Java)

【设计模式】第一篇:概述、耦合、UML、七大原则,详细分析总结(基于Java)

(二) 公共耦合

两个以上的模块共同引用一个全局数据项就称为公共耦合。大量的公共耦合结构中,会让你很难确定是哪个模块给全局变量赋了一个特定的值

【设计模式】第一篇:概述、耦合、UML、七大原则,详细分析总结(基于Java)

(四) 控制耦合

控制耦合 。一个模块通过接口向另一个模块传递一个控制信号,接受信号的模块根据信号值而进行适当的动作,这种耦合被称为控制耦合,也就是说,模块之间传递的不是数据,而是一些标志,开关量等等

【设计模式】第一篇:概述、耦合、UML、七大原则,详细分析总结(基于Java)

(六) 数据耦合

模块之间通过参数来传递数据,那么被称为数据耦合。数据耦合是最低的一种耦合形 式,系统中一般都存在这种类型的耦合,因为为了完成一些有意义的功能,往往需要将某些模块的输出数据作为另 一些模块的输入数据

【设计模式】第一篇:概述、耦合、UML、七大原则,详细分析总结(基于Java)

三 UML 类图及类图之间的关系

在一个相对完善的软件系统中,每个类都有其责任,类与类之间,类与接口之间同时也存在着各种关系,UML(统一建模语言)从不同的角度定义了多种图,在软件建模时非常常用,下面我们说一下在设计模式中涉及相对较多的类图,因为在后面单个设计模式的讲解中,我们会涉及到,也算是一个基础铺垫。

(一) 类

类是一组相关的属性和行为的集合,是一个抽象的概念,在UML中,一般用一个分为三层的矩形框来代表类

  • 第一层:类名称,是一个字符串,例如 Student

  • 第二层:类属性(字段、成员变量)格式如下:

    • 例如:-name:String
  • 第三层:类操作(方法、行为),格式如下:

  • 例如:+ display():void

【设计模式】第一篇:概述、耦合、UML、七大原则,详细分析总结(基于Java)

(三) 关系

(1) 依赖关系

定义:如果一个元素 A 的变化影响到另一个元素 B,但是反之却不成立,那么这两个元素 B 和 A 就可以称为 B 依赖 A

  • 例如:开门的人 想要执行开门这个动作,就必须借助于钥匙,这里也就可以说,这个开门的人,依赖于钥匙,如果钥匙发生了什么变化就会影响到开门的人,但是开门的人变化却不会影响到钥匙开门
  • 例如:动物生活需要氧气、水分、食物,这就是一个很字面的依赖关系

依赖关系作为对象之间耦合度最低的一种临时性关联方式

在代码中,某个类的方法通过局部变量、方法的参数或者对静态方法的调用来访问另一个类(被依赖类)中的某些方法来完成一些职责。

【设计模式】第一篇:概述、耦合、UML、七大原则,详细分析总结(基于Java)

(3) 聚合关系

聚合关系也称为聚集关系,它是一种特殊的较强关联关系。表示类(准确的说是实例化后的对象)之间整体与部分的关系,是一种 has-a 的关系

  • 例如:汽车(Car)有轮胎(Wheel),Car has a Wheel,这就是一个聚合关系,但是轮胎(Wheel)独立于汽车也可以单独存在,轮胎还是轮胎

聚合关系可以用带空心菱形的实线箭头来表示,菱形指向整体

【设计模式】第一篇:概述、耦合、UML、七大原则,详细分析总结(基于Java)

(5) 泛化关系

泛化描述一般与特殊(类图中“一般”称为超类或父类,“特殊”称为子类)的关系,是父类和子类之间的关系,是一种继承关系,描述了一种 is a kind of 的关系,特别要说明的是,泛化关系式对象之间耦合度最大的一种关系

Java 中 extend 关键字就代表着这种关系,通常抽象类作为父类,具体类作为子类

  • 例如:交通工具为抽象父类,汽车,飞机等就位具体的子类

泛化关系用带空心三角箭头的实线来表示,箭头从子类指向父类

【设计模式】第一篇:概述、耦合、UML、七大原则,详细分析总结(基于Java)

四 设计模式七大原则

(一) 开闭原则

定义:软件实体应当对扩展开放,对修改关闭

我们在开发任何产品的时候,别指望需求是一定不变的,当你不得不更改的你的代码的时候,一个高质量的程序就体现出其价值了,它只需要在原来的基础上增加一些扩展,而不至于去修改原先的代码,因为这样的做法常常会牵一发而动全身。

也就是说,开闭原则要求我们在开发一个软件(模块)的时候,要保证可以在不修改原有代码的模块的基础上,然后能扩展其功能

我们下面来具体谈谈

(1) 对修改关闭

对修改关闭,即不允许在原来的模块或者代码上进行修改。

A:抽象层次

例如定义一个接口,不同的定义处理思路,会有怎样的差别呢

定义一

定义二

两种方式看似都是差不多的,也都能实现要求,但是如果我们想要在其基础上增加一个新的参数

  • 如果以定义一的做法,一旦接口被修改,所有调用 connectServer 方法的位置都会出现问题
  • 如果以定义二的做法,我们只需要修改 FTP 这个实体类,添加一个属性即可
    • 这种情况下没有用到这个新参数的调用处就不会出现问题,即使需要调用这个参数,我们也可以在 FTP 类的构造函数中,对其进行一个默认的赋值处理

B:具体层次

对原有的具体层次的代码进行修改,也是不太好的,虽然带来的变化可能不如抽象层次的大,或者碰巧也没问题,但是这种问题有时候是不可预料的,或许一些不经意的修改会带了和预期完全不一致的结果

(2) 对扩展开放

对扩展开放,也就是我们不需要在原代码上进行修改,因为我们定义的抽象层已经足够的合理,足够的包容,我们只需要根据需求重新派生一个实现类来扩展就可以了

(3) 开发时如何处理

无论模块是多么“封闭”,都会存在一些无法对之封闭的变化。既然不可能完全封闭,设计人员必须对他设计的模块应该对那种变化封闭做出选择,他必须先猜测出最有可能发现的变化种类,然后构造抽象来隔离那些变化 ——《大话设计模式》

预先猜测程序的变化,实际上是有很大难度,或许不完善,亦或者完全是错误的,所以为了规避这一点,我们可以选择在刚开始写代码的时候,假设不会有任何变化出现,但当变化发生的时候,我们就要立即采取行动,通过 “抽象约束,封装变化” 的方式,创建抽象来隔离发生的同类变化

举例:

例如写一个加法程序,很容易就可以写的出来,这个时候变化还没有发生

【设计模式】第一篇:概述、耦合、UML、七大原则,详细分析总结(基于Java)

小结:

  • 我们希望开发刚开始就知道可能发生的变化,因为等待发现变化的时间越长,要抽象代码的代价就越大

  • 不要刻意的去抽象,拒绝不成熟的抽象和抽象本身一样重要

(二) 里氏替换原则

(1) 详细说明

定义:继承必须确保超类所拥有的性质在子类中仍然成立

里氏替换原则,主要说明了关于继承的内容,明确了何时使用继承,亦或使用继承的一些规定,是对于开闭原则中抽象化的一种补充

这里我们主要谈一下,继承带来的问题:

  • 继承是侵入性的,子类继承了父类,就必须拥有父类的所有属性和方法,降低了代码灵活度
  • 耦合度变高,一旦父类的属性和方法被修改,就需要考虑子类的修改,或许会造成大量代码重构

里氏替换原则说简单一点就是:它认为,只有当子类可以替换父类,同时程序功能不受到影响,这个父类才算真正被复用

其核心主要有这么四点内容:

  • ① 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法
  • ② 子类中可以增加自己特有的方法
  • ③ 当子类的方法重载父类的方法时,子类方法的前置条件(即方法的输入参数)要比父类的方法更宽松
  • ④ 当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件(即方法的的输出/返回值)要比父类的方法更严格或相等

对照简单的代码来看一下,就一目了然了

① 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法

前半句很好理解,如果不实现父类的抽象方法,会编译报错

后半句是这里的重点,父类中但凡实现好的方法,其实就是在设定整个继承体系中的一系列规范和默认的契约,例如 鸟类 Bird 中,getFlyingSpeed(double speed) 用来获取鸟的飞行速度,但几维鸟作为一种特殊的鸟类,其实是不能飞行的,所以需要重写继承的子类方法 getFlyingSpeed(double speed) 将速度置为 0 ,但是会对整个继承体系造成破坏

虽然我们平常经常会通过重写父类方法来完成一些功能,同样这样也很简单,但是一种潜在的继承复用体系就被打乱了,如果在不适当的地方调用重写后的方法,或多次运用多态,还可能会造成报错

我们看下面的例子:

父类 Father

子类 Son

子类 Daughter

测试类 Test

运行结果:

父类: speaking方法被调用
子类: speaking方法被调用
父类: speaking方法被调用

② 子类中可以增加自己特有的方法

这句话理解起来很简单,直接看代码

父类 Father

子类 Son

测试类 Test

来源:二境志

声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2020年9月23日
下一篇 2020年9月23日

相关推荐