9

Spartan Programmingに関するJeff の投稿は本当に楽しかったです。そのようなコードは読むのが楽しいことに同意します。残念ながら、一緒に仕事をすることが必ずしも楽しいものになるかどうかはわかりません。

何年もの間、私は「1 行に 1 つの式」という慣行について読み、それを順守してきました。多くのプログラミング本が次のようなコード例でこのアドバイスに反論したとき、私は善戦し、自分の立場を維持しました。

while (bytes = read(...))
{
   ...
}

while (GetMessage(...))
{
   ...
}

最近では、より実用的な理由から、1 行に 1 つの式を使用することを推奨しています。デバッグとプロダクション サポートです。「65行目」でNullPointer例外を主張する本番環境からログファイルを取得すると、次のようになります。

ObjectA a = getTheUser(session.getState().getAccount().getAccountNumber());

いら立たしく、完全に回避できます。null である「最も可能性の高い」オブジェクトを選択できるコードを専門家に提供することはできません...これは実際には非常に苦痛です。

1 行に 1 つの式を使用すると、コードをステップ実行するときにもかなり役立ちます。私はこれを、ほとんどの最新のコンパイラが、作成したばかりの余分な一時オブジェクトをすべて最適化できるという前提で実践しています...

私はきちんとしているように心がけていますが、明示的なオブジェクトでコードを雑然とさせることは、時には面倒に感じます。一般に、コードの閲覧が容易になるわけではありませんが、本番環境でトレースしたり、自分や他の人のコードをステップ実行したりする場合に非常に便利です。

あなたはどのようなスタイル提唱し、それを実際的な意味で合理化できますか?

4

8 に答える 8

7

The Pragmatic Programmer の中で Hunt と Thomas は Demeter の法則と名付けた研究について話し、それは機能をモジュール以外のモジュールに結合することに焦点を当てています。関数が結合の第 3 レベルに到達しないようにすることで、エラーの数が大幅に減少し、コードの保守性が向上します。

そう:

ObjectA a = getTheUser(session.getState().getAccount().getAccountNumber());

私たちはネズミ穴の4つのオブジェクトであるため、重罪に近い. つまり、これらのオブジェクトの 1 つで何かを変更するということは、まさにこのメソッドでこのスタック全体を呼び出したことを知っておく必要があります。なんてつらい。

より良い:

Account.getUser();

これは、現在モッキングソフトウェアで非常に人気のある表現形式のプログラミングに反することに注意してください。トレードオフは、とにかく密結合されたインターフェイスを使用することであり、表現力豊かな構文により使いやすくなります。

于 2008-09-09T03:26:01.690 に答える
5

理想的な解決策は、両極端の間のバランスを見つけることだと思います。すべての状況に適合するルールを作成する方法はありません。それには経験が伴います。中間変数をそれぞれ別の行で宣言すると、コードが読みにくくなり、保守が困難になる原因にもなります。同様に、中間値をインライン化すると、デバッグがはるかに難しくなります。

「スイート スポット」はその中間にあります。

于 2008-09-09T02:46:22.207 に答える
4

1 行に 1 つの式。

コードを難読化する理由はありません。いくつかの余分な用語を入力するのに時間がかかる分、デバッグ時間を節約できます。

于 2008-09-09T02:08:19.727 に答える
3

私は、必ずしもデバッグ可能性ではなく、可読性を重視する傾向があります。あなたが示した例は絶対に避けるべきですが、複数の式を適切に使用すると、コードがより簡潔で理解しやすくなると思います。

于 2008-09-09T03:01:43.217 に答える
2

私は通常、「短いほど良い」キャンプにいます。あなたの例は良いです:

ObjectA a = getTheUser(session.getState().getAccount().getAccountNumber());

これが 1 行ではなく 4 行を超えているのを見たら、私はうんざりします。読みやすく、理解しやすくなるとは思いません。ここで提示した方法から、単一のオブジェクトを掘り下げていることは明らかです。これは良くありません:

obja State = session.getState();
objb Account = State.getAccount();
objc AccountNumber = Account.getAccountNumber();
ObjectA a = getTheUser(AccountNumber);

これは妥協です:

objb Account = session.getState().getAccount();
ObjectA a = getTheUser(Account.getAccountNumber());

しかし、私はまだ単一行の表現を好みます。逸話的な理由は次のとおりです。私には、4行のタイプミスを読み直してエラーチェックするのが難しいです。単純に文字数が少ないため、1 行ではこの問題は発生しません。

于 2008-09-09T03:44:03.100 に答える
2
ObjectA a = getTheUser(session.getState().getAccount().getAccountNumber());

これは悪い例です。おそらく、頭のてっぺんから何かを書いたからでしょう。という名前の関数の戻り値を、aタイプという名前の変数に代入しています。 したがって、代わりにこれを書いたとしましょう:ObjectAgetTheUser

User u = getTheUser(session.getState().getAccount().getAccountNumber());

この式を次のように破ります。

Account acc = session.getState().getAccount();
User user = getTheUser( acc.getAccountNumber() );

私の理由は次のとおりです。このコードで何をしているのかをどう考えるか?
おそらく、「最初にセッションからアカウントを取得する必要があり、次にそのアカウントの番号を使用してユーザーを取得する必要がある」と思うでしょう。

コードはあなたが考えるように読むべきです。変数は、関連する主要なエンティティを参照する必要があります。それらのプロパティにはあまり影響しません (そのため、アカウント番号を変数に格納しません)。

心に留めておくべき 2 番目の要素は、このコンテキストでこのエンティティを再度参照する必要があるかどうかです。
たとえば、セッション状態からさらに多くのものを引き出す場合は、 を導入しSessionState state = session.getState()ます。

これはすべて当たり前のことのように思えますが、英語を母国語とする者ではない私には、なぜそれが意味をなすのかを言葉で説明するのが難しいのではないでしょうか。

于 2010-11-28T13:54:27.877 に答える
0

保守性と可読性が重要です。幸いなことに、短いほど読みやすくなることがよくあります。

以下は、私がコードを切り刻むために使用するいくつかのヒントです。

  • 変数名: この変数をチームの他の人にどのように説明しますか? 「numberOfLinesSoFar 整数」とは言いません。「numLines」または同様のことを言うでしょう-わかりやすくて短いです。メンテナがコードをまったく知らないふりをしてはいけませんが、自分で変数を書いたことを忘れたとしても、変数が何であるかを自分で理解できるようにしてください。はい、これは当たり前のことですが、多くのコーダーが取り組んでいるよりも多くの労力を費やす価値があるので、最初にリストします。
  • 制御フロー: 一度に多数の終了句を避ける (C++ の一連の })。通常、これが表示された場合は、回避する方法があります。一般的なケースは次のようなものです

:

if (things_are_ok) {
  // Do a lot of stuff.
  return true;
} else {
  ExpressDismay(error_str);
  return false;
}

で置き換えることができます

if (!things_are_ok) return ExpressDismay(error_str);
// Do a lot of stuff.
return true;

ExpressDismay (またはそのラッパー) が false を返すことができる場合。

別のケースは次のとおりです。

  • ループの繰り返し: より標準的であるほど優れています。より短いループでは、変数が単一のオブジェクトへのインデックスとして以外に使用されない場合は、1 文字の反復子を使用することをお勧めします。

ここで私が主張する特定のケースは、STL コンテナーを使用する「正しい」方法に反するものです。

for (vector<string>::iterator a_str = my_vec.begin(); a_str != my_vec.end(); ++a_str)

はかなり冗長で、オーバーロードされたポインター演算子 *a_str または a_str->size() がループ内に必要です。高速ランダム アクセスを持つコンテナーの場合、次のようにすると読みやすくなります。

for (int i = 0; i < my_vec.size(); ++i)

ループ本体で my_vec[i] への参照を使用しているため、混乱することはありません。

最後に、コーダーが行数のカウントに誇りを持っているのをよく見かけます。しかし、重要なのは行番号ではありません! これを実装する最善の方法はわかりませんが、コーディング文化に何らかの影響を与えている場合は、報酬をコンパクトなクラスを持つ人にシフトしようとします:)

于 2008-09-09T09:48:31.460 に答える
0

良い説明。これは、一般的な分割統治の考え方のバージョンだと思います。

于 2008-09-09T10:25:47.377 に答える