本学期有幸选择了孟宁老师的高级软件工程课程,该课程从软件工程的基本概念,由浅入深,深刻全面的讲解了代码中的软件工程的内容。从一个码农必备的修养到软件开发中使用的工具。从一个具体的menu实例来展现软件工程的思想,从开始构建一个软件模型。此外学习了从需求分析到软件设计,再到软件设计的一些基础概论,最后学习了软件危机,“没有银弹”。
通过学习了解了软件开发的详细过程,以及设计一个软件的具体方法和实际设计时需要注意的事项。通过本门课程提升了我的软件设计能力,为今后的工作打下了坚实的基础。此外,我觉得孟老师讲授的高软,不像一些浮于表面的课程,而是以其20余年的代码编程经验和12年教学经验,真正的深入工程化编程实战,从实际的软件设计出发,来传授我们真正实用软件设计的知识。其不仅是一门课程,更像是一门科学,一门哲学。
在创建Vue对象的时候将View的id="app"与Model(JavaScript对象定义的data)绑定起来,这样’Hello Vue!'就会自动更新到View DOM元素中。View DOM元素button上的事件click绑定Vue对象的方法reverseMessage,这样点击button按钮就能触发reverseMessage,reverseMessage方法只是修改了Model中JavaScript对象定义的message,而页面却能神奇地自动更新message。就是模型数据绑定和DOM事件监听。
<!DOCTYPE html>
<html>
<head>
<title>My first Vue app</title>
<script src="https://unpkg.com/vue"></script>
</head>
<body>
<div id="app">
<p>{{ message }}</p>
<button v-on:click="reverseMessage">Reverse Message</button>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!'
},
methods: {
reverseMessage: function () {
this.message = this.message.split('').reverse().join('')
}
}
})
</script>
</body>
</html>
(2)编译视图
<p>{{ message }}</p>
<button v-on:click="reverseMessage">Reverse Message</button>
...
//创建Watcher在模型中监听视图中出现的占位符/表达式的每一个成员
var watcher = new Watcher("message"):
//绑定监听事件和method
node.addEventListener("click", "reverseMessage"):
Watcher:将Compiler的解析结果,与Observer所观察的对象连接起来建立关系,在Observer观察到对象数据变化时,接收通知,同时更新DOM,称之为Watcher;
//创建Watcher,观察者模式中的观察者
var Watcher = function(exp, vm, cb){
this.exp = exp; // 占位符/表达式的一个成员
this.cb = cb; //更新视图的回调函数
this.vm = vm; //ViewModel
this.value = null;
this.getter = parseExpression(exp).get;
this.update();
};
Watcher.prototype = {
get : function(){
Dep.target = this;
var value = this.getter?this.getter(this.vm):'';
Dep.target = null;
return value;
},
update :function(){
var newVal = this.get();
if(this.value != newVal){
this.cb && this.cb(newVal, this.value);
this.value = newVal;
}
}
};
(3)重载数据模型
Observer.prototype.transform = function(data){
for(var key in data){
this.defineReactive(data,key,data[key]);
}
};
Observer.prototype.defineReactive = function(data, key, value){
var dep = new Dep();
Object.defineProperty(data, key ,{
enumerable:true,
configurable:false,
get:function(){
if(Dep.target){
//添加观察者
dep.addSub(Dep.target);
}
return value;
},
set:function(newVal){
if(newVal == value){
return;
}
//data[key] = newVal;//死循环!赋值还会调用set方法
value = newVal;//为什么可以这样修改?闭包依赖的外部变量
//遍历newVal
this.transform(newVal);
//发送更新通知给观察者
dep.notify(newVal);
}
});
//递归处理
this.transform(value);
};
(4)观察者模式:将Compiler的解析结果,与Observer所观察的对象连接起来建立关系,在Observer观察到对象数据变化时,接收通知,同时更新DOM,称之为Watcher;
//观察者模式中的被观察者的核心部分
var Dep = function(){
this.subs = {};
};
Dep.prototype.addSub = function(target){
if(!this.subs[target.uid]) {
//防止重复添加
this.subs[target.uid] = target;
}
};
Dep.prototype.notify = function(newVal){
for(var uid in this.subs){
this.subs[uid].update(newVal);
}
};
Dep.target = null;
并未调用set和get方法
。Observer.prototype.transform = function(data){
for(var key in data){
this.defineReactive(data,key,data[key]);
}
};
编译试图主要创建Watcher观察者,以及绑定监听事件。
//创建Watcher在模型中监听视图中出现的占位符/表达式的每一个成员
var watcher = new Watcher("message"):
//绑定监听事件和method
node.addEventListener("click", "reverseMessage"):
在创建Watcher时,首先初始化exp占位符,vm,cb。然后调用this.getter = parseExpression(exp).get获取其要监听的占位符exp的一个成员对应的get方法
初始化给this.getter,然后调用this.update(),在该函数中会调用get方法,这里的get方法既是exp占位符的一个成员的get方法。在该get方法中dep.addSub(Dep.target),把该观察者watcher加入到新建的dep被观察者的sub列表中
。
//创建Watcher,观察者模式中的观察者
var Watcher = function(exp, vm, cb){
this.exp = exp; // 占位符/表达式的一个成员
this.cb = cb; //更新视图的回调函数
this.vm = vm; //ViewModel
this.value = null;
this.getter = parseExpression(exp).get;
this.update();
};
Watcher.prototype = {
get : function(){
Dep.target = this;
var value = this.getter?this.getter(this.vm):'';
Dep.target = null;
return value;
},
update :function(){
var newVal = this.get();
if(this.value != newVal){
this.cb && this.cb(newVal, this.value);
this.value = newVal;
}
}
};
当在如下method方法中修改message时,“=”会被拦截,触发对应模型transform的set方法,该set方法是一个闭包,在其中首先判断是否和原值相等,若不想等则更新value = newVal,在此使用此方法更新类似于直接操作内存
,因此不会像data[key]=newVal一样再次出发set方法形成循环递归。而此处的value有相当于C++里的指针或者引用,更新会同时修改传递过来的data[key]。
在set方法中会递归调用transform,因为newVal可能也包含value值需要重载其get和set方法。然后调用dep.notify(newVal)方法,便会遍历把更新通知发给对应的观察者watcher,再调用watcher中的update方法,执行val newVal=this.get()获取的newVal既是从模型中获取的变化的值,接着判断和this.value(view中原本的值)不相等,则调用回调函数更新视图。
methods: {
reverseMessage: function () {
this.message = this.message.split('').reverse().join('')
}
}
set:function(newVal){
if(newVal == value){
return;
}
//data[key] = newVal;
//死循环!赋值还会调用set方法
value = newVal;
//为什么可以这样修改?闭包依赖的外部变量
//遍历newVal
this.transform(newVal);
//发送更新通知给观察者
dep.notify(newVal);
}
}
//发送更新通知给观察者
Dep.prototype.notify = function(newVal){
for(var uid in this.subs){
this.subs[uid].update(newVal);
}
};
//观察者更新视图
update :function(){
var newVal = this.get();
if(this.value != newVal){
this.cb && this.cb(newVal, this.value);
this.value = newVal;
}
通过VSCODE这一软件案例,讲述了一个优秀的软件应该具备的特质和需要具备的前瞻性的架构决策和扎实的工程基础。
Git是一个开源的分布式版本控制系统,可以有效、高速地处理从很小到非常大的项目版本管理。此外也是管理Linux内核开发而开发的一个开放源码的版本控制软件。
//git本地版本库操作基本方法
3. git init # 初始化一个本地版本库
4. git status # 查看当前工作区(workspace)的状态
5. git add [FILES] # 把文件添加到暂存区(Index)
6. git commit -m "wrote a commit log infro” # 把暂存区里的文件提交到仓库
7. git log # 查看当前HEAD之前的提交记录,便于回到过去
8. git reset —hard HEAD^^/HEAD~100/commit-id/commit-id的头几个字符 # 回退
9. git reflog # 可以查看当前HEAD之后的提交记录,便于回到未来
10. git reset —hard commit-id/commit-id的头几个字符 # 回退
(2)git远程版本库的基本用法
11. git clone 克隆一个存储库到一个新的目录下。
12. git fetch 下载一个远程存储库数据对象等信息到本地存储库。
13. git push 将本地存储库的相关数据对象更新到远程存储库。
14. git merge 合并两个或多个开发历史记录。
15. git pull 从其他存储库或分支抓取并合并到当前存储库的当前分支。
(3)团队合作
1、克隆或同步最新的代码到本地存储库
git clone https://DOMAIN_NAME/YOUR_NAME/REPO_NAME.git
git pull
2、为自己的工作创建一个分支,该分支应该只负责单一功能模块或代码模块的版本控制;
git checkout -b mybranch
git branch
3、在该分支上完成某单一功能模块或代码模块的开发工作;多次进行如下操作:
git add FILES
git commit -m "commit log"
4、最后,先切换回master分支,将远程origin/master同步最新到本地存储库,再合并mybranch到master分支,推送到远程origin/master之后即完成了一项开发工作。
git checkout master
git pull
git merge --no-ff mybranch
git push
Vim是一个类似于Vi的著名的功能强大、高度可定制的文本编辑器,在Vi的基础上改进和增加了很多特性。讲述了vim的基本用法。其具备三种模式:命令模式,输入模式,底线命令模式。
正则表达式,又称规则表达式,是一种文本模式,包括普通字符和特殊字符,是计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串,通常被用来检索、替换那些符合某个模式(规则)的文本。
主要讲述了正则表达式的使用方法,以及贪婪匹配和懒惰匹配的区别,捕获组等知识。
本章主要通过menu实例来带领我们进行编程实战,通过实验来培养学生的软件工程实战能力。主要学习到了一些软件工程设计中的知识。
主要讲述了模块化(Modularity)是在软件系统设计时保持系统内各部分相对独立,以便每一个部分可以被独立地进行设计和开发。这个做法背后的基本原理是关注点的分离 (SoC, Separation of Concerns)。软件设计中的模块化程度便成为了软件设计有多好的一个重要指标,一般我们使用耦合度(Coupling)和内聚度(Cohesion)来衡量软件模块化的程度。
此外还有软件设计中的一些基本方法:KISS原则,使用本地化外部接口来提高代码的适应能力,先写伪代码的代码结构更好。
主要讲述了消费者和生产者重用,接口等相关知识。
(1)接口的五个基本要素
(2)通用接口定义基本方法
(3)RESTful API的四种基本操作
(4)可重入的函数不一定是线程安全的,可能是线程安全的也可能不是线程安全的;可重入的函数在多个线程中并发使用时是线程安全的,但不同的可重入函数(共享全局变量及静态变量)在多个线程中并发使用时会有线程安全问题;不可重入的函数一定不是线程安全的。
主要讲述了需求的类型,需求分析的方法,高质量需求的要求,什么是用例,敏捷统一过程等知识。
用例(Use Case)的核心概念中首先它是一个业务过程(business process),经过逻辑整理抽象出来的一个业务过程,这是用例的实质。
主要讲述了什么是软件,软件的基本构成,软件的基本结构,软件中的一些而特殊机制。此外重点讲述了设计模式相关的内容,以及常用的设计模式的优缺点。此外讲述了MVVM模式,观察者模式。软件结构的描述方法,软件质量的定义,提升软件性能的方法。
统一过程(UP,Unified Process)的核心要义是 用例驱动 (Use case driven)、 以架构为中心(Architecture centric)、 增量且迭代 (Incremental and Iterative)的过程。用例驱动就是我们前文中用例建模得到的用例作为驱动软件开发的目标;以架构为中心的架构是后续软件设计的结果,就是保持软件架构相对稳定,减小软件架构层面的重构造成的混乱;增量且迭代体现在下图中。
显然,第一到第三步主要是计划阶段的工作,第四步是接下来要进一步详述的增量阶段的工作。
在每一次增量阶段的迭代过程中,都要进行从需求分析到软件设计实现的过程,具体敏捷统一过程
将增量阶段分为五个步骤:
设计模式的本质是面向对象设计原则的实际运用总结出的经验模型。目的是包容变化,即通过使用设计模式和多态等特殊机制,将变化的部分和不变的部分进行适当隔离。(高内聚,低耦合)
主要区别在于,MVC中,用户对于M的操作是通过C传递的,然后C将改变传给V,并且M将在发生变化时通知V,然后V通过C获取变化;在MVVM中,用户直接与V交互,通过VM将变化传递给M,然后M改变之后通过VM将数据传递给V,从而实现解耦。
另一个区别是,当M的数据需要进行解析后V才能使用时,C若承担解析的任务,就会变得很臃肿;在MVVM中,VM层承担了数据解析的工作,这时C就只需要持有VM,而不需要直接持有M了。从而完成了数据的解耦。
“在10年内无法找到解决软件危机的杀手锏(银弹)。
软件中的根本困难,即软件概念结构(conceptual structure)的复杂性
,无法达成软件概念的完整性和一致性,自然无法从根本上解决软件危机带来的困境。
主要讲述了软件危机,软件的生命周期,软件过程,软件分阶段开发的方法,以及团队的重要性。
分析、设计、实现、交付和维护五个阶段:
软件过程又分为描述性
的(descriptive)过程和说明性
的(prescriptive)过程。
采用不同的过程模型时应该能反映出要达到的过程目标,比如构建高质量软件、早发现缺陷、满足预算和日程约束等。不同的模型适用于不同的情况,我们常见的过程模型,比如瀑布模型、 V模型 、原型化模型等都有它们所能达到的过程目标和适用的情况。
V模型也是在瀑布模型基础上发展出来的,我们发现单元测试、集成测试和系统测试是为了在不同层面验证设计,而交付测试则是确认需求是否得到满足。 也就是瀑布模型中前后两端的过程活动具有内在的紧密联系,如果将模块化设计的思想拿到软件开发过程活动的组织中来, 可以发现通过将瀑布模型前后两端的过程活动结合起来,可以提高过程活动的内聚度,从而改善软件开发效率。 这就是V模型。V模型是开始一个特定过程活动和评估该特定过程的过程活动成对出现,从而便于软件开发过程的
组织和管理。
分阶段开发的交付策略分为两种,一是增量开发
(Incremental development),二是迭代开发
(Iterative development)。
CMM/CMMI用于评价软件生产能力并帮助其改善软件质量的方法,成为了评估软件能力与成熟度的
一套标准,它侧重于软件开发过程的管理及工程能力的提高与评估,是国际软件业的质量管理标
准。
CMMI共有5个级别,代表软件团队能力成熟度的5个等级,数字越大,成熟度越高,高成熟度等级
表示有比较强的软件综合开发能力。
初始级
,软件组织对项目的目标与要做的努力很清晰,项目的目标可以实现。但主要取决于实施人员。管理级
,软件组织在项目实施上能够遵守既定的计划与流程,有资源准备,权责到人,对项目相关的实施人员进行了相应的培训,对整个流程进行监测与控制,并联合上级单位对项目与流程进行审查。这级能保证项目的成功率。已定义级
,软件组织能够根据自身的特殊情况及自己的标准流程,将这套管理体系与流程予以制度化。科学管理成为软件组织的文化与财富。量化管理级
,软件组织的项目管理实现了数字化,降低了项目实施在质量上的波动。持续优化级
,软件组织能够充分利用信息资料,对软件组织在项目实施的过程中可能出现的问题予以预防。能够主动地改善流程,运用新技术,实现流程的优化。软件开发、技术运营和质量保障(QA)
部门之间的沟通、协作与整合。它的出现是由于软件行业日益清晰地认识到:为了按时交付软件产品和服务,开发和运营工作必须紧密合作。代码中的软件工程https://gitee.com/mengning997/se