第9章模块化设计.ppt
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Report函数报告输赢结果,并记录输赢的次数。因此 它必须有四个参数:输赢结果,更新输的次数、赢的次 数和平局的次数,但没有返回值。
prn_game_status函数报告至今为止的战况,因此需 要三个参数:输的次数、赢的次数和平的次数,但没有 返回值。
模块划分
print模块的进一步考虑
输的次数、赢的次数和平局的次数在Report和prn_game_status两 个函数中都出现。
Report函数修改这些变量的值,prn_game_status函数显示这些变 量的值。
这三个函数的原型和用户期望的原型不一致,用户不希望原型中有参 数。
输的次数、赢的次数和平局的次数和其他模块的函数无任何关系,因 此可作为该模块的内部状态。内部状态可以作为该模块的全局变量
这样report和prn_game_status函数中都不需要这三个参数了。
第9章 模块化开发
自顶向下的分解 模块划分 库的设计与实现 库的应用
自顶向下的分解 模块划分 库的设计与实现 库的应用
自顶向下的分解
模块化是指解决一个复杂问题时自顶向下逐层 把软件系统划分成若干模块的过程,由此分解 来降低复杂性。
自顶向下的分解
例子:猜硬币的游戏
功能:
枚举成员
此时,aa与ww的允许取值范围为各自类型定义好的枚举成员
}Байду номын сангаас
模块划分
Select模块的设计
selection_by_player从键盘接收用户的输入并返回此 输入值。因此,原型为 p_r_s selection_by_player();
selection_by_machine函数由机器产生一个石头、剪 子、布的值,并返回。因此,原型为 p_r_s selection_by_machine();
ovouitdcopmrne_ppch__oerrml__ppss(a)ss;reeell/ee(cc/pttc_iiooro_nnms__,bbppyy___arpm_rlsaae)cy;.hecrinp(ep());;
void void
Report(outcoomutec)o; me
//print.cpp
函数抽取
获取用户输入selection_by_player 获取机器输入selection_by_machine 评判结果compare 报告结果并记录结果信息report 显示目前战况prn_game_status 显示帮助信息prn_help六个函数
分成四个模块:
– 主模块: main函数 – 获取选择的模块:
自顶向下的分解
void play() { int coin ; char flag = 'Y'; srand(time(NULL)); //设置随机数种子 while (flag == 'Y' || flag =='y')
{ coin = rand() * 2 / (RAND_MAX+1); //生成扔硬币的结果
自顶向下的分解
问题的分解过程
main( ) { 显示游戏介绍;
玩游戏;}
int main() { prn_instruction(); play(); return 0; }
rand() int get_call_from_user()
自顶向下的分解 模块划分 库的设计与实现 库的应用
compare(p_r_s,
p_r_s);
prn_g#ainmcleu_dstea<tuioss(t)r;eaovmuo>itdcopmrne_choemlpp(a);re(p_r_s,
p_r_s);
#uisninclgundaem<##ueisiiospnnisnacctcrllguueenddasatmee#dumi<<;>snieccncsstigptlmduanlcediabe>emvv>soo<tediisddip;opRascrteenrp_esotgardatm;(mo>ue_tcsotamtues);();
if (get_call_from_user() == coin) cout << "你赢了";
else cout << "我赢了"; cout << "\n继续玩吗(Y或y)?"; cin >> flag; } }
自顶向下的分解 get_call_from_user的实现
该函数接收用户输入的一个整型数。如果输 入的数不是0或1,则重新输入,否则返回输 入的值
把程序再分成几个小的源文件。每个源文件都包含一组 相关的函数。一个源文件被称为一个模块。
模块划分标准:块内联系尽可能大,块间联系尽可能小
模块划分
石头、剪刀、布游戏
游戏规则
– 布覆盖石头 – 石头砸坏剪刀 – 剪刀剪碎布
游戏的过程为: 游戏者选择出石头、剪子或布; 计算机随机选择一个; 根据规则,输出结果; 继续游戏,直到游戏者选择结束为止。 在此过程中,游戏者可以随时阅读游戏指南或查看当前战况。
模块划分
重新认识四个模块:
– 主模块: main函数
main.cpp
– 获取选择的模块:
p_r_s selection_by_player(); select.cpp p_r_s selection_by_machine();
– 比较模块:
compare.cpp
outcome compare(p_r_s, p_r_s);
自顶向下的分解
play函数的实现
Play函数随机产生正反面,让用户猜,报告对错结果,然后询 问是否要继续玩
即,计算机随机给出一个结果,用户输入一个结果,两者比较
void play() { char flag = ‘y’; //确定用户是否需要继续玩
while (flag == ‘Y’ || flag == ‘y’) { coin = 生成正反面; //计算机随机产生正反面
第一层的分解
模块划分
本程序需要实现的:根据用户输入的要求,实现不 同的操作。
分析一下,用户有可能有多少种可能的输入要求? ∮输入石头剪刀布 —— 继续玩 ∮输入阅读游戏指南 —— 显示游戏指南 ∮输入查看当前状况 —— 显示游戏战况 ∮输入结束游戏 —— 不完了,最后显示游戏战况
第一层的分解 的伪代码表示
自顶向下的分解
int main() {
prn_instruction(); play();
return 0; }
自顶向下的分解
prn_instruction的实现
prn_instruction函数的实现非常简单,只要一 系列的输出语句把程序指南显示一下就可以了
void prn_instruction() { cout << "这是一个猜硬币正反面的游戏.\n"; cout << "我会扔一个硬币,你来猜.\n"; cout << "如果猜对了,你赢,否则我赢。\n"; }
– 输出模块:
void prn_help(); print.cpp
void Report(outcome);
void prn_game_status ();
怎样解决多模块的函数声明和预处理命令
//main.cpp
p_r_s selection_by_player();
p_r_s sel/ec/tsioenl_ebcyt_.mcapcphine();
模块划分
Compare模块的设计
compare函数比较用户输入的值和机器产生的值, 确定输赢。
它要有两个参数,都是p_r_s类型的,它也应该有 一个返回值,就是判断的结果 。
原型为: outcome compare(p_r_s, p_r_s);
模块划分
print模块的设计
prn_help显示一个用户输入的指南,告诉用户如何输 入他的选择。因此,它没有参数也没有返回值。
– 提供游戏指南; – 计算机随机产生正反面,让用户猜,报告对错结果。
重复此过程,直到用户不想玩了为止。
顶层分解
自顶向下的分解
程序要做两件事:显示程序指南;模拟玩游 戏的过程。
main( ) {
显示游戏介绍; 玩游戏; }
主程序的两个步骤是相互独 立的,没有什么联系,因此 可设计成两个独立的函数: void prn_instruction(); void play();
#include <iostream>
using namespace std;
头文件的设计
模块划分
为方便起见,我们把所有的符号常量定义、类型定义和函数 原型声明写在一个头文件中,让每个模块都include这个头 文件。那么,每个模块就不必要再写那些函数的原型声明了
但这样做又会引起另一个问题,当把这些模块连接起来时, 编译器会发现这些类型定义、符号常量和函数原型的声明在 程序中反复出现多次
selection_by_player selection_by_machine – 比较模块: compare – 输出模块: report、 prn_game_status prn_help函数
模块划分
模块划分
枚举类型的定义
为了提高程序的可读性,我们定义两个枚举类型 :
enum p_r_s {paper, rock, scissor, game, help, quit} ;
int get_call_from_user() {int guess; // 0 = head, 1 = tail
do { cout << "\n输入你的选择(0表示正面,1表示反面):"; cin >> guess;
} while (guess !=0 && guess !=1) ; return guess; }
While (用户输入 != quit) {switch(用户的选择) {case paper, rock, scissor: 机器选择; 评判结果; 报告结果; case game: 显示目前的战况; case help: 显示帮助信息; default: 报告错误; }
显示战况;
模块划分
模块划分
下面的三个文件同属于一个工程文件,指出下面程序的执行结果
//cpp1.cpp
static int n; void func2( );
void func1( ) { n++;
func2( ); cout<<n<<endl;}
//cpp2.cpp
extern int n; void func2( ) { n++;
cout<<n<<endl;}
//cpp3.cpp
#include <iostream>
int n;
void func1( );
10
11
void main( )
1
{ n=10;
cout<<n<<endl;
func1( );}
模块划分
当程序变得更多更长更复杂的时候,要在一个单独的源 文件中处理众多的函数会变得困难
enum outcome {win, lose, tie, error} ;
枚定例举的如声 数上面明据定用片义于断了声,而两明个且新新全的的部类类都型型以p,_类r_能型s和够安o允u全tc许的om用e形。常式量来来表表示示。特
接着我们可以用这两个类型去定义变量,如:
p_r_访s a问a;修辞符 enum 枚举名 outc{ome ww;
输入用户的猜测; if (用户猜测 == coin)
报告本次猜测结果正确; else 报告本次猜测结果错误; cin>>flag; } }
play函数的细化
自顶向下的分解
生成正反面:如果用0表示正面,1表示反面,那么生成 正反面就是随机生成0和1两个数
输入用户的猜测。如果不考虑程序的鲁棒性,这个问题 直接用一个输入语句即可。但想让程序做得好一点,就 必须考虑得全面一些。比如,用户可以不守规则,既不 输入0也不输入1,而是输入一个其他值,程序该怎么办? 因此这个任务还可以进一步细化,所以再把它抽象成一 个函数get_call_from_user。
解决方法:需要用到一个新的编译预处理命令: #ifndef 标识符 … #endif
说明模块划分还是有道理的,这 几个函数之间联系紧密 (需要使用共同的参数)
模块划分
三个打印函数的原型:
void prn_help(); 没有参数也没有返回值 void Report(outcome); 1个参数,没有返回值 void prn_game_status (); 没有参数也没有返回值
prn_game_status函数报告至今为止的战况,因此需 要三个参数:输的次数、赢的次数和平的次数,但没有 返回值。
模块划分
print模块的进一步考虑
输的次数、赢的次数和平局的次数在Report和prn_game_status两 个函数中都出现。
Report函数修改这些变量的值,prn_game_status函数显示这些变 量的值。
这三个函数的原型和用户期望的原型不一致,用户不希望原型中有参 数。
输的次数、赢的次数和平局的次数和其他模块的函数无任何关系,因 此可作为该模块的内部状态。内部状态可以作为该模块的全局变量
这样report和prn_game_status函数中都不需要这三个参数了。
第9章 模块化开发
自顶向下的分解 模块划分 库的设计与实现 库的应用
自顶向下的分解 模块划分 库的设计与实现 库的应用
自顶向下的分解
模块化是指解决一个复杂问题时自顶向下逐层 把软件系统划分成若干模块的过程,由此分解 来降低复杂性。
自顶向下的分解
例子:猜硬币的游戏
功能:
枚举成员
此时,aa与ww的允许取值范围为各自类型定义好的枚举成员
}Байду номын сангаас
模块划分
Select模块的设计
selection_by_player从键盘接收用户的输入并返回此 输入值。因此,原型为 p_r_s selection_by_player();
selection_by_machine函数由机器产生一个石头、剪 子、布的值,并返回。因此,原型为 p_r_s selection_by_machine();
ovouitdcopmrne_ppch__oerrml__ppss(a)ss;reeell/ee(cc/pttc_iiooro_nnms__,bbppyy___arpm_rlsaae)cy;.hecrinp(ep());;
void void
Report(outcoomutec)o; me
//print.cpp
函数抽取
获取用户输入selection_by_player 获取机器输入selection_by_machine 评判结果compare 报告结果并记录结果信息report 显示目前战况prn_game_status 显示帮助信息prn_help六个函数
分成四个模块:
– 主模块: main函数 – 获取选择的模块:
自顶向下的分解
void play() { int coin ; char flag = 'Y'; srand(time(NULL)); //设置随机数种子 while (flag == 'Y' || flag =='y')
{ coin = rand() * 2 / (RAND_MAX+1); //生成扔硬币的结果
自顶向下的分解
问题的分解过程
main( ) { 显示游戏介绍;
玩游戏;}
int main() { prn_instruction(); play(); return 0; }
rand() int get_call_from_user()
自顶向下的分解 模块划分 库的设计与实现 库的应用
compare(p_r_s,
p_r_s);
prn_g#ainmcleu_dstea<tuioss(t)r;eaovmuo>itdcopmrne_choemlpp(a);re(p_r_s,
p_r_s);
#uisninclgundaem<##ueisiiospnnisnacctcrllguueenddasatmee#dumi<<;>snieccncsstigptlmduanlcediabe>emvv>soo<tediisddip;opRascrteenrp_esotgardatm;(mo>ue_tcsotamtues);();
if (get_call_from_user() == coin) cout << "你赢了";
else cout << "我赢了"; cout << "\n继续玩吗(Y或y)?"; cin >> flag; } }
自顶向下的分解 get_call_from_user的实现
该函数接收用户输入的一个整型数。如果输 入的数不是0或1,则重新输入,否则返回输 入的值
把程序再分成几个小的源文件。每个源文件都包含一组 相关的函数。一个源文件被称为一个模块。
模块划分标准:块内联系尽可能大,块间联系尽可能小
模块划分
石头、剪刀、布游戏
游戏规则
– 布覆盖石头 – 石头砸坏剪刀 – 剪刀剪碎布
游戏的过程为: 游戏者选择出石头、剪子或布; 计算机随机选择一个; 根据规则,输出结果; 继续游戏,直到游戏者选择结束为止。 在此过程中,游戏者可以随时阅读游戏指南或查看当前战况。
模块划分
重新认识四个模块:
– 主模块: main函数
main.cpp
– 获取选择的模块:
p_r_s selection_by_player(); select.cpp p_r_s selection_by_machine();
– 比较模块:
compare.cpp
outcome compare(p_r_s, p_r_s);
自顶向下的分解
play函数的实现
Play函数随机产生正反面,让用户猜,报告对错结果,然后询 问是否要继续玩
即,计算机随机给出一个结果,用户输入一个结果,两者比较
void play() { char flag = ‘y’; //确定用户是否需要继续玩
while (flag == ‘Y’ || flag == ‘y’) { coin = 生成正反面; //计算机随机产生正反面
第一层的分解
模块划分
本程序需要实现的:根据用户输入的要求,实现不 同的操作。
分析一下,用户有可能有多少种可能的输入要求? ∮输入石头剪刀布 —— 继续玩 ∮输入阅读游戏指南 —— 显示游戏指南 ∮输入查看当前状况 —— 显示游戏战况 ∮输入结束游戏 —— 不完了,最后显示游戏战况
第一层的分解 的伪代码表示
自顶向下的分解
int main() {
prn_instruction(); play();
return 0; }
自顶向下的分解
prn_instruction的实现
prn_instruction函数的实现非常简单,只要一 系列的输出语句把程序指南显示一下就可以了
void prn_instruction() { cout << "这是一个猜硬币正反面的游戏.\n"; cout << "我会扔一个硬币,你来猜.\n"; cout << "如果猜对了,你赢,否则我赢。\n"; }
– 输出模块:
void prn_help(); print.cpp
void Report(outcome);
void prn_game_status ();
怎样解决多模块的函数声明和预处理命令
//main.cpp
p_r_s selection_by_player();
p_r_s sel/ec/tsioenl_ebcyt_.mcapcphine();
模块划分
Compare模块的设计
compare函数比较用户输入的值和机器产生的值, 确定输赢。
它要有两个参数,都是p_r_s类型的,它也应该有 一个返回值,就是判断的结果 。
原型为: outcome compare(p_r_s, p_r_s);
模块划分
print模块的设计
prn_help显示一个用户输入的指南,告诉用户如何输 入他的选择。因此,它没有参数也没有返回值。
– 提供游戏指南; – 计算机随机产生正反面,让用户猜,报告对错结果。
重复此过程,直到用户不想玩了为止。
顶层分解
自顶向下的分解
程序要做两件事:显示程序指南;模拟玩游 戏的过程。
main( ) {
显示游戏介绍; 玩游戏; }
主程序的两个步骤是相互独 立的,没有什么联系,因此 可设计成两个独立的函数: void prn_instruction(); void play();
#include <iostream>
using namespace std;
头文件的设计
模块划分
为方便起见,我们把所有的符号常量定义、类型定义和函数 原型声明写在一个头文件中,让每个模块都include这个头 文件。那么,每个模块就不必要再写那些函数的原型声明了
但这样做又会引起另一个问题,当把这些模块连接起来时, 编译器会发现这些类型定义、符号常量和函数原型的声明在 程序中反复出现多次
selection_by_player selection_by_machine – 比较模块: compare – 输出模块: report、 prn_game_status prn_help函数
模块划分
模块划分
枚举类型的定义
为了提高程序的可读性,我们定义两个枚举类型 :
enum p_r_s {paper, rock, scissor, game, help, quit} ;
int get_call_from_user() {int guess; // 0 = head, 1 = tail
do { cout << "\n输入你的选择(0表示正面,1表示反面):"; cin >> guess;
} while (guess !=0 && guess !=1) ; return guess; }
While (用户输入 != quit) {switch(用户的选择) {case paper, rock, scissor: 机器选择; 评判结果; 报告结果; case game: 显示目前的战况; case help: 显示帮助信息; default: 报告错误; }
显示战况;
模块划分
模块划分
下面的三个文件同属于一个工程文件,指出下面程序的执行结果
//cpp1.cpp
static int n; void func2( );
void func1( ) { n++;
func2( ); cout<<n<<endl;}
//cpp2.cpp
extern int n; void func2( ) { n++;
cout<<n<<endl;}
//cpp3.cpp
#include <iostream>
int n;
void func1( );
10
11
void main( )
1
{ n=10;
cout<<n<<endl;
func1( );}
模块划分
当程序变得更多更长更复杂的时候,要在一个单独的源 文件中处理众多的函数会变得困难
enum outcome {win, lose, tie, error} ;
枚定例举的如声 数上面明据定用片义于断了声,而两明个且新新全的的部类类都型型以p,_类r_能型s和够安o允u全tc许的om用e形。常式量来来表表示示。特
接着我们可以用这两个类型去定义变量,如:
p_r_访s a问a;修辞符 enum 枚举名 outc{ome ww;
输入用户的猜测; if (用户猜测 == coin)
报告本次猜测结果正确; else 报告本次猜测结果错误; cin>>flag; } }
play函数的细化
自顶向下的分解
生成正反面:如果用0表示正面,1表示反面,那么生成 正反面就是随机生成0和1两个数
输入用户的猜测。如果不考虑程序的鲁棒性,这个问题 直接用一个输入语句即可。但想让程序做得好一点,就 必须考虑得全面一些。比如,用户可以不守规则,既不 输入0也不输入1,而是输入一个其他值,程序该怎么办? 因此这个任务还可以进一步细化,所以再把它抽象成一 个函数get_call_from_user。
解决方法:需要用到一个新的编译预处理命令: #ifndef 标识符 … #endif
说明模块划分还是有道理的,这 几个函数之间联系紧密 (需要使用共同的参数)
模块划分
三个打印函数的原型:
void prn_help(); 没有参数也没有返回值 void Report(outcome); 1个参数,没有返回值 void prn_game_status (); 没有参数也没有返回值