本期我们会引入许多关于软件工程的一些专业术语,以及对于软件制作过程的大致流程,以及制作软件的一些原则和所需要考虑的问题让你感觉到软件工程的魅力!!!
一.软件
软件的定义 软件=程序+数据+文档
软件危机的定义:在计算机软件开发和维护过程中所遇到的一系列严重问题 特点:周期长、成本高、质量差、维护难
软件生存周期:计算机系统工程、需求分析、设计、编码、测试、运行、维护
1.1软件过程模型
首先我们要了解软件原型这个概念,即在迅速了解需求分析构造出一个可运行的程序,称之为软件原型,之后我们会在按照一些软件模型过程,在软件原型的基础上直到做出最终软件产品的过程就是软件过程模型。
瀑布模型
特征
1.接受上一阶段的结果作为本阶段的输入。
2.利用这一输入实施本阶段应完成的活动。
3.对本阶段的工作进行评审。
4.将本阶段的结果作为输出,传递给下一阶段
缺点:
- 灵活性差难以满足对于需求经常变化以及需求不明确的软件开放
- 早期开放出现的问题往往在交付阶段才发现,维护成本大
软件演化模型:
1.2 软件演化模型
对于需求分析,构造出原型之后,会将软件原型交付客户体验然后给出意见或者新的需求,在重新迭代之前过程直至演化最终软件产品 这是软件演化过程。
增量模型
特征:1.每一个增量都使用了瀑布模型的成分(重复使用),结合了演化过程的迭代,强调每一个线性过程的结果都是一个可运行的程序交付客户使用反馈新的意见不断发布新的增量版本。
- 特别适用于需求不断变化的软件开放,以及具有一定的风险可控能力,比如可以避免早期增量版本使用不成熟的技术。----在这里可以在每个迭代中可以引入和测试新技术。
图中解读:每个线性过程包括: 交流得知需求 计划确定方案 交流确定方案的可行性发现风险消除风险,按照需求建模 分析设计 进一步 开放 编码测试 最后 发布运行 发布增量版本
喷泉模型
特征:1.这是一个面向对象开放软件的过程,以对象需求为动力,迭代内部过程不断完善软件扩充对象需求。 2.各个过程之间都是没有明显的缝隙的,可以说非常适用于面向对象开放多个过程都有重叠的软件开发,这可以节约软件开放过程和时间
缺点: 1.多个过程的重叠需要大量的开放人员 2.各个过程没有明确的间隙不利于软件项目的管理 3. 喷泉模型要求严格管理文档,审核难度会加大。(每个过程会产生大量文档 测试用例 设计文档)
螺旋模型
这里要了解四个象限概念: 1. 计划制定 2. 风险分析 3. 工程实施 4. 客户反馈
螺旋模型就是在这四个详细迭代 像一个螺线一样不断完善软件产品演化直到最终产品的过程。
1. 计划制定: 得知需求 然后快速分析需求设计制定方案 2. 评估方案风险选定方案消除风险 3.实施工程 : 实施制定的软件计划编码测试运行 4. 交付客户体验 得到反馈 迭代上面过程
第二部分:系统工程
其实这里的系统指的是:使得计算机处理信息完成已定义目标的元素集合/
: 软件硬件 文档数据库 人员 规程
第三部分 需求工程
这一过程是很重要的,避免返工,提高用户满意度,减少后期成本等等,简单来说这一步是确定软件要做什么的过程。
具体来说需求工程分为下面: 需求获取,需求分析 ,需求规格说明,需求验证,需求管理
需求规格说明:将需求进行转化问文档的形式 这个过程要有严格的格式要求
需求验证: 这一部分要求数据的一致性,准确性,使得后面测试用例的完备
需求管理:这是软件生命周期的一部分,设计需求的变动.
第四部分 设计工程
1. 过程抽象
过程抽象关注的是对计算或操作的封装。它使得开发者可以将复杂的程序逻辑封装在一个函数或方法中,只暴露出其接口(即函数名称、参数和返回值),而隐藏实现细节。这样,用户可以通过调用这个函数来执行某个操作,而不需要了解该操作的内部具体实现。这种方式有助于减少代码的复杂性,提高代码重用性。
举例:
def calculate_area(radius){
return 3.14 * radius * radius }
在这个例子中,calculate_area 函数封装了计算圆面积的具体实现,用户只需使用这个函数,而不需要知道计算过程的具体细节。
2. 数据抽象
数据抽象则关注于数据结构及其操作的封装。它通过定义抽象数据类型(ADT)来描述数据的结构和相关操作,而不关心这些数据的具体实现方式。这使得开发者能够使用这些抽象的数据结构,而不必关心其内部实现,从而提高了程序的可维护性和灵活性。
举例:
在这个例子中,定义了一个链表的抽象数据类型,并提供了基本的操作(如 push 和 pop)。用户可以使用这些操作而无需关心 内部是如何实现的。
2.逐步求精
逐步求精(Refinement)是一种常用的软件开发和问题求解技术,它强调将一个复杂的问题分解为更小、更简单的子问题,通过逐步细化来最终得到解决方案。这种方法提供了一种层次化的思维方式,可以帮助开发者更系统、更清晰地解决问题。
#include <stdio.h>
// 子问题 1:计算和 int calculate_sum(int numbers[], int count) {
int total = 0;
for (int i = 0; i < count; i++) {
total += numbers[i];
}
return total;
}
// 子问题 2:计算长度 int calculate_length(int numbers[]) {
// 返回长度,这里示范使用 -1 作为结束标志
int length = 0;
while (numbers[length] != -1) {
length++;
}
return length;
}
// 子问题 3:计算平均数 double calculate_average(int numbers[]) {
int count = calculate_length(numbers);
if (count == 0) {
return 0; // 防止除以零
}
int total_sum = calculate_sum(numbers, count);
return (double)total_sum / count;
}
// 主函数 int main() {
// 示例数据,使用 -1 来表示结束
int nums[] = {1, 2, 3, 4, 5, -1}; // -1 不被计算在内
double average = calculate_average(nums);
printf("Average: %.2f\n", average); // 输出平均数
return 0;
}
简单来说就是将复杂的问题简化成很多简单的小问题,实际上多个简单问题的花费和比复杂问题的花费低,但是考虑到接口的花费的花不能将问题无限分化下去。
3.信息隐蔽
这里信息隐蔽针对是是软件内部各个功能板块之间关系。可以做到在调用某一个功能时候而不会调用没必要使用的信息。
以及其他部分的功能与其他独立的板块功能他们之间是隐蔽关系。
- 功能独立性
内聚:是指板块内部或者类内部相关性和协作性程度。内聚程度高说明功能越单一越专注,内部板块之间的联系越强。
耦合:是指各个板块之间的联系程度,耦合性越低则说明各个板块之间的独立性强,可重用,可维护性强。
第五部分 面向对象方法
在C语言中,虽然没有直接支持面向对象的特性,但我们可以使用结构体(struct)、函数指针和一些设计模式来实现类似类和对象的行为。以下是对文章中的每个概念进行补充的C语言示例代码。
5.1 类与对象
类(Class) 是面向对象编程的基本构建块,它是对现实世界中某一类事物的抽象描述。类定义了对象的属性(数据)和方法(行为)。
对象(Object) 是类的实例,代表类的具体实体。每个对象都有独立的状态和行为,可以通过对象访问和修改类中定义的属性和方法。
例如,考虑一个“汽车”类,可以定义属性如颜色、品牌和速度,以及方法如加速、刹车等。然后,我们可以创建“我的车”这个对象,具有特定的颜色、品牌和速度。
示例
#include <stdio.h>
// 定义Car结构体 typedef struct {
char brand[50];
char color[20];
int speed;
} Car;
// 方法:加速 void accelerate(Car* car, int amount) {
car->speed += amount;
printf("%s is now going at %d km/h.\n", car->brand, car->speed);
}
// 方法:刹车 void brake(Car* car) {
car->speed = 0;
printf("%s has stopped.\n", car->brand);
}
int main() {
// 创建一个Car对象
Car myCar = { "Toyota", "red", 0 };
accelerate(&myCar, 50); // 输出:Toyota is now going at 50 km/h.
brake(&myCar); // 输出:Toyota has stopped.
return 0;
}
5.2 封装
在C语言中,我们可以通过将结构体的定义放在源文件而不是头文件中来实现一定程度的封装。同时,使用函数来访问和修改结构体的内部状态。
示例:
#include <stdio.h> #include <string.h>
// 定义Car结构体 typedef struct {
char brand[50];
char color[20];
int speed;
} Car;
// 函数声明 void setColor(Car* car, const char* color); const char* getColor(const Car* car);
void setColor(Car* car, const char* color) {
strncpy(car->color, color, sizeof(car->color));
}
const char* getColor(const Car* car) {
return car->color;
}
int main() {
Car myCar = { "Toyota", "", 0 };
setColor(&myCar, "red");
printf("The car color is: %s\n", getColor(&myCar)); // 输出:The car color is: red
return 0;
}
5.3 继承
虽然C语言没有继承的直接概念,但我们可以通过包含结构体来模拟继承。
示例:
#include <stdio.h>
// 定义交通工具结构体(父类) typedef struct {
char type[20];
} Vehicle;
// 定义汽车结构体(子类) typedef struct {
Vehicle vehicle; // 包含Vehicle结构体
int number_of_doors;
} Car;
void start(const Vehicle* vehicle) {
printf("Starting %s.\n", vehicle->type);
}
int main() {
Car myCar = { {"Car"}, 4 };
start((Vehicle*)&myCar); // 输出:Starting Car.
return 0;
}
5.4 多态
可以使用函数指针来模拟多态,多态的核心:允许同一接口通过不同实现调用。
示例:
#include <stdio.h>
// 定义动物类型 typedef struct Animal {
void (*speak)(void);
} Animal;
// 定义狗 void dogSpeak() {
printf("Woof!\n");
}
// 定义猫 void catSpeak() {
printf("Meow!\n");
}
int main() {
Animal dog = { dogSpeak };
Animal cat = { catSpeak };
dog.speak(); // 输出:Woof!
cat.speak(); // 输出:Meow!
return 0;
}
结论
尽管C语言不具备面向对象编程的直接特性,通过结构体和函数指针,我们仍然能够实现类似的概念。理解这些基本概念可以帮助程序员在C语言中构建结构化、可维护和可扩展的程序。
继承--c++
在 C++ 中,继承是一种面向对象编程(OOP)的重要特性,它允许一个类(称为派生类或子类)从另一个类(称为基类或父类)继承属性和方法。这种机制使得代码可以被重用,并且支持层次化的数据组织。
结尾
本期我们基本了解了一个软件的制作过程以及后期的运行维护的概念,大致感受到软件工程到底是在干嘛。深入了解这些概念是为了让你后期学习更加有规划,心中有谱,进而选择一个专攻的方向比如到底是编码开放过程你更下尝试还是后期的测试过程你更兴趣等等,每一个部分都会有对应的岗位不同的工作,软件工程在这里体现出足够的工作协调性合作性,以及对于软件需求不断的变化对软件制作也需要不断的改变这时软件工程中迭代的思想在其中体现非常明显,让你感觉到每一次迭代所出现的新产品一次次变得更好解决最终软件目标这就是软件工程中一系列思想的魅力,拔高我们的逻辑思维,合作能力,整体观,以及要有不断接受改变的心态,还有面对诸多工具的改变活到老学到老的终身理念。