软件工程期末复习

一、工程化编程实战
1、代码风格规范
(1)代码风格
原则:简明、易读、⽆⼆义性
好处
代码⻛格决定了代码是否规范、是否易于阅读;
如果对⽅的代码杂乱⽆章,读起来都费劲;
减少⼀些不必要的语法错误。
(2)好代码
规范整洁。遵守常规语⾔规范,合理使⽤空格、空⾏、缩进、注释等;  
逻辑清晰。没有代码冗余、重复,让⼈清晰明了的命名规则。做到逻辑清晰不仅要求程序员的编程能⼒,更重要的是提⾼设计能⼒,选⽤合适的设计模式、软件架构⻛格可以有效改善代码的逻辑结构,会让代码简洁清晰;  
优雅。优雅的代码是设计的艺术,是编码的艺术,是编程的最⾼追求。 ⼀般来讲,我们对代码⻛格的基本原则要求是简明、易读、⽆⼆义性。
(3)性能优先策略背后隐藏的代价
当软件⼯程师的⼈⼒成本远⼤于所消耗的计算资源成本时,提⾼代码编写的⼯作效率将更有价值;  
质量保证的⼈⼒成本和质量保证的成效也⽐所消耗的计算资源成本更有价值;  
性能优先的策略往往会让代码很难理解,结果需要消耗更多的⼯时;  
⾯向机器的代码修改起来更困难,可扩展性差,同样会消耗更多⼯时。
(4)拒绝修修补补、要不断重构代码
如果制流程盘根错节、判定过程难以理解、或者⽆条件的分⽀难以消除,那么就该重新返回到设计了。重新检查设计,搞清楚问题是设计中的固有问题,还是设计转化为代码的过程中引⼊的问题。  
(5)极限编程中不同的两类参与者的合作
客户:他们使用事例定义特性,描述详细的测试并分配优先级
程序员:实现故事的人
(6)结对编程中同类参与者的合作
驾驶员或飞行员:控制计算机并编写代码
领航员:检查驱动程序代码并提供反馈
2、模块化软件设计
(1)模块化:在软件系统设计时保持系统内各部分相对独⽴,以便每⼀个部分可以被独⽴地进⾏设计和开发
目标:每⼀个软件模块都将只有⼀个单⼀的功能,并相对独⽴于其他软件模块
每⼀个软件模块都容易理解容易开发。 
整个软件系统也更容易定位软件缺陷,因为每⼀个软件缺陷都局限在很少的⼀两个软件模块内。  
整个系统的变更和维护也更容易,因为⼀个软件模块内的变更只影响很少的⼏个软件模块。  
软件模块化的程度:耦合度、内聚度
耦合度:软件模块之间的依赖程度
内聚度:软件模块内部各种元素之间互相依赖的紧密程度
3、可重用软件设计
(1)消费者重⽤  & ⽣产者重⽤

消费者重⽤:软件开发者在项⽬中重⽤已有的⼀些软件模块代码,以加快项⽬⼯作进度。
软件开发者在重⽤已有的软件模块代码时⼀般会重点考虑如下四个关键因素(需要按照顺序依次评估):  
该软件模块是否能满⾜项⽬所要求的功能;  
采⽤该软件模块代码是否⽐从头构建⼀个需要更少的⼯作量,包括构建软件模块和集成软件模块等相关的⼯作;  
该软件模块是否有完善的⽂档说明;  
该软件模块是否有完整的测试及修订记录。 
(2)接⼝:互相联系的双⽅共同遵守的⼀种协议规范。在软件系统内部⼀般的接⼝⽅式是通过定义⼀组 API 函数来约定软件模块之间的沟通⽅式。定义了软件模块对系统的其他部分提供了怎样的服务,以及系统的其他部分如何访问所提供的服务。

在⾯向过程的编程中,接⼝⼀般定义了数据结构及操作这些数据结构的函数;
在⾯向对象的编程中,接⼝是对象对外开放的⼀组属性和⽅法的集合。函数或⽅法具体包括名称、参数和返回值等。
(3)微服务:由⼀系列独⽴的微服务共同组成软件系统的⼀种架构模式

每个微服务单独部署,跑在⾃⼰的进程中,也就是说每个微服务可以有⼀个⾃⼰独⽴的运⾏环境和软件堆栈
每个微服务为独⽴的业务功能开发,⼀般每个微服务应分解到最⼩可变产品,达到功能内聚的理想状态。微服务⼀般通过 RESTful API 接⼝⽅式进⾏封装;
系统中的各微服务是分布式管理的,各微服务之间⾮常强调隔离性,互相之间⽆耦合或者极为松散的耦合,系统通过前端应⽤或 API ⽹关来聚合各微服务完成整体系统的业务功能。
(4)接⼝与耦合度之间的关系
公共耦合:共享数据区或变量名的软件模块之间即是公共耦合。两个软件模块之间的接⼝定义是隐式的共享了共享了数据区或变量名。  
数据耦合:在软件模块之间仅通过显式的调⽤传递基本数据类型即为数据耦合。  
标记耦合:在软件模块之间仅通过显式的调⽤传递复杂的数据结构 ( 结构化数据)即为标记耦合,这时数据的结构成为调⽤双⽅软件模块隐含的规格约定,因此耦合度要⽐数据耦合⾼。但相⽐公共耦合没有经过显式的调⽤传递数据的⽅式耦合度要低。
4、可重⼊函数与线程安全
(1)线程
操作系统能够进⾏运算调度的最⼩单位。
它包含在进程之中,是进程中的实际运作单位。
⼀个线程指的是进程中⼀个单⼀顺序的控制流,⼀个进程中可以并发多个线程,每条线程并⾏执⾏不同的任务。⼀般默认⼀个进程中只包含⼀个线程。  
(2)函数调⽤堆栈
借助函数调⽤堆栈可以将我们写的函数调⽤代码整理成⼀个顺序执⾏的指令流,也就是⼀个线程,每⼀个线程都有⼀个独⾃拥有的函数调⽤堆栈空间,其中函数参数和局部变量都存储在函数调⽤堆栈空间中,因此函数参数和局部变量也是线程
独⾃拥有的。除了函数调⽤堆栈空间,同⼀个进程的多个线程是共享其他进程资源的,⽐如全局变量是多个线程共享的。
(3)可重⼊函数
可重⼊函数可以由多于⼀个任务并发使⽤,⽽不必担⼼数据错误。
不可重⼊函数不能由超过⼀个任务所共享,除⾮能确保函数的互斥(或者使⽤信号量,或者在代码的关键部分禁⽤中断)。
可重⼊函数可以在任意时刻被中断,稍后再继续运⾏,不会丢失数据。
可重⼊函数要么使⽤局部变量,要么在使⽤全局变量时保护⾃⼰的数据。
(4)线程安全
如果你的代码所在的进程中有多个线程在同时运⾏,⽽这些线程可能会同时运⾏这段代码。如果每次运⾏结果和单线程运⾏的结果是⼀样的,⽽且其他的变量的值也和预期的是⼀样的,就是线程安全的。  
线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,⽽⽆写操作,⼀般来说,这个全局变量是线程安全的;
若有多个线程同时执⾏读写操作,⼀般都需要考虑线程同步,否则就可能影响线程安全。
可重⼊的函数不⼀定是线程安全的;
可重⼊的函数在多个线程中并发使⽤时是线程安全的,但不同的可重⼊函数(共享全局变量及静态变量)在多个线程中并发使⽤时会有线程安全问题;  
不可重⼊的函数⼀定不是线程安全的。
二、从需求分析到软件设计
1、需求
(1)需求:对⽤户期望的软件⾏为的表述
(2)需求分析:在获取需求的基础上进⼀步对软件涉及的对象或实体的状态、特征和⾏为进⾏准确描述或建模的⼯作。
(3)需求的重要性:导致项目失败的主要因素(不完整的需求、缺乏用户参与、不切实际的期望、缺乏管理层的支持、不断变化的要求和规格、缺乏计划、系统不再需要);需求过程的某些部分几乎涉及所有这些原因;如果没有及早发现,需求错误可能代价高昂。
(4)需求类型:
功能需求,根据需要的活动描述需要的行为
质量需求或非功能需求,描述软件必须具有的一些质量特性
设计约束,如平台或接口组件的选择
过程约束,用于构建系统的技术或资源的限制
(5)需求获取方法:
面试的股份持有者
检查可用的文件
观察当前系统(如果存在)
与用户做学徒,了解更多的细节用户的任务
在小组中采访用户或利益相关者
使用特定领域的策略,如联合应用设计
与现有和潜在用户进行头脑风暴
(6)高质量的需求:制定需求测试;解决冲突;需求的特点(正确、保持一致、无二义性、完整、可行性、无与目标不相关的需求、可测试的、可追踪的)
2、需求分析
(1)需求分析的两类基本⽅法
原型化⽅法:可很好地整理出⽤户接⼝⽅式( UI , User Interface ),⽐如界⾯布局和交互操作过程。
建模的⽅法:可快速给出有关事件发⽣顺序或活动同步约束的问题,能够在逻辑上形成模型来整顿繁杂的需求细节。  
(2)⽤例:经过逻辑整理抽象出来的⼀个业务过程。某个⻆⾊触发某个⽤例为相应的⻆⾊完成⼀个业务任务。
业务过程:在待开发软件所处的业务领域内完成特定业务任务的⼀系列活动。
⽤例的⼏个基本要素:    
⼀个⽤例应该由业务领域内的某个⻆⾊( Actor )所触发。  
⽤例必须能为特定的⻆⾊完成⼀个特定的业务任务。  
⼀个⽤例必须终⽌于某个特定⻆⾊,也就是特定⻆⾊明确地或者隐含地得到了业务任务完成的结果。
⻆⾊是业务领域内的参与者或者业务实体(人,客户;外部的硬件或软件;待开发软件系统内部的⼀个组件,内部计时器可以触发某个业务过程。)
⽤例的三个抽象层级:
抽象⽤例:要⽤⼀个⼲什么、做什么或完成什么业务任务的动名词短语指明⼀个⽤例。(“ 打电话 ”)
⾼层⽤例:给⽤例的范围划定⼀个边界,⽤例在什么时候什么地⽅开始,以及在什么时候什么地⽅结束。(“ 打电话 ” 这⼀⽤例的开始状态就是⽤户拿起电话机听筒准备拨号,终⽌状态就是⽤户听到了接通电话的铃声反馈)
扩展⽤例:将⻆⾊和待开发软件系统为了完成⽤例所规定的业务任务的交互过程⼀步⼀步详细地描述出来。(进⼀步扩展 “ 打电话 ” 这⼀⽤例)
⽤例建模的基本步骤
从需求表述中找出⽤例(常是动名词短语表示的抽象⽤例)
描述⽤例开始和结束的状态,⽤ TUCBW 和 TUCEW 表示的⾼层⽤例
对⽤例按照⼦系统或不同的⽅⾯进⾏分类,描述⽤例与⽤例、⽤例与⻆⾊之间的上下⽂关系,并画出⽤例图
进⼀步逐⼀分析⽤例与⻆⾊的详细交互过程,完成⼀个两列的表格将⻆⾊和待开发软件系统之间从⽤例开始到⽤例结束的所有交互步骤都列举出来扩展⽤例。  
提取⽤例的⽅法:
从需求中寻找业务领域相关的动名词和动名词短语,⽐如做什么事、什么事情必须被完成,或者执⾏某任务等
验证这些业务领域相关的动名词和动名词短语到底是不是⽤例。验证业务领域相关的动名词或动名词短语是不是⽤例的标准是满⾜四个必要条件:业务过程、由某个⻆⾊触发开始、显式地或隐式地终⽌于某个⻆⾊、为某个⻆⾊完成了有⽤的业务⼯作
在需求中识别出⻆⾊、系统或⼦系统。   
业务领域建模
收集应⽤业务领域的信息。聚焦在功能需求层⾯,也考虑其他类型的需求和资料;(获取需求阶段)  
头脑⻛暴。列出重要的应⽤业务领域概念,给出这些概念的属性,以及这些概念之间的关系;  
给这些应⽤业务领域概念分类。分别列出哪些是类、哪些属性和属性值、以及列出类之间的继承关系、聚合关系和关联关系。  
将结果⽤  UML  类图画出来。

三、软件的结构、特性和描述⽅法
1、软件
1、软件的基本构成元素
(1)对象
对象是类的实例,是属性和⽅法的集合。【属性是描述对象或存储对象的状态信息,也可是⼀个对象)】
对象的创建:构造⽅法;销毁:析构⽅法。
(2)函数和变量 / 常量
全局常量、字符串常量、函数以及编译时可决定的某些东⻄⼀般存储在代码段;  
初始化的全局变量、初始化的(全局/局部)静态变量存储在已初始化数据段;  
未初始化的全局变量、未初始化的(全局/局部)静态变量存储在未初始化数据段;  
动态内存分配( malloc 、 new 等)存储在堆中;  
(初始化/未初始化)局部变量(不包含静态变量)、局部常量存储在栈中;  
命令⾏参数、环境变量存储在与栈底紧挨着的位置。  
(3)指令和操作数
指令:由  CPU  加载和执⾏的软件基本单元。⼀条指令有四个组成部分:标号、指令助记符、操作数、注释。⼀般指令可以表述为指令码 +操作数。指令码可以是⼆进制的机器指令编码,也可以是⼋进制的编码,程序员更喜欢⽤汇编语⾔指令助记符,如  mov 、 add  和  sub ,给出了指令执⾏操作的线索。  
操作数类型:
⽴即数,⽤数字⽂本表示的数值;
寄存器操作数,使⽤  CPU  内已命名的寄存器;
内存操作数,引⽤内存位置。
2、软件的基本结构
(1)顺序结构
(2)分⽀结构:在顺序结构的基础上,利⽤影响标志寄存器上标志位的指令和跳转指令组合起来借助于标志寄存器或特定寄存器暂存条件状态实现分⽀结构。
(3)循环结构
(4)函数调⽤框架
(5)继承和对象组合
(6)多态
class A
{
public:
A(){}
virtual void foo()
{
cout<<“This is A.”<<endl;
}
};

class B: public A
{
public:
B(){}
void foo()
{
cout<< “This is B.”<<endl;
}
}

class C: public A
{
public:
C(){}
void foo()
{
cout<<“This is C.”<<endl;
}
};
int main(int argc, char *argv[])
{
A a = new B();
a->foo(); //34
a = new C();
a->foo();//37
// 显然 33 ⾏和 35 ⾏两句代码都是 a->foo() ,代码完全相同,执⾏效果却不同,这就是多态。
(7)回调函数:是⼀个⾯向过程的概念,是代码执⾏过程的⼀种特殊流程
// 回调函数
int SearchCondition(tLinkTableNode * pLinkTableNode, void * args)
{
char * cmd = (char
) args;
tDataNode * pNode = (tDataNode )pLinkTableNode;
if(strcmp(pNode->cmd, cmd) == 0)
{
return SUCCESS;
}
return FAILURE;
}
// 传递回调函数
SearchLinkTableNode(head, SearchCondition,(void
)cmd)

// 执⾏回调函数
tLinkTableNode * SearchLinkTableNode(tLinkTable pLinkTable, int Conditon(tLinkTableNode * pNode, void * args), void * args)
{

tLinkTableNode * pNode = pLiAnkTable->pHead;
while(pNode != NULL)
{
if(Conditon(pNode,args) == SUCCESS)
{
return pNode;
}
pNode = pNode->pNext;
}
return NULL;
}A
(8)闭包:是变量作⽤域的⼀种特殊情形,⼀般⽤在将函数作为返回值时,该函数执⾏所需的上下⽂环境也作为返回的函数对象的⼀部分,这样该函数对象就是⼀个闭包;
function makeFunc() {
var name = “Mozilla”;
function displayName() {
alert(name);
}
return displayName;
}
var myFunc = makeFunc();
myFunc();
(9)异步调⽤  
var promise = new Promise(function(resolve, reject) {
if (/
异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});

promise.then(function(value) { // resolve(value)
// success
}, function(value) { // reject(error)
// failure
});
(10)匿名函数
function timeout(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
timeout(100).then(() => {
console.log(‘done’);
});
3、软件设计模式
(1)关系(继承、聚合、关联)

继承关系(车辆 is 小汽车) 聚合关系(发动机引擎 part of ⼩汽⻋) 关联关系

(2)设计模式的优点:
提⾼程序员的思维能⼒、编程能⼒和设计能⼒;
使程序设计更加标准化,代码编制更加⼯程化,使软件开发效率⼤⼤提⾼,从⽽缩短软件的开发周期;
使设计的代码可重⽤性⾼、可读性强、可靠性⾼、灵活性好、可维护性强。
(3)设计模式的部分组成:设计模式名称;⽬的(该设计模式要解决什么样的问题);解决⽅案;解决⽅案有哪些约束和限制条件。
(4)设计模式的分类:
根据模式是主要⽤于类上还是主要⽤于对象上来划分:
类模式:⽤于处理类与⼦类之间的关系。这些关系通过继承来建⽴,是静态的,在编译时刻便确定下来了。⽐如模板⽅法模式等属于类模式。
对象模式:⽤于处理对象之间的关系。这些关系可以通过组合或聚合来实现,在运⾏时刻是可以变化的,更具动态性。由于组合关系或聚合关系⽐继承关系耦合度低,因此多数设计模式都是对象模式。
根据设计模式可以完成的任务类型来划分:
创建型模式:⽤于描述 “ 怎样创建对象 ” ,它的主要特点是 “ 将对象的创建与使⽤分离 ” 。⽐如单例模式、原型模式、建造者模式等属于创建型模式。  
结构型模式:⽤于描述如何将类或对象按某种布局组成更⼤的结构,⽐如代理模式、适配器模式、桥接模式、装饰模式、外观模式、享元模式、组合模式等属于结构型模式。结构型模式分为类结构型模式和对象结构型模式,前者采⽤继承机制来组织接⼝和类,后者⾤⽤组合或聚合来组合对象。由于组合关系或聚合关系⽐继承关系耦合度低,所以对象结构型模式⽐类结构型模式具有更⼤的灵活性。  
⾏为型模式:⽤于描述程序在运⾏时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都⽆法单独完成的任务,它涉及算法与对象间职责的分配。⽐如模板⽅法模式、策略模式、命令模式、职责链模式、观察者模式等属于⾏为型模式。⾏为型模式分为类⾏为模式和对象⾏为模式,前者采⽤继承在类间分配⾏为,后者采⽤组合或聚合在对象间分配⾏为。由于组合关系或聚合关系⽐继承关系耦合度低,所以对象⾏为模式⽐类⾏为模式具有更⼤的灵活性。
(5)设计模式
单例( Singleton )模式:某个类只⽣成⼀个实例,该类提供⼀个全局访问点供外部获取该实例。如数据库实例。  
原型( Prototype )模式:将⼀个对象作为原型,通过对其进⾏复制⽽克隆出多个和原型类似的新实例。⼏乎所有通过复制的⽅式创建新实例的场景都有原型模式。  
建造者( Builder )模式:将⼀个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。主要应⽤于复杂对象中的各部分的建造顺序相对固定或者创建复杂对象的算法独⽴于各组成部分。
代理( Proxy )模式:为某对象提供⼀种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从⽽限制、增强或修改该对象的⼀些特性。代理模式是不要和陌⽣⼈说话原则的体现。如外部接⼝本地化将外部的输⼊和输出封装成本地接⼝,有效降低模块与外部的耦合度。
适配器( Adapter )模式:将⼀个类的接⼝转换成客户希望的另外⼀个接⼝,使得原本由于接⼝不兼容⽽不能⼀起⼯作的那些类能⼀起⼯作。继承和对象组合都可以实现适配器模式,但由于组合关系或聚合关系⽐继承关系耦合度低,所以对象组合⽅式的适配器模式⽐较常⽤。

装饰( Decorator )模式:在不改变现有对象结构的情况下,动态地给对象增加⼀些职责,即增加其额外的功能。装饰模式实质上是⽤对象组合的⽅式扩展功能,因为⽐继承的⽅式扩展功能耦合度低。装饰模式在  Java  语⾔中的最著名的应⽤莫过于  Java I/O  标准库的设计了。例如, InputStream  的⼦类  FilterInputStream , OutputStream  的⼦类  FilterOutputStream , Reader  的⼦类  BufferedReader  以及  FilterReader ,还有  Writer  的⼦类  BufferedWriter 、 FilterWriter  以及  PrintWriter  等,它们都是抽象装饰类。  
外观( Facade )模式:为复杂的⼦系统提供⼀个⼀致的接⼝,使这些⼦系统更加容易被访问。  
享元( Flyweight )模式:运⽤共享技术来有效地⽀持⼤量细粒度对象的复⽤。⽐如线程池、固定分配存储空间的消息队列等往往都是该模式的应⽤场景。
策略( Strategy )模式:定义了⼀系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使⽤算法的客户。策略模式是多态和对象组合的综合应⽤。  
命令( Command )模式:将⼀个请求封装为⼀个对象,使发出请求的责任和执⾏请求的责任分割开。这样两者之间通过命令对象进⾏沟通,这样⽅便将命令对象进⾏储存、传递、调⽤、增加与管理。
模板⽅法模式:定义⼀个操作中的算法⻣架,⽽将算法的⼀些步骤延迟到⼦类中,使得⼦类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。模版⽅法是继承和重载机制的应⽤,属于类模式。
职责链模式:为了避免请求发送者与多个请求处理者耦合在⼀起,将所有请求的处理者通过前⼀对象记住其下⼀个对象的引⽤⽽连成⼀条链;当有请求发⽣时,可将请求沿着这条链传递,直到有对象处理它为⽌。通过这种⽅式将多个请求处理者串联为⼀个链表,去除请求发送者与它们之间的耦合。
中介者模式:定义⼀个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。在现实⽣活中,常常会出现好多对象之间存在复杂的交互关系,这种交互关系常常是 “ ⽹状结构 ” ,它要求每个对象都必须知道它需要交互的对象。如果把这种 “ ⽹状结构 ” 改为 “ 星形结构 ” 的话,将⼤⼤降低它们之间的 “ 耦合性 ” ,这时只要找⼀个 “ 中介者 ” 就可以了。在软件的开发过程中,这样的例⼦也很多,例如,在  MVC  框架中,控制器( C )就是模型( M )和视图( V )的中介者,采⽤ “ 中介者模式 ” ⼤⼤降低了对象之间的耦合性,提⾼系统的灵活性。
观察者( Observer )模式/发布 - 订阅模式:指多个对象间存在⼀对多的依赖关系,当⼀个对象的状态发⽣改变时,把这种改变通知给其他多个对象,从⽽影响其他对象的⾏为,这样所有依赖于它的对象都得到通知并被⾃动更新。

(6)设计原则

开闭原则:软件应当对扩展开放,对修改关闭。
Liskov 替换原则:继承必须确保超类所拥有的性质在⼦类中仍然成⽴。(⼦类继承⽗类时,除添加新的⽅法完成新增功能外,尽量不要重写⽗类的⽅法。当今为了降低耦合度我们往往使⽤对象组合来替代继承关系)
依赖倒置原则:⾼层模块不应该依赖低层模块,两者都应该依赖其抽象(接⼝或者抽象类);抽象不应该依赖细节(具体的实现类),细节应该依赖抽象。(要⾯向接⼝编程,不要⾯向实现编程)(降低模块之间的耦合度、加强模块的抽象封装、提⾼模块的内聚度)
单⼀职责原则:规定⼀个类应该有且仅有⼀个引起它变化的原因,否则类应该被拆分。(⼀个类只负责⼀项职责)
迪⽶特法则/最少知识原则:如果两个软件实体⽆须直接通信,那么就不应当发⽣直接的相互调⽤,可以通过第三⽅转发该调⽤。(降低类之间的耦合度,提⾼模块的相对独⽴性)
合成复⽤原则(组合 / 聚合复⽤原则):在软件复⽤时,要尽量先使⽤组合或者聚合关系来实现,其次才考虑使⽤继承关系来实现。如果要使⽤继承关系,则必须严格遵循 Liskov替换原则。  
类的复⽤:继承复⽤;对象组合复⽤
继承复⽤的缺点:破坏了类的封装性。因为继承会将⽗类的实现细节暴露给⼦类,⽗类对⼦类是透明的,所以这种复⽤⼜称为 “ ⽩箱 ” 复⽤。⼦类与⽗类的耦合度⾼。⽗类的实现的任何改变都会导致⼦类的实现发⽣变化,这不利于类的扩展与维护。继承复⽤限制了复⽤的灵活性。从⽗类继承⽽来的实现是静态的,在编译时已经定义,所以在运⾏时不可能发⽣变化。
合成复⽤的优点:采⽤组合或聚合复⽤时,可以将已有对象纳⼊新对象中,使之成为新对象的⼀部分,新对象可以调⽤已有对象的功能。优点:维持了类的封装性。因为属性对象的内部细节是新对象看不⻅的,所以这种复⽤⼜称为 “ ⿊箱 ” 复⽤;新旧类之间的耦合度低。这种复⽤所需的依赖较少,新对象存取属性对象的唯⼀⽅法是通过属性对象的接⼝;复⽤的灵活性⾼。这种复⽤可以在运⾏时动态进⾏,新对象可以动态地引⽤与属性对象类型相同的对象。
4、软件架构举例
(1)三层架构(⾯向接⼝编程)
(2)MVC模式 /中介者模式 (Model-View-Controller)
Model (模型)代表⼀个存取数据的对象及其数据模型。  
封装核⼼数据和功能

独⽴于特定的输出表示和输⼊⾏为

只有纯粹的功能性接⼝,也就是⼀系列的公开⽅法

View (视图)决定模型以什么样的⽅式展示给⽤户,⼀般表达为可视化的界⾯接⼝。  
同⼀个模型可以对应于多个视图,这样对于视图⽽⾔,模型就是可重⽤的代码。

模型内部必须保留所有对应视图的相关信息,以便在模型的状态发⽣改变时,可以通知所有的视图进⾏更新。

Controller (控制器)作⽤于模型和视图上,控制数据流向模型对象,并在数据变化时更新视图。控制器可以使视图与模型分离开解耦合。(业务逻辑处理)

(3)MVVM, Model-View-ViewModel
低耦合。View 可以独⽴于 Model 变化和修改,⼀个 ViewModel 可以绑定到不同的"View" 上,当 View 变化的时候 Model 可以不变,当 Model 变化的时候 View 也可以不变。  
可重⽤性。你可以把⼀些视图逻辑放在⼀个 ViewModel ⾥⾯,让很多 View 重⽤这段视图逻辑。  
独⽴开发。开发⼈员可以专注于业务逻辑和数据的开发( ViewModel ),设计⼈员可以专注于⻚⾯设计。  
可测试。界⾯素来是⽐较难于测试的,测试可以针对 ViewModel 来写。

(4)软件架构复⽤⽅法:克隆、重构
(5)构建软件架构模型的基本⽅法就是在不同层次上分解系统并抽象出其中的关键要素
分解⽅法:
⾯向功能,⽤例建模;  
⾯向特征,根据数量众多的某种系统显著特征在不同抽象层次上划分模块的⽅法;  
⾯向数据,在业务领域建模中形成概念业务数据模型即应⽤了⾯向数据的分解⽅法;
⾯向并发,在⼀些系统中具有多种并发任务的特点,那么我们可以将系统分解到不同的并发任务中(进程或线程),并描述并发任务的时序交互过程;  
⾯向事件,当系统中需要处理⼤量的事件,⽽且往往事件会触发复杂的状态转换关系,这时系统就要考虑⾯向事件的分解⽅法,并内在状态转换关系进⾏清晰的描述;  
⾯向对象,是⼀种通⽤的分析设计范式,是基于系统中抽象的对象元素在不同抽象层次上分解的系统的⽅法。
(6)关键视图
在合理的分解和抽象基础上抽取系统的关键要素,使用关键视图描述关键要素之间的关系。
分解视图: 依赖视图、泛化视图、执⾏视图、实现视图、部署视图 、⼯作任务分配视图
(7)软件质量
IEEE 将软件质量定义为,⼀个系统、组件或过程符合指定要求的程度,或者满⾜客户或⽤户期望的程度。
McCall 软件质量模型
从三个⻆度来定义和识别软件产品的质量:  
产品修订(变更能力)
产品转型(适应新环境)
产品运营(基本运营特点)
ISO/IEC 25010  软件质量模型
过程质量模型/过程改进模型:有 CMM/CMMI 、 ISO 9000 和 Software Process Improvement and Capability dEtermination (SPICE) 等
软件质量属性:易于修改维护(Modifiability)  、良好的性能表现、安全性、可靠性、健壮性、易⽤性、商业⽬标
四、软件危机和软件过程
1、软件危机
(1)软件危机的典型表现:开发⼤型软件过程中的根本问题是难以汇集众多参与⼈员的设计理念形成完整的、⼀致的软件复杂概念结构,从⽽使得⼤型软件项⽬往往会进展缓慢、成本暴涨及错误百出 。—— 《人月神话》
(2)软件概念结构的复杂性,⽆法达成软件概念的完整性和⼀致性,⾃然⽆法从根本上解决软件危机带来的困境。

2、软件过程模型
(1)软件⽣命周期的模式(软件过程模型):
有利于整个软件项⽬团队形成对软件开发过程的⼀致理解(团队协作);
软件过程模型提供了⼀个框架,让我们审视整个软件开发过程活动中活动互相之间是否冲突,是否有重复过程活动,是否遗漏的事项等;
在规划整个软件开发过程时,软件过程模型⽅便我们为特定的⽬标筛选和评估合适的软件过程。

(2)软件的⽣命周期:分析、设计、实现、交付和维护阶段
分析:需求分析和定义,⽐如在敏捷统⼀过程中⽤例建模和业务领域建模就属于分析阶段。
设计:分为软件架构设计和软件详细设计,前者⼀般和分析阶段联系紧密;后者⼀般和实现阶段联系紧密。
实现:分为编码和测试,其中测试⼜涉及到单元测试、集成测试、系统测试等。  
交付:主要是部署、交付测试和⽤户培训等。  
维护:⼀般是软件⽣命周期中持续时间最⻓的⼀个阶段,⽽且在维护阶段很可能会形成单独的项⽬,从⽽经历分析、设计、实现、交付⼏个阶段,最终⼜合并进维护阶段。

(3)软件过程模型

瀑布模型:把整个软件过程按顺序划分成了需求、设计、编码、测试和部署五个阶段。瀑布模型的根本特点是按顺序划分阶段,没有任何迭代。
适用:需求能完全透彻理解、需求⼏乎不会变更的项⽬
原型化的瀑布模型:
原型:原型是根据需要完成的软件的⼀部分,完成哪⼀部分是根据开发原型的⽬标确定。
在需求分析阶段,原型可能是软件的⽤户接⼝部分,⽐如⽤户交互界⾯。⽤户接⼝原型可以有效地整理需求,在需求分析的过程提供直观的反馈形式,有利于确认需求是否被准确理解。  
在设计阶段,原型可能是软件架构的关键部分,⽐如采⽤某种设计模型解决某个问题。软件架构原型可以有效地评估软件设计⽅案是否能够有效解决特定问题,有利于验证技术⽅案的可⾏性,为⼤规模投⼊开发提供技术储备和经验积累。
分阶段的增量和迭代开发过程
分阶段开发:让客户在没有开发完成之前就可以使⽤部分功能,也就是每次可以交付系统的⼀⼩部分,从⽽缩短开发迭代的周期。
交付策略:
增量开发是从⼀个功能⼦系统开始交付,每次交付会增加⼀些功能,这样逐步扩展功能最终完成整个系统功能的开发。  
迭代开发是⾸先完成⼀个完整的系统或者完整系统的框架,然后每次交付会升级其中的某个功能⼦系统,这样反复迭代逐步细化最终完成系统开发。
螺旋模型
螺旋模型将每⼀次迭代过程分为四个主要阶段:计划、确定目标、替代方案和约束条件、评估替代方案和风险、开发和测试
统⼀过程(Unified Process):⽤例驱动、以架构为中心、增量且迭代的过程
V 模型
3、个⼈软件过程 PSP ( Personal Software Process )
个体质量管理 PSP2.1
计划阶段:项⽬评估  
开发阶段:分析、设计规格、编码标准规范、设计、设计评审、编码、 代码评审、测试
统计记录各项⼯作⽤了多少时间  
项⽬测试报告  
程序规模度量  
开发完成后进⾏总结分析  
过程改进计划
4、团队软件过程 TSP
容易导致项⽬失败的团队问题:缺乏有效的领导和管理;不能做出妥协、安排或不善于合作;缺少参与; 拖拉与缺乏信⼼;质量低劣;功能多余;⽆效的组员互评
团队的基本要素:规模;凝聚⼒;团队协作
团队项⽬的基本策略:做出承诺之前先计划;完成概念设计;选择开发策略;完成初步规模估算;完成初步时间估算;评估⻛险;建⽴策略⽂档;制定配置管理计划
5、软件能⼒成熟度模型 CMM ( Capability Maturity Model For Software) && 能⼒成熟度集成模型 CMMI ( Capability Maturity Model Integration)  
(1)CMM/CMMI ⽤于评价软件⽣产能⼒、改善软件质量的⽅法,成为了评估软件能⼒与成熟度的⼀套标准,它侧重于软件开发过程的管理及⼯程能⼒的提⾼与评估。  
(2)CMMI 共有 5 个级别,⾼成熟度等级表示有⽐较强的软件综合开发能⼒:初始级;管理级;已定义级;量化管理级;持续优化级。
初始级:软件组织对项⽬的⽬标与要做的努⼒很清晰,项⽬的⽬标可以实现。但是由于任务的完成带有很⼤的偶然性,软件组织⽆法保证在实施同类项⽬时仍然能够完成任务。项⽬实施能否成功主要取决于实施⼈员。  
管理级:初级 + 软件组织在项⽬实施上能够遵守既定的计划与流程,有资源准备,权责到⼈,对项⽬相关的实施⼈员进⾏了相应的培训,对整个流程进⾏监测与控制,并联合上级单位对项⽬与流程进⾏审查。⼆级⽔平的软件组织对项⽬有⼀系列管理程序,避免了软件组织完成任务的偶然性,保证了软件组织实施项⽬的成功率。
已定义级:管理级 + 软件组织能够根据⾃身的特殊情况及⾃⼰的标准流程,将这套管理体系与流程予以制度化。这样,软件组织不仅能够在同类项⽬上成功,也可以在其他项⽬上成功。
量化管理级:已定义级 + 软件组织的项⽬管理实现了数字化。通过数字化技术来实现流程的稳定性,实现管理的精度,降低项⽬实施在质量上的波动。
持续优化级:量化管理级 + 软件组织能够充分利⽤信息资料,对软件组织在项⽬实施的过程中可能出现的问题予以预防。能够主动地改善流程,运⽤新技术,实现流程的优化。  
(3)CMMI 评估过程:
准备阶段:⼩组⼈员培训、计划以及其它必要的评估准备⼯作。
评估阶段:评估员要对记录进⾏整理,并检验所观察到的⼀切信息,然后把这些数据与 CMM/CMMI 模型进⾏⽐较,最后给出⼀个评估报告。
报告阶段
敏捷⽅法
(1)宣言:个体和互动   ⾼于   流程和⼯具;⼯作的软件   ⾼于   详尽的⽂档;客户合作   ⾼于   合同谈判;响应变化   ⾼于   遵循计划  
(2)Scrum 敏捷开发⽅法
团队⻆⾊:项⽬经理(项目开发过程);产品经理(定义产品功能和特性);团队(进⾏实际分析、设计、实现、测试)
Scrum 敏捷开发的基本流程:
找出完成产品需要做的事情
决定当前需要解决的事情
团队按照任务执⾏
得到软件的⼀个增量版本,冲刺评审会议,发布给⽤户。然后在此基础上⼜进⼀步计划增量的新功能和改进。
(3)DevOps(Development Operations):
⼀组过程、⽅法与系统的统称,⽤于促进软件开发、技术运营和质量保障部⻔之间的沟通、协作与整合
开发(软件⼯程)、技术运营和质量保障( QA )三者的交集
⼀套旨在缩短从提交变更到变更后的系统投⼊正常⽣产之间的时间,同时确保产品⾼质量的实践⽅法
(4)DevOps 和敏捷⽅法
开发和运维往往分别按照不同的节奏进⾏,导致产品部署的时间间隔过⻓使得⼀个开发团队的敏捷⼯作变成了⼀直试图避免的瀑布⽣命周期。这时⽆论开发团队有多么敏捷,从总体上改变企业业务缓慢和迟钝的表现都是极其困难的。  
DevOps 实现业务上全周期的敏捷性,在业务运营者作出决策、开发者进⾏响应和 IT 运维上线部署之间能够紧密互动和快速反馈,从⽽形成与业务需求始终努⼒保持⼀致的持续改进过程。  
DevOps 使得敏捷⽅法的优势可以体现在整个企业业务组织机构层⾯。通过实现反应灵敏且稳定部署持续交付的业务运维,使其能够与开发过程的创新保持同步, DevOps 可以做到整个业务实现过程的敏捷性。
(5)最⼩可⾏产品 MVP(Minimum Viable Product )
把产品最核⼼的功能⽤最⼩的成本实现出来(或者描绘出来),然后快速征求⽤户意⻅获得反馈进⾏改进优化。
(6)DevOps 和全栈⾃动化( Full Stack Automation )
在敏捷和精益原则的指导下,全栈⾃动化是 DevOps 得以实现的重要⽀撑。
全栈⾃动化将开发阶段和运维阶段的⼯作通过⾃动化的⽅式⽆缝衔接,从⽽极⼤地加速从需求、开发、测试、发布和运维整个过程迭代周期,做到持续集成持续交付。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值