87
Code Complete Horky 3/Aug,2010 http://blog.csdn.net/horkychen

代码大全(内训)

Embed Size (px)

Citation preview

Code Complete

Horky

3/Aug,2010http://blog.csdn.net/horkychen

Agenda• Defensive programming• Organizing straight-line code • Using Conditionals• Controlling Loops• Unusual Control Structures• Table-Driven Methods• General Control Issues• Layout and Style• Code Tuning Strategies

Defensive programming

Defensive programming

• Common Skills– Error handling– Assertion– Exception handling– Offensive programming

Defensive programming• Protect your program from invalid inputs

– Common Sense• Garbage in, garbage out

– Let the user beware• Good program won’t put out garbage.

– Garbage in, nothing out– Garbage in, error message out– No garbage allowed in

• During construction, protecting yourself against the small stuff matters more than you might think. (魔鬼就在细节之中 !)

• General ways– Check the values of all data from external source

• Check all data falls within the allowable range for I/O operation and data transfer between routines.

– Check the value of all routine input parameters– Decide how to handle bad inputs

• Error handling• Assertion• Exception

Several reasons to enhance error handling

• Overall thinking– To enhance your capability on question analysis.– To enhance your capability on design.– To enhance your coding skill.

• Find earlier, fix earlier– To reduce potential issues.– To reduce bug fixing efforts.

Error handling

• To handle errors that you do expect to occur!– Return a neutral value– Substitute the next piece of valid data– Return the same answer as the previous time– Substitute the closest legal value– Log a warning message to a file– Return an error code– Call an error processing routine/object– Display an error message wherever the error is encountered– Handle the error in whatever way works best locally

• Not good design.– Shutdown

Assertion• 同 <<程序员修练之道 >>之断言式编程• Common Sense

– Use assertion to handle errors that should never occur.– Easy for parameters checking.

• Sample in Objective-C

If that error should never occur, use assertion to define it. If it happens, crash early!If that error should never occur, use assertion to define it. If it happens, crash early!

Disable assertion in XCode

• Define preprocessor macro– NS_BLOCK_ASSERTIONS

Practice• Below function:

/*! @brief Description: Buffer handling function. @param pBuffer The target buffer to be processed. @param action Action type of processing, that includes COPY,SAVE and REMOVE. @param pFileName The target file name include full path for SAVE action.*/

OSErr HandleTheBuffer(BYTE *pBuffer, ACTION action, BYTE *pFileName)

• ACTION is a enumerate, includes: BUFFER_ACTION_COPY BUFFER_ACTION_SAVE BUFFER_ACTION_REMOVE

• Question:– How to check every parameters?

Exception handling• Basic structure try

{ 包含可能抛出异常的语句;

throw 错误 的对象}catch( 类型名 [形参名 ]) // 捕获特定类型的异常{}catch( 类型名 [形参名 ]) // 捕获特定类型的异常{}catch(...) // 三个点则表示捕获所有类型的异常{

}

• Common Sense– Throw an exception only for conditions that are truly exceptional.

• Similar with Assertion– Don’t use an exception to pass the buck

• To handle the error condition locally if it is possible.– Avoid throwing exceptions in constructors and destructors unless you

catch them in the same place.– Consider building a centralized exception reporter.

Exception handling in Objective-C

• Exception handing is not available on iOS.

Sample

Centralized exception reporter in C++

Design of Centralized Exception Reporter

Offensive Programming

• Common Sense– Crash Early– Don’t use an exception to pass the buck!

Organizing straight-line code

Statements that must be in specific order

• Organize code so that dependencies are obvious.• Name routines so that dependencies are obvious.• Use routine parameters to make dependencies

obvious.• Document unclear dependencies with comments.• Check dependencies by assertion or error

handling code

Organize code ///Initialize members and start calculation

Revenue.ComputeMarketingExpense();

Revenue.ComputeSalesExpense();Revenue.ComputeTravelExpense();Revenue.ComputePersonnelExpense();

Revenue.DisplayExpenseSummary();

///Initialize members Revenue.InitializeExpenseData();

///Start calculation Revenue.ComputeMarketingExpense(); Revenue.ComputeSalesExpense(); Revenue.ComputeTravelExpense(); Revenue.ComputePersonnelExpense();

Revenue.DisplayExpenseSummary();

Name Routines

• Below routine will initialize member data also:– ComputeMarketingExpense()

If change the name as below:– ComputeMarketingExpenseAndInitializeMemberData()

Name is correct, but the routine is terrible.

Use routine parameters///Initialize members

Revenue.InitializeExpenseData(expenseData);

///Start calculation Revenue.ComputeMarketingExpense(expenseData); Revenue.ComputeSalesExpense(expenseData); Revenue.ComputeTravelExpense(expenseData); Revenue.ComputePersonnelExpense(expenseData);

Revenue.DisplayExpenseSummary(expenseData);

Revenue.ComputeMarketingExpense( marketingData );

Revenue.ComputeSalesExpense( salesData );

Revenue.ComputeTravelExpense( travelData );

Revenue.ComputePersonnelExpense( personnelData );

Revenue.DisplayExpenseSummary( marketingData,salesData, travelData, personnelData );

Check dependencies

• To add one flag to indicate one specified condition.

• Then check the flag is correct before take next action.

• Risk: It may bring new issue!

Statements whose order doesn’t matter

• Making code read from Top to Bottom– Terrible example:

• travelData.ComputeQuarterly();• salesData.ComputeQuarterly();• marketingData.ComputeQuarterly();

• travelData.ComputeAnnual();• salesData.ComputeAnnual();• marketingData.ComputeAnnual();

• salesData.Print();• marketingData.Print();• travelData.Print();

– Good example:• travelData.ComputeQuarterly();• travelData.ComputeAnnual();• travelData.Print();

• salesData.ComputeQuarterly();• salesData.ComputeAnnual();• salesData.Print();

Statements whose order doesn’t matter

• Grouping related statements– Closing principle– If each statements in block has strong

dependencies, and they are independent with other parts, grouping them in new routine will be better.

Using Conditionals

Common Sense

• Principle of coding– Correct– Simple and Clear– Easy for modification• 深棋手 力之迷资 记忆

Plain if-then statements• Write the nominal path through the code first; then write the

unusual cases.• Make sure that you branch correctly on equality.

– To avoid off-by-one error.

• Put the normal case after the if rather than after the else• Follow the if clause with a meaningful statement

– if ( condition )– ;– else– {

• DoSomething();– }

• Consider the else clause– According GM’s survey(1976), 5 to 8 percent of if statements need else statement.– Why you ignore the else? That needs explanation.

• Check for reversal of the if and else clauses

Sample of if statments

Modification

Chains of if-then-else statements

• Simplify complicated tests with boolean function calls

• Put the most common cases first• Make sure that all cases are covered

case Statements

• Choosing the most effective ordering of cases– Order cases alphabetically or numerically• Equally important

– Put the normal case first– Order cases by frequency

Tips

• Keep the actions of each case simple– Call routines as needed.

• Don’t make up phony variables in order to be able to use the case statement.

• Use the default clause only to detect legitimate defaults

• Use the default clause to detect errors

Controlling Loops

Selecting the kind of loop

• Need a loop that executes a specified number of times– To use a for loop– If change the index value of a for loop force it to

terminate, use a while loop instead.

• Else to use a while loop.

• Reference:– Write Solid Cod, Maguire, 1993

Controlling the loop

• Major principles–简化循环体相关连的因素(所引用的变量或数据),判断或结束条件一定要简单。

–循环也是另类的子程序,可以视其为 Black box.

• Entering the Loop– Put initialization code directly before the loop– Use while ( true ) for infinite loops– Don’t use a for loop when a while loop is more

appropriate

Sample

Control statements vs. housekeeping statements

Processing the middle of the loop• Use { and } to enclose the statements in a loop• Avoid empty loops• Keep loop-housekeeping chores at either the beginning or the

end of the loop• Make each loop perform only one function

– 可以做,但不代表 做。正 的代 不代表是好的代应该 确 码 码 !

Exiting the loop• Existing loops early

– According to an article in Software Engineering Notes, the software error that 7 brought down the New York City phone systems for 9 hours on January 15, 8 1990 was due to an extra break statement (SEN 1990)

break的分散代表逻辑上的分散,会带来维护上的风险!大量的 return出现在函数体,多个深层嵌套,变量中途转为它用,多处理重复功能相近的代码,都预示者开发者的逻辑可能已经出现问题!

Checking endpoints

• Willingness to perform this kind of check is a key difference between efficient and inefficient programmers.

• How to check– Hand calculations– Mental simulations– Calculate by Excel

• What are benefits?– You understand how your code works rather than

guessing about it!

How long should a loop be

• Make your loops short enough to view all at once.

• Limit nesting to three levels• Move loop innards of long loops into routines• Make long loops especially clear.– Single exit.

Creating loop easily

• From the inside out

Unusual Control Structures

Common Sense

• 宝 有多大的 力,取决于它在 的手上!剑 杀伤 谁

Multiple returns from a routine

• Constructed programming– Single entering and single exit

• Use a return when it enhances readability• Use guard clauses (early returns or exits) to

simplify complex error processing• Minimize the number of returns in each

routine

SampleA

B

C

Recursion

• Recursion is not the best solution, it is an iteration algorithm only.

• Tips– Make sure the recursion stops– Use safety counters to prevent infinite recursion– Limit recursion to one routine– Keep an eye on the stack– Don’t use recursion for factorials or Fibonacci

numbers

goto statement

• The phony goto debate• goto is a solution for try-finally

Summary• Follow control structures was a good idea:

– Unrestricted use of gotos– Ability to compute a goto target dynamically, and jump to the

computed location– Ability to use goto to jump from the middle of one routine into the

middle of another routine– Ability to call a routine with a line number or label that allowed

execution to begin somewhere in the middle of the routine– Ability to have the program generate code on the fly, then execute

the code it just wrote

• The field of software development has advanced largely through restricting what programmers can do with their code.

Reference

• “Go To Statement Considered Harmful”

<<Communications of the ACM>> 11, no.3

Mar,1968 (P147-148)

Dijkstra, Edsger

Table-Driven Methods

General considerations

• Advantage:– Simplify codes and enhance the performance– Enhance the system customization capability.

• Weakness:– Not everyone know it well, it may bring extra

efforts.

• Virtually anything you can select with logic statements, you can select with tables instead.

Indexed Access Tables

Stair-Step Access Tables

Examplestatic unsigned int chooseDPI(unsigned int

original_dpi, int datatype)

{

int i, d, diff, k;

static Support_Mode support_mode[] = {

{4, 0}, //support two modes

{150, 2},

{300, 3}, //300DPI with color and gray

{600, 3}, //600DPI with color and gray

{1200, 2}

};

diff = -1;

k = 1;

for (i = 1; i <= support_mode[0].dpi; ++i){

if (support_mode[i].support_mode & datatype){

d = abs(support_mode[i].dpi - original_dpi);if (diff == -1){

diff = d;k = i;

}else{

if (d <= diff){

diff = d;k = i;

}else

break;}

}}return support_mode[k].dpi;

}

General Control Issues

Boolean Expressions

• Using true and false for Boolean tests– Make the intent clearer– Reduce misunderstanding

Making complicated expression simple

*Decision table is another solution!

Forming Boolean Expressions Positively

• Not a few people don’t have not any trouble understanding a non-short string of non-positives.

• Apply DeMorgan’s Theorems to simplify boolean tests with negatives– if ( !displayOK || !printerOK )– if( !( displayOK && printerOK ))

Using Parentheses to clarify boolean expressions

• if ( a<b == c == d)• if ( (a<b) == ( c==d ) )

Common problems with Boolean Expressions

• Put constants on the left side of comparisons• Consider creating preprocessor macro

substitutions for &&, and == (but only as a last resort)

Taming dangerously deep nesting

• Factor deeply nested code into its own routine• Use a more object-oriented approach

– Factory design model

• Summary:• More generally, complicated code is a sign

that you don’t understand your program well enough to make it simple.

Control Structure and Complexity

• The control flow is at least one of the largest contributors to complexity, if not the largest.

• HP apply McCabe’s complexity metric, that is helpful to improve coding quality.

• General guideline for reducing complexity– Improve your own mental juggling abilities– You can decrease the complexity of your programs

and the amount of concentration required to understand them.

How to measure complexity

• Techniques for counting the decision points in a routine– Start with 1 for the straight pat through the routine– Add 1 for each of the following keywords, or their

equivalents: if while repeat for and or– Add 1 for each case in a case statement

• Exercise– if ( ( (status = Success ) and done) or ( not done and ( numLines >= maxLines ) ) ) then …

Layout and StyleAesthetic Aspect

Attention to detail

• 代 是供人 的码 阅读– The smaller part of the job of programming is writing

a program so that the computer can read it; the larger part is writing it so that other humans can read it.

• Objectives of good layout– Accurately represent the logical structure of the code.– Consistently represent the logical structure of the

code– Improve readability– Withstand modifications

Major concepts

• Using only one statement per line• Indent a comment with its corresponding

code• Use blank lines to separate parts of a routine• Use blank lines between paragraphs• One class in one file

Samples

Grouping

V.S.

Quality characteristics and performance

• 性能同代 速度之 存在很松散的 系码 间 联a.使用 卡器 取 片读 读 图b.使用数据线 + 准 出工具 取 片标 导 读 图

分强 速度,程序的整体性能常常不升反降过 调!

Performance and Code Tuning

• 程序需求• 程序设计• 类和子程序的设计• 程序同操作系 的交互统• 代码编译• 硬件• 代 整码调

Program Requirements

• 将效能要求从 1 秒降到 4 秒, 省了节700Million USD.

• 巢的 化鸟 设计变

Program Design

• 框架是一个重要的效能限制。

Class and Routine design

• 包括算法的选择

Operating system interactions

• 用系 子程序需要很多的花调 统 费

Code Compilation

• 不同的 器 来不同的性能编译 带

Hardware

• 最 有效为简单

Code tuning

• 正 代 行 整的 践对 确 码进 调 实

Why need code-tuning

• 不是最有效的方法• 不是最 便的方法简• 球 拾球的 作员 动–掌握 写高效代 的 是成 意编 码 这门艺术 为严肃 义

上的程序 的加冕 式员 仪– 是高效的代 并不一定是“更好”的代问题 码 码

Pareto Principle

• 80/20 原则– 4% 的部分决定了 50% 的 行运 时间

• The best is the enemy of the good!

Old Wives' Tales

• 代 行数越少,效率越高码• 特定 算可能比其他的快,代 模也越小运 码规• 当随 随地 行 化应 时 进 优– 很 在程序所有功能都 之前 定出程序性能的难 运转 确

瓶颈– 在 小的情况下,程序 能正 定出程序的瓶极 员 确确 颈– 在 初期, 度 注 化会干 其它程序目开发 过 关 优 扰对 标

的理解和判断• 程序的 行速度同其正 性同等重要运 确– 是要一个花 1 秒得出 果的程序, 是另一错误结 还

个花 4 秒得出正 果的程序确结

Jackson’s principle

• 不要 代 行 化对 码进 优• 不要 化优 - 除非你已 有了一个非常清晰,经

而且未 化的解决方案经优

性能 量测

• 矩 求和的性能阵 评测• 一定要踏 的 行 量,除非你真得非常实 进 测

定,决不能想当然。一切都有可能确 !

Key points

• 性能只是 件整体 量的一个方面,通常不是最软 质重要的。精 的代 整也只是 整体性能的细 码调 实现一 方法,通常也不是决定性。(架 、种 构 细节设

、数据 及算法计 结构 )• 定量 量是 性能最 化的 。测 实现 优 关键• 小部分代 花 了 大部分的 。码 费 绝 时间• 代 整需要反 。码调 复尝试• 化工作做好准 的最佳方式是在最初 段为优 备 阶 编

写清晰的代 。码

Appendix

• 一家企 ,在引入 行代 修改的 之业 单 码 检查前,修改 生 的几率是发 错误 55%, 而引入之后, 几率降低到错误 2% (Freedman and Weinberg 1982) 。

• 一家 信企 在引入代 后,程序的电 业 码检查正 率从确 86% 提升到 99.6% (Perrott 2004) 。

Reference

• The Art of Computer Programming

THANKS