软件构造学习记录——知识点总结

软件构造知识点总结

本文仅供参考,不能保证正确性,且部分内容因为在实验中已经多次练习过,便不再赘述

  • 软件构造的多维视图
    在这里插入图片描述

    • Code-level view: source code——代码的逻辑组织
    • Component-level view: architecture——代码的物理组织
    • Moment view: 特定时刻的软件形态
    • Period view: 软件形态随时间的变化
  • 软件构造的质量目标

    • 外部质量因素:影响用户,取决于内部质量,matters
      • 正确性:最重要的质量指标
      • 健壮性:对异常情况的处理能力
      • 可扩展性
      • 可复用性
      • 兼容性
      • 高效,Efficiency
      • 可移植性
      • 易用性
      • 及时性
    • 内部质量因素:影响软件本身和开发者
    • 折中,tradeoff
    • 五个关键的质量目标
      • Easy to understand
      • Ready for change
      • Cheap for develop: reusability
      • Safe from bugs
      • Efficient to run
  • SCM&VCS

    • SCM:Software Configuration Management,追踪和控制软件的变化,核心是版本控制和基线确立

      • SCI软件配置项:软件中发送变化的基本单元(如对于Git来说是文件,对于传统的VCS是变化的代码行)
      • baseline基线:稳定版本
      • CMDB配置管理数据库:存储软件的各配置项随时间发生变化的信息+基线
    • VCS:Version Control System,分为三类

      • Local VCS:本地版本控制系统,仓库位于开发者的本地机器,无法共享和协作
      • Centralized VCS(如CVS,SVN):集中式版本控制系统,仓库位于独立的服务器,支持协作
      • Distributed VCS(如Git):分布式版本控制系统,仓库位于独立的服务器和开发者的本地机器
    • Git

      • .git目录相当于本地的CMDB

      • staging area暂存区:隔离工作目录和Git仓库

      • 文件的三种状态:

        • Modified已修改
        • Staged已暂存
        • Committed已提交
      • Object Graph对象图:版本间的演化关系图,是一张有向无环图DAG,存储在.git目录下,下面是一个Object Graph的示意图,取自讲义2-1

        在这里插入图片描述
        每一个commit在图中表示为一个节点,也被称为一个版本,commit指向它的父亲

  • 可变性和不可变性

  • spec规约

    • 强度:前置更弱、后置更强的规约强度更高
    • 分类
      • 确定的规约:给定一个满足前置条件的输入,输出唯一且确定
      • 欠定的规约(Under-deterministic):同一个输入可以有多个输出(多次执行输出相同)
      • 非确定的规约(NonDeterministic):同一个输入,多次执行时输出可能不同,可能实现中有随机或者和时间有关的因素
  • ADT抽象数据类型

    • ADT是由操作定义的,与内部实现无关
    • 可变与不可变
      • 可变数据类型:提供了可改变其内部数据的值的操作
      • 不可变数据类型:提供的操作不能改变内部值,而是构造新的对象
    • ADT操作的分类
      • 构造器Creators
      • 生产器Producers
      • 观察器Observers
      • 变值器Mutators
    • 设计一个好的ADT
      • 设计简单、一致的操作
      • 足以支持client对数据的所有操作需要,且尽可能实现简单
      • 要么抽象、要么具体,不要混合
    • 抽象函数、表示独立性、不变量、表示泄露
    • 防御式拷贝
  • instanceof判断某个对象是不是特定类型或其子类型

    instanceof is dynamic type checking, not the static type

    重写的equals()函数必须是等价关系:1.自反 2.对称 3.传递

    hashCode()函数要求:一次运行中多次调用同一对象的hashCode()方法,返回值必须相同;不过在多次运行中不要求其返回值相同

    等价的对象必须有相同的 hashCode

  • Equality of Mutable Types:

    • observational equality观察等价性: 在不改变状态的情况下, 两个 mutable 对象是否看起来一致
    • behavioral equality行为等价性:调用对象的任何方法都展示出一致的结果,对可变类型来说,往往倾向于实现严格的观察等价性

    当可变类型被mutator改变时,hashCode值会发生变化(具体见3-5 Equality in ADT and OOP)
    The Java library is unfortunately inconsistent about its interpretation of equals() for mutable classes.
    比如在JDK中,Collections使用observational equality,而其他的可变类(like StringBuilder)使用behavioral equality.

  • Liskov Substitution Principle(LSP)
    如果对于类型 T 的对象x, q(x) 成立, 那么对于类型 T 的子类型 S 的对象y, q(y) 也成立。
    要求
    1. 前置条件不能强化
    2. 后置条件不能弱化(前两条其实为强度不变弱的spec)
    3. 要不变量保持父类的不变量
    4. 子类型方法参数:逆变
    5. 子类型方法返回值:协变
    6. 抛出的异常类型:协变
    协变:
    父类型到子类型:越来越具体 specific
    返回值类型:不变或变得更具体
    异常的类型:不变或变得更具体
    逆变:
    参数类型:不变或变得更抽象

    泛型是类型不变的:ArrayList是List子类,但List不是 List子类,类型参数在编译后被丢弃,在运行时不可用,该过程被称为类型擦除

    委托的种类:

    1. Dependency:use a,临时的委托. 如duck.fly(new FlyWithWings()); 在Duck类中, fly方法:void fly(Flyable f) {…}
    2. Association:has a,永久的委托,以成员变量的形式存在
    3. Composition:owns a,更强的Association,但是难以变化,如成员变量不可设定、不可改变
    4. Aggregation:has a,更弱的Association,可以动态变化
      可以认为 Composition/Aggregation 是 Association 的两种具体形态

    面向复用的设计模式

    Structural patterns

    • Adapter适配器模式

      将某个类/接口转换为client期望的其他形式

    • Decorator装饰器模式

      为每一个特性构造子类,通过委托增加到对象上
      在这里插入图片描述
      Component是被装饰的的对象,Decorator是抽象类,是所有装饰类的父类

    • Facade外观模式

    Behavioral patterns

    • Strategy策略模式:给client提供多种策略
      在这里插入图片描述

    • Template method模板模式

      使用的是继承和重写,常常用于白盒框架,比如下面的安装两种车的例子:
      在这里插入图片描述
      在BuildCar()实现的通用的代码逻辑

    • Iterator迭代器模式

      为客户端提供遍历元素的方法

      Iterable<>接口:实现该接口的类需要实现Iterator iterator()方法;

      Iterator<>接口:实现该接口的类需要实现boolean hasNext()方法和E next()方法.

    面向可维护性编程

    度量可维护性的标准:圈复杂度、代码行数、继承树的深度、类之间的耦合度、单元测试的覆盖度等

    追求:高内聚、低耦合

  • 评价可维护性的标准:

    • 可分解性Decomposability

      将问题分解为各个可独立解决的问题的能力

      目标:使模块之间的依赖关系显式化和最小化

    • 可组合性Composability

      将模块组合起来形成新的系统的能力

      目标:使模块可以在不同的环境下复用

    • 可理解性Understandability

      每个模块被设计者理解的能力

    • 可持续性Continuity

      规格说明中小的变化将只影响一小部分,而不会影响这个体系结构

    • 出现异常之后的保护Protection

      运行时的不正常将局限于小范围模块内

  • 五项规则:

    • 直接映射:模块的结构与现实世界中问题领域的结构保持一致——持续性、可分解性
    • 尽可能少的接口:模块应尽可能少的与其他模块通讯——持续性、保护性、可理解性、可组合性
    • 尽可能小的接口:如果两个模块通讯,那么它们应交换尽可能少的信息——可持续性、保护性
    • 显示接口:当A与B通讯时,应明显的发生在A与B的接口之间——可分解性、可组合性、可持续性、 可理解性
    • 信息隐藏:经常可能发生变化的设计决策应尽可能隐藏在抽象接口后面——可持续性
  • 设计原则:

    • SRP单一责任原则:ADT中不应该有多于一个原因使其发生变化
    • OCP开放-封闭原则:对扩展的开放和对修改的封闭
      利用继承或委托实现扩展
    • LSP里氏替换原则
    • DIP依赖转置原则:高层模块不应该依赖于低层模块,二者都应该依赖于抽象,抽象不应该依赖于实现细节,实现细节依赖于抽象
    • ISP接口聚合原则:客户端不应依赖于它不需要的方法——尽量使用小接口而不是“胖”接口

面向可维护性的设计模式

  • Creational patterns
    工厂方法模式 Factory Method pattern
    抽象工厂模式 Abstract Factory,抽象工厂模式和工厂方法模式的不同在于,抽象工厂创建的不是一个完整产品,而是“产品族”(遵循固定搭配规则的多类产品的实例),得到的结果是:多个不同产品的object ,各产品创建过程对 client 可见,但“搭配”不能改变。
    本质上, Abstract Factory 是把多类产品的 factory method 组合在一起,如果不用抽象工厂而使用多个工厂方法可能导致client用错工厂——牛仔裤+西装

  • Structural patterns
    Proxy代理模式,某些对象不希望被client直接访问,故设置Proxy,在二者间建立防火墙
    三种类型:

    • Remote Proxy远程代理,为对象创建缓存
    • Virtual Proxy虚代理
    • Access control访问控制
      作用:隔离对复杂对象的访问,降低其代价,同时可以控制访问
  • Behavioral patterns

    • Observer观察者模式 “偶像粉丝模式”

      粉丝(Observer)会接受偶像(Subject)的状态变化,或者说偶像会广播自己的变化
      偶像类中存储了粉丝列表(List),当自身的状态发生变化时通知粉丝,采用的是委托的机制

    • Visitor访问者模式
      Strategy模式和Visitor模式的异同:
      两者都是通过委托建立两个对象的动态联系;但是Visitor强调的是外部定义某种对ADT的操作,该操作与ADT关系不大,而Strategy则强调的是ADT内部某些要实现的功能的相应算法的灵活替换,这些算法也是ADT的重要组成部分,总的来说visitor是站在外部client的角度,灵活增加对ADT的各种不同操作(哪怕ADT没实现该操作),strategy则是站在内部ADT的角度,灵活变化对其内部功能的不同配置。

基于状态的构造技术

State Pattern状态模式
Memento备忘录模式,记录对象的历史状态,以便于回滚
Originator:需要备忘的类
Caretaker:添加Originator的备忘记录和恢复
Memento:备忘录,记录Originator对象的历史状态
Originator只需要存储当前状态,在每次备份时,都生成一个外部的Memento对象,Caretaker负责掌控全部的状态备份,客户端通过它来操纵ADT的状态备份与恢复
Caretaker中保存了一个List用于回滚

基于语法的构造技术

正则表达式

终止节点(叶节点、终止符)
产生式 = 终结符、产生式和操作符
*、+、?、连接xy、或|、[]、[^] (注:在[]中的"^" 表示取反,但不在方括号中^匹配的是字符串的开头,$匹配结尾
特殊字符:
‘.’:代表任何单字符,除了换行符’\n’
‘\d’:一个数字,等同于[0-9]
‘\s’:匹配任何空白符,包括空格、制表符、换页符等,等价于[ \f\n\r\t\v],注意最开头有空格,\f为换页符,\r是回车符,\n换行符,\v垂直制表符
‘\w’:匹配大小写字母和数字以及下划线,等同于[a-zA-Z_0-9]
‘\D’:非数字,相当于[^0-9]
‘\S’:非空白符,[^\s]
‘\W’:非字母、非数字、非下划线,[^\w]
Greedy贪婪的匹配:被强制要求第一次尝试匹配时读入整个输入串,匹配失败时从后往前逐个字符地回退并尝试匹配,直到匹配成功或无字符可退
Reluctant勉强的:第一次匹配时只匹配首字符,失败后逐个往后增加,直到匹配成功或无字符可加
Possessive独占的直接匹配整个字符串

健壮性、正确性

正确性是最重要的指标
对外的接口倾向于健壮性;对内的实现倾向于正确性
可靠性Reliability=健壮性Robustness+正确性Correctness

断言和异常

Java中的错误、异常类
Throwable为所有异常、错误类的基类
Error+Exception
Unchecked Exceptions:所有的Error以及RuntimeException,可以不捕获,不写异常处理,编译器不会check,unchecked
Checked Exception:需要自己的程序进行捕获并进行异常处理,编译器会check,checked
Try-with-Resource语句

try(Ressource res = ...) {
	... 
}
==
try {
    ...
}
finally {
	...
	res.close();
}

也就是说当try(Resource res = …) {}的try退出时,res.close会被自动执行
举个栗子:
try(Scanner in = new Scanner(new File("…"))) {

}
try退出时会自动执行in.close();甚至还可以打开多个Resource,退出try时也会关闭多个,每个资源之间用";"隔开
try(Scanner in = …; PrintWriter out = …) {

}

assert断言在开发阶段帮助调试程序,运行时关闭断言避免降低性能
需要注意的是assert必须避免副作用;如assert list.remove(x)语句,加入assert被disable了,list的元素就不会被删除,这种情况用boolean found = list.remove(x); assert found;
程序之外的事,不受你控制,不要乱用断言,断言检查的只是程序内部状态是否符合规约,外部的东西使用异常进行处理,断言确保正确性,异常确保健壮性;使用异常处理"预料到可以发生"的情况,使用断言处理"绝不应该发生"的情况
pre-condition用异常处理,post-condition用assert处理;nonpublic的方法的前置条件可以用assert,public和nonpublic的方法的后置条件都可以用assert处理

snapshot
基本类型的值直接显示,对象类型的值用圆圈圈起来;不可变类型的对象用双圈椭圆;final的使用双线箭头

线程

Thread.sleep(),使当前线程休眠,值得注意的是进入休眠的线程不会丢失现有的锁,从休眠中苏醒后可以继续执行
t.interrupt(),向其他线程发出中断信号,值得注意的是中断是一种请求,线程不一定要立即停止,只有在线程正在Thread.sleep(),Thread.join()或Object.wait()时才会立即响应,否则只会设置线程的中断状态,将中断状态设置为true,表示有线程希望自己中断
t.isInterrupted()只是查询状态,不会做其他事情
Thread.interrupted()查询状态,并将其重置为false,该函数返回的是设置前的状态
值得注意的是interrupt()和isInterrupted()都是实例方法,而interrupted()是类方法(静态)
t.join()让当前线程暂停直到线程t执行结束

线程安全Threadsafe

线程安全可以从三个方面理解:

  • Behaves correctly:不违反spec、保持RI
  • Regardless of how threads are executed:与操作系统如何调度无关
  • Without additional coordination:不需要在spec中强制要求client满足某种“线程安全”的义务

达到线程安全的四种手段

  • 限制数据共享:线程间不共享mutable的数据

  • 使用不可变的数据类型和不可变的引用

    如果一个ADT满足加强的Immutability的定义,则可以确定该ADT线程安全。加强的Immutability定义:

    • 没有mutator
    • 所有成员变量都是private final的
    • 没有表示泄露
    • 可变对象没有任何形式的mutation
  • 使用线程安全的ADT

    如synchronized类型,但是不要保存别名;而且即使使用synchronized类型,迭代器仍然不安全,使用迭代器时需要加锁

    synchronized(c) {
        for(Type e : c)
            ...
    }
    

    虽然synchronized类型保证某一操作线程安全,但是多个操作间也存在竞争

  • 通过锁的机制共享线程安全的可变数据

    使用synchronized代码块或者synchronized方法,如果使用synchronized方法,当线程调用该方法时,会自动获取该方法所在对象的内部锁,并在方法返回时释放它,即使返回是由未捕获的异常引起的,也会释放锁;如果该方法是static的,由于静态方法与类关联,而不是对象,此时线程获取与该类关联的Class对象的内部锁。但是使用锁机制会降低程序的并发性,影响性能

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值