1.10a – 如何设计你的第一个程序

from http://www.learncpp.com/cpp-tutorial/1-10a-how-to-design-your-first-programs/

将原文中的建议提到前面来讲了:

//begin advise

编写程序时的几个建议

保持你的程序有一个简单的开始新手通常对于它们想要编程实现的内容有很大的野心。“我想要写一个角色扮演的游戏,有图像,有声音,随机的怪物和地下城,有一个城镇,你能够访问并卖掉从地下城获取的东西”,如果你从一开始便尝试写一个复杂的东西,是将会被严重的打击到,从而失去信心。相反,将你的第一个目标设置成足够的简单,一些你能够完成的任务。举个例子,“我想要在屏幕显示2d表征世界的内容”。

 

随着时间的推进,添加新的内容。当你实现你的简单的程序,并且能够很好的运行的时候,你应该添加新的特征。举个例子,一旦你实现显示一个2d世界,添加一个能够走动的特征。当你能够走动的时候,添加墙壁能够阻止你前进。但你有了墙后,建立一个简单的城镇。当你建立了一个简单的城镇后,添加商人。通过不断向你的程序中添加新的特征,能够逐渐使你的程序具有的功能越来越多,并且不会打击你的信心。

 

一次注意一个方面。不要尝试一次性编写很多东西,不要将你的注意力分散到繁杂的任务上。一次关注一个任务,尽可能充分的完成它。一个完整的工作任务的完成相比于6个部分的工作任务的开始要好很多。如果你分散了你的注意力,你很有可能犯错误,忘记一些重要的细节。

 

测试你的每一部分代码。新手通常一次性编写完整的程序。然后,他们开始进行第一次编译,编译器产生出成百上千的错误。这不是用来吓唬人的,如果你的代码没有工作,很难够指出具体问题在什么地方。相反,写一部分代码,然后立刻编译测试它。如果它不能工作,你会确切的知道具体问题出在什么地方,然后修复它。一但确定你的代码能够正确运行时,开始编写下一段,重复上述步骤。它也许会花费你很长时间完成你的代码,但是,当你完成你的代码时,整个程序应该是能够执行的,你也没有必要花费更长的时间试图去指出它为什么会犯错。

 

大多数新手会快速略过这些步骤和建议,因为它看上去花费了很多工作,并且也没有像写代码那么有趣。但是,对于任何一个重大的项目,遵从这些步骤,将会节约你很大一部分时间。开始一点的计划能够节省后来很多的调试。

在你适应这些概念之时,他们会自然而然的出现。最后,你能够在没有实现计划下写出所有函数。

} // end advise

 

 

 

现在,你已经学习了关于编程的基本内容,让我们更进一步学习如何设计一个程序吧。当你坐着编程的时候,通常你会有一些不同的问题需要解决,或一些状况你想要模拟。初学者在将思想转变成实际的代码上通常会有困难。但是事实上呢,你有很多从日常生活中获取的解决问题的技能。

需要记住的最重要的事情是在你开始编写代码之前设计你的程序。编程就好比建筑学。没有一个良好的建造计划,你便开始建造房子,那么将会发生什么情况呢?事实是这样的,除非你是天才,你建造的房子总会有一大堆的问题:屋顶漏水,墙壁不直等等。类似的来说,在没有很好的计划之前便开始编写你的程序,你将会发现你的代码具有很多的问题,因此你不得不花费大量的时间去修正这些通过一些设计本来能够避免的问题。

简单的预先计划将会减少你时间和遇到问题时的挫败感。

 

1. 定义一个问题

你需要指出的第一件事情是确切地明白你的程序是用来解决什么问题。理想状态上,你应该用一两句话进行陈述。举个例子:

 

  • * 我想要写一个电话本的应用程序帮助我记录我的朋友电话号码。
  • * 我想要写一个随机空洞生成器,能够产生看起来有趣的洞穴(这一句可能翻译的有问题,原句为:I want to write a random dungeon generator that will produce interesting looking caverns.)
  • * 我想要写一个程序,能够利用股票的信息,尝试着去预测我可以选那一股。

尽管,这一步看上去好像是很明显的,但是它还是很重要的。你能够遇到的最糟糕的事情就是你写的程序并没有实现你想要实现的功能。

 

2. 定义你的目标

当你是一个有经验的程序员的时候,这一步能够被分解为很多细小的步骤,包含:

  • * 明白你的目标用户是谁
  • * 定义你的程序将在哪种目标架构或操作系统上运行
  • * 决定哪些工具你将会使用到
  • * 决定你将一个人还是一个团体编写程序
  • * 收集需求(一个具有一些列程序将做什么的文档)

但是,作为一个新手,对于这些问题的答案,往往是简单的:你写的程序仅仅你自己使用,在你的系统上,使用你购买或是下载下来的IDE。这使得事情变得简单,因此我们在这一步上不会花太多的时间。

 

3. 确定任务的子任务

在现实生活中,我们通常需要完成非常复杂的问题。试图指出如何完成这些任务是很有挑战的。同样,我们通常采用由上至下的方法来解决问题。也就是说,不像解决一个单一的问题,我们将一个任务分解为很多子任务,每一个部分都是很容易就能够解决的。如果这些子任务仍然很难被解决,他们能够被进一步的分解。不停的将复杂的任务分解为简单的部分,你最后会得到能够容易解决的各个问题,并且不至于琐碎。

让我们来看一下例子。加入我们要写一个关于胡萝卜的报告。我们的任务像这个样子:

* 写一个关于胡萝卜的报告

写一个关于胡萝卜的报告是一个很大的任务,因此让我们细分它+

* 写一个关于胡萝卜的报告

        o 对胡萝卜进行研究

        o 写一个大纲

        o 用关于胡萝卜的详细信息填充大纲

这样变得更加容易执行,我们已经将一个问题分解成了三个任务。但是,在这个例子中,“对胡萝卜进行研究是模糊不清的”,因此我们继续分解:

* 写一个关于胡萝卜的报告

        o 对胡萝卜进行研究

                D 去图书馆获取关于胡萝卜的书

                D 在网上搜索关于胡萝卜的信息

        o 写一个大纲

                D 关于成长的信息

                D 关于加工的信息

                D 关于营养的信息

        o 用关于胡萝卜的详细信息填充大纲

现在我们有了任务的列表,并且没有一个是特别困难的。通过完成每一个相对简单的子任务,我们能够完成一个更复杂的问题。

 

另一个建立层次的方法是采用自底向上的方法。我们通过这个方法能够将一些列简单的任务,通过归类、组织,分门别类。

作为一个例子,很多人在工作日不得不工作或是上学,因此假定我们系那个要解决的问题是“起床去工作”。如果,每天早上从床上爬起来的过程中你做了什么事,你也许会想到一系列事情:

D 挑选要穿的衣服

D 穿上衣服

D 吃早餐

D 开车去上班

D 刷牙

D 爬起床

D 准备早餐

D 进入你的车

D 洗个澡

将上面的过程进行分门别类得到如下的结果:

* 起床去工作

        o 卧室相关事情

                D 爬起床

                D 挑选要穿的衣服

        o 浴室相关事情

                D 洗个澡

                D 刷牙

        o 早餐相关事情

                D 准备早餐

                D 吃早餐

        o 交通相关事情

                D 进入你的车

                D 开车去上班

这种分层次的方法在编程中被证明是很有效的,因为一旦当你有一个任务层次,本质上你同时定义了你的程序的结构。最顶层的任务成为main()。子任务可以是程序中的函数。

如果证明子任务中的一项很难被实现的时候,通常继续将这个子任务进行细分,让一个函数通过调用实现新任务的不同的子函数。最后你会实现这个程序。

 

4. 指出事情发生的序列

现在你的程序有了一个结构,到了将这些任务组织到一起的时候了。首先需要确定事件发生的先后序列。举个例子,当你早上起床的时候,你完成那些事情的时候具体的顺序是什么样的呢?可能是这样的:

* 爬起床

* 挑选要穿的衣服

* 洗个澡

* 穿上衣服

* 准备早餐

* 吃早餐

* 刷牙

* 进入你的车

* 开车去上班

如果写一个计算器,我们会采用下面的顺序:

* 获取用户输入的第一个数据

* 获取用户输入的操作符

* 获取用户输入的第二个数据

* 计算结果

* 输出结果

这些一系列定义在函数中表现如下:

   1: int main()
   2: {
   3:     GetOutOfBed();
   4:     PickOutClothes();
   5:     TakeAShower();
   6:     GetDressed();
   7:     PrepareBreakfast();
   8:     EatBreakfast();
   9:     BrushTeeth();
  10:     GetInCar();
  11:     DriveToWork();
  12: }

 

或是计算器的例子:

   1: int main()
   2: {
   3:     // Get first number from user
   4:     GetUserInput();
   5:  
   6:     // Get mathematical operation from user
   7:     GetMathematicalOperation();
   8:  
   9:     // Get second number from user
  10:     GetUserInput();
  11:  
  12:     // Calculate result
  13:     CalculateResult();
  14:  
  15:     // Print result
  16:     PrintResult();
  17: }

 

 

5. 指出每一个任务中需要输入和输出的数据

当你有事件的层次,和事件的具体序列的时候,接下去的事情是指出每一个任务需要输入哪些数据,以及产生哪些数据。如果你已经从前面的步骤中获取了输入的数据,那么数据的数据将编程参数,如果你在其他的函数中计算得到结果,那么输出结果会是一个返回值。

当我们完成后,我们应该有每一个函数的原型。如果你已经忘记了,函数原型是包含函数名字,参数,返回类型的函数的声明,但是没有具体的实现。

让我们举个例子,GetUserInput()是相当的直接的。我们将获取用户输入的数据,并通过return返回给调用的函数。我们已经知道了各个序列之间的关系,获取得到的数据将会是其他函数的参数。通过CalculateResult()来计算结果,返回结果,但是它并不能自己显示结果。因此,我们需要返回这个值让其他的函数使用。

对于CaculateResult函数原型是这样的:

   1: int CalculateResult(int nInput1, char chOperator, int nInput2);

 

6. 编写每个任务的详细部分

在这个步骤,我们将编写每个任务具体的实现。如果你将任务划分的足够小,每一个小任务都将是非常简单,容易实现的。如果一个任务看上去还是挺复杂的,它就需要进一步的划分成一些容易实现的小任务。

举个例子:

   1: char GetMathematicalOperation()
   2: {
   3:     cout << "Please enter an operator (+,-,*,or /): ";
   4:  
   5:     char chOperation;
   6:     cin >> chOperation;
   7:  
   8:     // What if the user enters an invalid character?
   9:     // We'll ignore this possibility for now
  10:     return chOperation;
  11: }

 

 

7. 将输入和输出联系到一起

最后就是用一种合适的方式将输入与输出联系到一起。举个例子,CalculateResult函数的结果传到PrintResult函数,因此能够打印出结果。这通常包含中介变量的使用,用来暂时存储结果,方便函数间的传递。

   1: // nResult is a temporary value used to transfer the output of CalculateResult()
   2: // into an input of PrintResult()
   3: int nResult = CalculateResult(nInput1, chOperator, nInput2);
   4: PrintResult(nResult);

这种方式会比下面的代码更容易读:

   1: PrintResult( CalculateResult(nInput1, chOperator, nInput2) );

 

对于新手通常是最不好处理的一步。

完整的计算程序可以自己实现,答案间原文。

 

转载请注明来自:http://grass-and-moon.cnblogs.com

你可能感兴趣的:(设计)