standard|编码|规范
许多项目的经验能得出这样的结论:采用编程标准可以使项目更加顺利地完成。标准是成功的关
键么?当然不。但它们可以帮助我们,而且我们需要我们能得到的所有的帮助!老实说,对一个
细节标准的大部分争论主要是源自自负思想。对一个合理的标准的很少决定能被说为是缺乏技术
性的话,那只是口味的原因罢了。所以,要灵活的控制自负思想,记住,任何项目都取决于团队
合作的努力。
使用“应该”一词的作用是指导项目定制项目细节规范。因为项目必须适当的包括 (include),
排除(exclude)或定制(tailor)需求。
使用“可以”一词的作用与“应该”类似,因为它指明了可选的需求。
命名是程序规划的核心。古人相信只要知道一个人真正的名字就会获得凌驾于那个人之上的不可思议的力
量。只要你给事物想到正确的名字,就会给你以及后来的人带来比代码更强的力量。别笑!
名字就是事物在它所处的生态环境中一个长久而深远的结果。总的来说,只有了解系统的程序员才能为系
统取出最合适的名字。如果所有的命名都与其自然相适合,则关系清晰,含义可以推导得出,一般人的推
想也能在意料之中。
如果你发觉你的命名只有少量能和其对应事物相匹配的话, 最好还是重新好好再看看你的设计吧。
例如:RetryMax 表示最多重试次数,RetryCnt 表示当前重试次数。
例如:IsHitRetryLimit。
举个NetworkABCKey的例子,注意C是应该是ABC里面的C还是key里面的C,这个是很令人费解的。有些
人不在意这些,其他人却很讨厌这样。所以你会在不同的代码里看到不同的规则,使得你不知道怎么
去叫它。
class FluidOz // 不要写成 FluidOZ class GetHtmlStatistic // 不要写成 GetHTMLStatistic
class NameOneTwo class Name
class JjLinkList { }
class NameOneTwo { function DoIt() {}; function HandleError() {}; }
class NameOneTwo { function VarAbc() {}; function ErrorNumber() {}; <br> var mVarAbc; var mErrorNumber; var mrName; }
class NameOneTwo { function StartYourEngines( &$rSomeEngine, &$rAnotherEngine); }
function HandleError($errorNumber) { $error = OsErr(); $time_of_error = OsErr->getTimeOfError; $error_processor = OsErr->getErrorProcessor; }
class Test { var mrStatus; <br> function DoSomething(&$rStatus) {}; function &rStatus() {}; }
global $gLog; global &$grLog;
<br>define("A_GLOBAL_CONSTANT", "Hello world!");
function test()<br>{<br> static $msStatus = 0; }
function some_bloody_function() { }
if ($condition) while ($condition) { { ... ... } }
if ($condition) { while ($condition) { ... ... } }
if ($very_long_condition && $second_very_long_condition) { ... } else if (...) { ... })。あるプログラム ブロックから別のプログラム ブロックに移動するには、カーソルを使用してキーを括弧に一致させる必要はありません。行の末尾まで前後に移動して、一致する括弧を見つけます。
インデント/タブ/スペースのルール
インデントにはタブを使用します。
function func() { if (something bad) { if (another thing bad) { while (more input) { } } } }
if (condition) { } while (condition) { } strcmp($s, $s1); return 1;
当完成对象架构时,为该对象建立一个Open()方法,Open()方法应该以对象实体命名。
class Device { function Device() { /* initialize and other stuff */ } function Open() { return FAIL; } }; $dev = new Device; if (FAIL == $dev->Open()) exit(1);
这由程序员决定。不同的花括号样式会产生些微不同的样观。一个通用方式是:
if (条件1) // 注释 { } else if (条件2) // 注释 { } else // 注释 { }如果你有用到else if 语句的话,通常最好有一个else块以用于处理未处理到的其他情况。可以的话
if ( 6 == $errorNum ) ...
一个原因是假如你在等式中漏了一个等号,语法检查器会为你报错。第二个原因是你能立刻找到数值
而不是在你的表达式的末端找到它。需要一点时间来习惯这个格式,但是它确实很有用。
switch (...) { case 1: ... // FALL THROUGH case 2: { $v = get_week_number(); ... } break; default: }
Continue 和 break 像 goto 一样,它们在代码中是有魔力的,所以要节俭(尽可能少)的使用它们。
使用了这一简单的魔法,由于一些未公开的原因,读者将会被定向到只有上帝才知道的地方去。
Continue有两个主要的问题:
看看下面的例子,考虑一下问题都在哪儿发生:
while (TRUE) { ... // A lot of code ... if (/* some condition */) { continue; } ... // A lot of code ... if ( $i++ > STOP_VALUE) break; }注意:"A lot of code"是必须的,这是为了让程序员们不能那么容易的找出错误。
通过以上的例子,我们可以得出更进一步的规则:continue 和 break 混合使用是引起灾难的正确方法。
(condition) ? funct1() : func2(); or (condition) ? long statement : another long statement;
var $mDate var& $mrDate var& $mrName var $mName $mDate = 0; $mrDate = NULL; $mrName = 0; $mName = NULL;
while ($dest++ = $src++) ; // VOID
if (FAIL != f())比下面的方法好:
if (f())即使 FAIL 可以含有 0 值 ,也就是PHP认为false的表示。在某人决定用-1代替0作为失败返回值的时候,
非零测试采用基于缺省值的做法,那么其他函数或表达式就会受到以下的限制:
大部分函数在FALSE的时候返回0,但是发挥非0值就代表TRUE,因而不要用1(TRUE,YES,诸如此类)等式检测一个布尔值,应该用0(FALSE,NO,诸如此类)的不等式来代替:
if (TRUE == func()) { ...应该写成:
if (FALSE != func()) { ...
while ($a != ($c = getchar())) { process the character }
++和--操作符类似于赋值语句。因此,出于许多的目的,在使用函数的时候会产生副作用。使用嵌入式赋值
提高运行时性能是可能的。无论怎样,程序员在使用嵌入式赋值语句时需要考虑在增长的速度和减少的可维
护性两者间加以权衡。例如:
a = b + c; d = a + r;不要写成:
d = (a = b + c) + r;虽然后者可以节省一个周期。但在长远来看,随着程序的维护费用渐渐增长,程序的编写者对代码渐渐遗忘,
开发一个通用结构需要预先花费许多的努力来设计。当努力不成功的时候,无论出于什么原因,有几种办法推
荐使用:
如果你需要某些事项的源代码,如果已经有某人做过的话,就向群组发email求助。结果会很惊喜哦!
在许多大的群组中,个人往往不知道其他人在干什么。你甚至可以发现某人在找一些东西做,并且自愿为你写代
码,如果人们在一起工作,外面就总有一个金矿。
这样的其中一个原因就是人们不喜欢做一个小库,对小库有一些不正确感觉。把这样的感觉克服掉吧,电脑才不
关心你有多少个库呢。
如果你有一些代码可以重用,而且不能放入一个已经存在的库中,那么就做一个新的库吧。如果人们真的考虑重
用的话,库不会在很长的一段时间里保持那么小的。
If you are afraid of having to update makefiles when libraries are recomposed or added then don't include libraries in your makefiles, include the idea of services. Base level makefiles define services that are each composed of a set of libraries. Higher level makefiles specify the services they want. When the libraries for a service change only the lower level makefiles will have to change.
In an ideal world a programmer could go to a web page, browse or search a list of packaged libraries, taking what they need. If you can set up such a system where programmers voluntarily maintain such a system, great. If you have a librarian in charge of detecting reusability, even better.
Another approach is to automatically generate a repository from the source code. This is done by using common class, method, library, and subsystem headers that can double as man pages and repository entries.
利用类似ccdoc的文档抽取系统。在这一文档的其他部分描述的是怎么利用ccdoc记录一个类和方法。
这些标头说明可以以这样的一个方式来提取并分析和加以组织,它们不像一般的标头一样是无用的。
因此花时间去填上他吧。
工程的每部分都有特定的注释布局。
// :TODO: tmh 960810: possible performance problem // We should really use a hash table here but for now we'll // use a linear search. // :KLUDGE: tmh 960810: possible unsafe type cast // We need a cast here to recover the derived type. It should // probably use a virtual method or template.
Any project brings together people of widely varying skills, knowledge, and experience. Even if everyone on a project is a genius you will still fail because people will endlessly talk past each other because there is no common language and processes binding the project together. All you'll get is massive fights, burnout, and little progress. If you send your group to training they may not come back seasoned experts but at least your group will all be on the same page; a team.
There are many popular methodologies out there. The point is to do some research, pick a method, train your people on it, and use it. Take a look at the top of this page for links to various methodologies.
You may find the CRC (class responsibility cards) approach to teasing out a design useful. Many others have. It is an informal approach encouraging team cooperation and focusing on objects doing things rather than objects having attributes. There's even a whole book on it: Using CRC Cards by Nancy M. Wilkinson.
An individual use case may have a name (although it is typically not a simple name). Its meaning is often written as an informal text description of the external actors and the sequences of events between objects that make up the transaction. Use cases can include other use cases as part of their behaviour.
Have as many use cases as needed to describe what a system needs to accomplish.
In practice the Open/Closed principle simply means making good use of our old friends abstraction and polymorphism. Abstraction to factor out common processes and ideas. Inheritance to create an interface that must be adhered to by derived classes.
The contract is enforced in languages like Eiffel by pre and post condition statements that are actually part of the language. In other languages a bit of faith is needed.
Design by contract when coupled with language based verification mechanisms is a very powerful idea. It makes programming more like assembling spec'd parts.
if ($abool= $bbool) { ... }程序员在这里真的是要赋值么?一般常常是,但通常又不是这样。这样避免引起这样的混乱呢?解
$abool= $bbool; if ($abool) { ... }
function example() { great looking code if (0) { lots of code } more code }
你不能使用/**/,因为注释内部不能包含注释,而大段的程序中可以包含注释,不是么?
To see why ask yourself:
class X { function GetAge() { return $this->mAge; } function SetAge($age) { $mAge= $age; } var $mAge; }
class X { function Age() { return $mAge; } function Age($age) { $mAge= $age; } var $mAge; }Similar to Get/Set but cleaner. Use this approach when not using the Attributes as Objects approach.
class X { function Age() { return $mAge; } function rAge() { return &$mAge; } function Name() { return mName; } function rName() { return &$mName; } var $mAge; var $mName; }<br> X $x; $x->rName()= "test";The above two attribute examples shows the strength and weakness of the Attributes as Objects approach.
When using rAge(), which is not a real object, the variable is set directly because rAge() returns a reference. The object can do no checking of the value or do any representation reformatting. For many simple attributes, however, these are not horrible restrictions.
A layering violation simply means we have dependency between layers that is not controlled by a well defined interface. When one of the layers changes code could break. We don't want code to break so we want layers to work only with other adjacent layers.
Sometimes we need to jump layers for performance reasons. This is fine, but we should know we are doing it and document appropriately.
My god he's questioning code reviews, he's not an engineer!
Not really, it's the form of code reviews and how they fit into normally late chaotic projects is what is being questioned.
First, code reviews are way too late to do much of anything useful. What needs reviewing are requirements and design. This is where you will get more bang for the buck.
Get all relevant people in a room. Lock them in. Go over the class design and requirements until the former is good and the latter is being met. Having all the relevant people in the room makes this process a deep fruitful one as questions can be immediately answered and issues immediately explored. Usually only a couple of such meetings are necessary.
If the above process is done well coding will take care of itself. If you find problems in the code review the best you can usually do is a rewrite after someone has sunk a ton of time and effort into making the code "work."
You will still want to do a code review, just do it offline. Have a couple people you trust read the code in question and simply make comments to the programmer. Then the programmer and reviewers can discuss issues and work them out. Email and quick pointed discussions work well. This approach meets the goals and doesn't take the time of 6 people to do it.
Some issues to keep in mind:
Make a web page or document or whatever. New programmers shouldn't have to go around begging for build secrets from the old timers.
プログラマは一般にバグ追跡に抵抗しますが、正しく使用すればプロジェクトに非常に役立ちます:
参考までに、修正したバグの数によって報酬を与えるのは良い考えではありません :-)
ソース コード管理はバグ追跡システムにリンクされるべきです。リリース前にソースが凍結されているプロジェクトの一部では、有効なバグ ID を伴うチェックインのみが受け入れられる必要があります。また、バグを修正するためにコードが変更された場合は、チェックイン コメントにバグ ID を含める必要があります。
実際のところ、コードを所有していない場合は、コードを変更することはできません。文脈が多すぎます。あなたにとって合理的であると思われる仮定は、完全に間違っている可能性があります。変更が必要な場合は、担当者に変更を依頼してください。または、これこれの変更を加えてもよいかどうかを尋ねます。 OK と言われたら先に進み、そうでなければエディターをホルスターに入れてください。
どのルールにも例外があります。午前 3 時に成果物を作成するために変更が必要な場合は、変更する必要があります。誰かが休暇中で、誰もそのモジュールを割り当てられていない場合は、あなたがそれを行う必要があります。他の人のコードに変更を加える場合は、その人が採用しているのと同じスタイルを使用するようにしてください。
プログラマーは、特に変更に敏感なコードをコメントでマークする必要があります。ある領域のコードを別の領域のコードに変更する必要がある場合は、そのように指示してください。データ形式を変更すると永続ストアやリモート メッセージ送信との競合が発生する場合は、その旨を伝えてください。メモリ使用量を最小限に抑えたい場合、または他の目的を達成しようとしている場合は、そう言ってください。誰もがあなたほど優秀なわけではありません。
最悪の罪は、コーディング スタイルに合わせてシステム内を飛び回ってコードの一部を変更することです。誰かが標準に従ってコーディングしていない場合は、その人に尋ねるか、標準に従ってコーディングするようにマネージャーに依頼してください。一般的な礼儀を守りましょう。
共通の責任を持つコードは慎重に扱う必要があります。対立を解決するのは難しいため、根本的な変更は避けてください。全員が同じルールに従うように、ファイルを拡張する方法についてファイルにコメントを追加します。すべての共通ファイルで共通の構造を使用するようにしてください。そうすることで、人々がどこで何を見つけ、どのように変更を加えるかを推測する必要がなくなります。競合が発生しないように、できるだけ早く変更をチェックインします。
余談ですが、バグ追跡の目的でもモジュールの責任を割り当てる必要があります。
if (22 == $foo) { start_thermo_nuclear_war(); } else if (19 == $foo) { refund_lotso_money(); } else if (16 == $foo) { infinite_loop(); } else { cry_cause_im_lost(); }在上例中22和19的含义是什么呢?如果一个数字改变了,或者这些数字只是简单的错误,你会怎么想?
使用不可思议的数字是该程序员是业余运动员的重要标志,这样的程序员从来没有在团队环境中工作过,
又或者是为了维持代码而不得不做的,否则他们永远不会做这样的事。
你应该用define()来给你想表示某样东西的数值一个真正的名字,而不是采用赤裸裸的数字,例如:
define("PRESIDENT_WENT_CRAZY", "22"); define("WE_GOOFED", "19"); define("THEY_DIDNT_PAY", "16"); if (PRESIDENT_WENT_CRAZY == $foo) { start_thermo_nuclear_war(); } else if (WE_GOOFED == $foo) { refund_lotso_money(); } else if (THEY_DIDNT_PAY == $foo) { infinite_loop(); } else { happy_days_i_know_why_im_here(); }现在不是变得更好了么?
Robert Martin put OO in perspective:
The two extremes are thin classes versus thick classes. Thin classes are minimalist classes. Thin classes have as few methods as possible. The expectation is users will derive their own class from the thin class adding any needed methods.
While thin classes may seem "clean" they really aren't. You can't do much with a thin class. Its main purpose is setting up a type. Since thin classes have so little functionality many programmers in a project will create derived classes with everyone adding basically the same methods. This leads to code duplication and maintenance problems which is part of the reason we use objects in the first place. The obvious solution is to push methods up to the base class. Push enough methods up to the base class and you get thick classes.
Thick classes have a lot of methods. If you can think of it a thick class will have it. Why is this a problem? It may not be. If the methods are directly related to the class then there's no real problem with the class containing them. The problem is people get lazy and start adding methods to a class that are related to the class in some willow wispy way, but would be better factored out into another class. Judgment comes into play again.
Thick classes have other problems. As classes get larger they may become harder to understand. They also become harder to debug as interactions become less predictable. And when a method is changed that you don't use or care about your code will still have to be retested, and rereleased.