软件工程(三)

第八章 设计优化

一 小即是美

(那我可能丑到爆炸了)

1 设计的“味道”(由内味了)

僵化性,脆弱性,顽固性,粘滞性
不必要的复杂性,不必要的重复性,晦涩性

2 设计的优化

1.运行时的多态:多态性在结构上形成类的继承层次
2.重写的要求:
重写的方法本质上与父类方法具有相似的行为,但在细节上进行了有针对性的调整
重写的方法与原方法在相同的条件作用下工作,子类的方法不应具有比其父类更严格的条件限制
重写的方法最高不能超出父类方法的状态
3.耦合的消息链:交互集中的设计与交互分散的设计
4.狎昵关系:两个类间表现出过分亲密的关系,即高耦合
5.被拒绝的遗赠:
一是借助继承的方式。但当子类只需要重用父类的某些功能,对父类的行为并不想保持一致,这就是拒绝的遗赠现象,预示着继承层次的误用,应考虑第二种方式进行扩展
二是通过所谓的委托(Delegation) 进行,其基本形式是新类与原有类构成关联关系,即具有一个它的实例变量
6.循环依赖:如果两个类分别处于不同的包中,并且具有双向导航的关联关系,这样的循环依赖好像是不可避免的

二 设计原则

1.接口隔离原则(ISP)

(鄙人理解就是对操作进行封装,相当于电脑,只需要关心如何使用,不用关心内部具体实现,而且更换各种配置也相对简单【比如把有线鼠标换成无线】)
1)含义:
a。应尽量使用 “ 接口继承” ” ,而非 “ 实现继承 ” 。接口关注对象的概貌,将对象中 “不变 ” 的信息抽象出来,不涉及细节,因此是 “ 稳定 ” 的
b。通过 接口只将需要的操作 “ 暴露 ” 给客户类,而将不需要的操作隐藏起来。接口在这里充当类的视图
c。好处就是当业务需求变化时,更容易发生改变的是具体类,而这些变更可以通过稳定的抽象类进行隔离,使得Client 不受变化的影响,从而提高了系统的可维护性
2)面向接口的作用:
a。面向接口的设计能够使Client 只需关注如何进行业务活动(如:驾驶),而不必关心其使用对象的具体实现
b。一个对象可以很容易地被(实现了相同接口的)另一个对象所替换,这样对象间的连接不必硬绑定(hard wire )到一个具体类的对象上,因此增加了灵活性
c。这是一种松散的耦合,同时增加了重用的可能性

2. 依赖倒置原则(DIP)

1)宗旨:应依赖于抽象,而不要依赖具体

  1. 扩展的基础越具体,扩展的难度也越大,具体类的变化无常势必造成扩展类的 不稳定
  2. 依赖倒置原则使细节和具体实现都依赖于抽象,抽象的稳定性决定了系统的 稳定性
  3. 一个基础稳定的系统要比一个基础不稳定的系统在整体上要更 “ 稳定 ”(感觉像在放屁)
    (应该就是留点发挥空间,增加灵活性)

3. 开放封闭原则(OCP)

(比如三个类:interface good,milk implements good,现在要对milk进行打折,保持原来代码不变的基础上,新增一个类,discountmilk,在里面实现对于milk打折的操作)
1)开放封闭原则(The Open-Closed Principle, OCP):一 个模块对扩展应是开放的,而对修改应封闭的
1.这条原则是面向对象思想的最高 境界,即设计者 应给出对于需求变化进行扩展的模块,而永远不需要改写已经实现的内部代码或逻辑 。
2.特点:
a。模块 的行为可以被扩展,以需要满足新的需求
b。模块 的源代码是不允许进行改动的
3.OCP 是相对的,没有绝对符合OCP 的设计,而且一个软件系统的所有模块不可能都满足OCP ,要 做的 是尽量 最小化 不满足OCP 的模块数量

4. Liskov替换原则(LSP)

(这个比较难理解,举个栗子:爹会的儿子必须会,儿子还能会爹不会的,作为新时代的弄潮儿,儿子出生可以选择的方向比爹多【输入宽松】,但是步入 会的需要的结果比爹严格的多【输出更加具体】)
1)Liskov 替换原则(Liskov Substitution Principle, LSP):任何 出现父类的地方应能使用子类对其进行无条件的替换,即当使用子类对其父类进行替换时,该组件仍象替换前一样正常工作

  1. LSP 要求对象间的继承关系既与静态属性相关又与动态行为相关
  2. 通常规定 父类型在使用前和使用后都要具备必要的条件—— 前置条件和后置条件
  3. 当 当 子类型替换父类型 后不能 违反父类型中的前置条件和后置条件,即一个子类型不得具有比父类型更多的限制,这是因为可能对于父类型的某些使用是合法的,但是会因为违背子类型的其中一个额外限制,从而违背了LSP
    2)要求:
    1.子类必须实现父类的抽象方法,但不得重写父类的非抽象方法
    2.子类可以有自己的方法
    3.当子类覆盖或实现父类的方法时,方法的输入参数可以比父类方法的输入参数更宽松
    4.当子类覆盖或实现父类的方法时,方法的返回结果可以比父类方法的返回结果范围更严格

5. 单一职责模式(SRP)

(这个简单,就是舔狗,职责单一,舔就完事了,此处@某姓子腾)
1)单一职责原则(Single Responsibility Principle, SRP )中所谓职责,可理解为功能,就是设计的类功能应该只有一个,而不应为两个或多个 。

  1. 职责 是引起 “ 变化 ” 的原因:当一个类中有两个以上的变化方向,会产生过多的变化

6. 合成/聚合复用原则(CARP)

(聚合就是弱包含,合成就是强包含,详情见下面的借鉴)
1)合成/ 聚合复用原则(Composite/Aggregate ReusePrinciple, CARP )中的合成与聚合是两种特殊的关联关系,是以委托方式实现对象间功能的 重用(另外 一种面向对象特有的重用方式是 继承)。

  1. 委托 重用与继承重用是两种本质上不同的重用方式,委托重用追求的是对象间的独立性即低耦合,而继承重用追求的是对象间应能尽可能的高内聚
  2. 合成/ 聚合复用原则指的是应尽量使用合成/ 聚合形式的委托重用,尽量不使用继承重用
    2)借鉴
    聚合指的是弱包含关系:例如(人属于人群的一部分,而人不包含了人群)
    合成指的是强包含关系:例如(人的手臂属于这个人的一部分,而这个人也包含了这个人的手臂)

三 设计模式

重点画的是:工厂,适配器,观察者,状态

1. 抽象工厂模式

(就是相当于吧在超市的自助找货换成,在KFC的点菜,服务员帮你找餐【KFC为抽象工厂】)
1)抽象工厂(Abstract Factory )模式的主要作用是实现了客户类在创建产品类时引入的耦合,如在对具体对象创建时使用的new 操作,需要指定一个具体的产品类的名字,这样就在客户类和具体产品类之间引入了依赖关系,而这种依赖关系按照面向接口编程等原则是应该进行优化处理的
2)将产品的创建过程从客户类中分离,通过使用一个类似系统服务的工厂类来解决这个问题,工厂类提供了一个创建一系列相关或相互依赖对象的接口,而客户类无需指定它们需要的具体产品类
3)消除对象创建的耦合(就是以前写的工厂类,创建对象时,new的是factory,而不是一个具体的类

2.单例模式

(说的好听点,就是专一,一生只爱一个人;说的难听点,就是懒汉模式,只做一件事,其他的懒得做)
1)单例模式(Singleton )保证了一个类仅有一个实例,并提供一个访问它的全局访问点
要求:

  1. 类的所有构造方法都为私有的,防止其被外部创建
  2. 提供一个公有的方法获取该类的实例
  3. 类中的实例变量为私有或受保护的

3. 适配器模式

(就是字面意思,把俩不兼容的类通过适配器类强行兼容,比如转换口)

  1. 适配器模式(Adapter )把一个类的接口变换成客户类所期待的另一种接口,从而使原本因接口原因不匹配而无法一起工作的两个类能够一起
  2. 适配器 一般有两种工作方式:一种是通过委托的方式,另外一种是通过继承(接口实现)的
  3. 无论 哪种方式,适配器都可以充当被适配对象参与与客户类的交互,并可以对基本的适配功能做进一步的扩展,而这个功能扩展的作用又可以通过另外的 “ 装饰模式 ” 进一步

4. 桥模式

(把抽象部分和实现部分分离,(五花肉大法好))

  1. 桥模式的主要思想是将抽象部分与它的实现部分( 行为)进行分离,使它们都可以独立地变化
  2. 使用桥模式时,首先应该识别出一个类所具有的两个独立变化的维度,将它们设计为两个独立的继承等级结构,为两个维度都提供抽象层,并建立抽象耦合
  3. 一般把业务方法和与之关系最密切的维度设计为 “ 抽象类” 层次结构( 抽象部分) ,而将另一个维度设计为 “ 实现类” 层次结构( 实现部分)

5. 装饰模式

(将桥模式中的“抽象”和“实现”合二为一,是桥模式的一种特殊情况)

  1. 一个类可能有些额外的责任( 除主体业务) ,如加密、缓存、压缩等,这些可能只是辅助主体业务的附着,并不严格按照维度变化
  2. 装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案,提供比继承更多的灵活性
  3. 动态给一个对象增加功能,并可以再动态的撤消,因此增加由一些基本功能的组合而产生的非常大量的功能
  4. 7. 代理模式

    (就是中介)
    1)代理模式(Proxy )一般用来对有价值(稀缺)资源的管理,比如数据库的连接等,目的就是为了提高这些资源的利用率或者系统性能

    1. 它给这些资源对象提供一个代理对象,并由代理对象控制对资源对象的使用,起到中介的作用
    2. 第十一章 软件测试

      一 形式化验证

      1. 软件测试的 基本原理是 根据 用户需求的满足情况判断软件的质量情况

      二 测试技术

      三 软件度量***

      方法复杂程度的度量:McCabe 指标
      类的内聚性的度量:LCOM* (这个*是自己带的,不是强调的意思)

      1. McCabe指标

      1)McCabe 环形复杂度 ,以 方法的控制流程图结构为基础进行计算:边数 – 节点数 + 2
      2)考虑到复合条件的情况,McCabe的 的 计算实际上反映了方法中下列语句产生的分支结构:if 语句、条件组合&& 和||for 语句和while 语句。

      软件工程(三)

    六 断言

    1 概念

    1. 断言提供对 异常进行检查的能力,但只能在开发阶段使用,并且不能替代常规的 异常处理
    2. 断言规定了方法必须要满足的条件 ,即需求 的程序特性。 如对输入不能确定是否 满足 既定要求 ,比如在防御性程序设计 (defensive programming )中要求方法总是可用,则必须要使用异常处理而不能使用

    2 测试框架

    就讲了一个测试平台

    1. JUnit:JUnit 可以使用反射机制在测试环境中调用传递过来的方法, 所有某类的test 测试方法都放在一个测试类中,这个类需要从JUnit 提供的系统类TestCase 继承而来

    七 可测试性:

    1. 测试的构建一般采用的方式是 “ 自底向上(Bottom Up)” 的方式 ,也就是 新的测试总是在已经经过测试的类和方法的基础上进行构建
    2. 测试进行的过程中尤其是在多人并行开发的时候经常会出现彼此依赖的情况。 为避免 等待而影响开发进度, 可以构建一个 模拟程序(mock )或桩(stub)

    1 可测试性构建的原则:

    1. 设计简单的方法: 规模较小的方法的类的测试性要好于带有较少方法但每个方法的规模较大的类
    2. 避免私有方法:方法的可测试性是较差的,由于封装性,普通的JUnit 类无法直接对它们进行访问
    3. 优先使用通用方法:静态代码无法应用多态的特性,也就意味着没有代码重用,无论是对被测程序还是测试程序
    4. 组合优于继承: 类之间通过关联进行组合的关系更易于测试
    5. 避免隐藏的依赖关系与全局状态:对全局状态一定要谨慎,因为全局状态使测试的构建变得复杂,如果全局状态没能共享或者遗漏,都会导致一些意外的后果

    声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2019年11月24日
下一篇 2019年11月24日

相关推荐