クラスメソッド定義を注文する最も人間に優しい方法は?


38

どのクラス定義でも、メソッド定義はさまざまな方法で並べられています:アルファベット順、最も一般的な使用法に基づいた時系列、可視性でアルファベット順にグループ化、ゲッターとセッターでグループ化されたアルファベット順など。新しいクラスの作成を開始すると、私はすべてを入力する傾向があり、クラス全体の記述が完了したら順序を変更します。そのメモについて、私は3つの質問を持っています:

  1. 順序は重要ですか?
  2. 「最高の」注文はありますか?
  3. 私はそうではないと思うので、異なる順序付け戦略の長所と短所は何ですか?

1
単にメソッドを並べ替えるために、人々がコードをカット/ペーストすることを本当に期待していません。IDEが自動的に実行する場合は、問題ありません。それ以外の場合、強制するのは困難です。
Reactgular

明確にするために、私の質問は構文ではなく読みやすさ/使いやすさについてです。
ジョントロン

回答:


52

一部のプログラミング言語では、宣言されるまで物を利用できないため、順序が重要です。しかし、それを除けば、ほとんどの言語ではコンパイラにとって重要ではありません。だから、あなたはそれが人間にとって重要なままになっています。

私のお気に入りのマーティン・ファウラーの引用は次のとおりです。Any fool can write code that a computer can understand. Good programmers write code that humans can understand.したがって、クラスの順序は、人間が理解しやすいものに依存するべきだと思います。

私は個人的に、ボブ・マーティンが彼のClean Code本で与えているステップダウン治療を好みます。クラスの最上位にあるメンバー変数、コンストラクター、その他すべてのメソッド。そして、メソッドをクラス内でどのように使用されるかによって近くなるように順序付けます(すべてのパブリック、プライベート、そして保護を任意に配置するのではなく)。彼はそれを「垂直距離」またはそのようなものを最小化すると呼びます(現時点では私に本を持っていません)。

編集:

「垂直距離」の基本的な考え方は、ソースコードを理解するためだけにソースコードの周りを人々が飛び回るのを避けることです。物事が関連している場合、それらはより密接でなければなりません。無関係なものはさらに離れている可能性があります。

Clean Codeの第5章 (素晴らしい本、ところで)は、Martin氏が注文コードをどのように提案するかについて、非常に詳細に説明しています。彼は、コードを読むことは新聞記事を読むようなものであるべきだと提案している:高レベルの詳細が最初に(上部に)来て、あなたが読むほど詳細が得られる。「ある関数が別の関数を呼び出す場合、関数は垂直方向に近く、呼び出し元は可能な限り呼び出し先の上にいる必要があります」と言います。さらに、関連する概念は密接に関連している必要があります。

だから、多くの点で悪い(オブジェクト指向設計が不十分でdouble、お金を使うことは決してない)不自然な例がありますが、その考えを示しています:

public class Employee {
  ...
  public String getEmployeeId() { return employeeId; }
  public String getFirstName() { return firstName; }
  public String getLastName() { return lastName; }

  public double calculatePaycheck() {
    double pay = getSalary() / PAY_PERIODS_PER_YEAR;
    if (isEligibleForBonus()) {
      pay += calculateBonus();
    }
    return pay;
  }

  private double getSalary() { ... }

  private boolean isEligibleForBonus() {
    return (isFullTimeEmployee() && didCompleteBonusObjectives());
  }

  public boolean isFullTimeEmployee() { ... }
  private boolean didCompleteBonusObjectives() { ... }
  private double calculateBonus() { ... }
}

メソッドは、呼び出し元のメソッドに近くなるように順序付けられており、上から順に実行されます。privateメソッドの下にすべてのメソッドを配置した場合public、プログラムのフローをたどるには、さらにジャンプする必要があります。

getFirstNameそして、getLastName概念的に関連する(とされているgetEmployeeId、おそらくあまりにもある)ので、彼らは一緒に接近しています。それらをすべて下に移動することはできますが、上と下に表示getFirstNameしたくないでしょうgetLastName

これが基本的なアイデアを与えてくれることを願っています この種のことに興味があるなら、を読むことを強くお勧めしますClean Code


インスタンス変数のセッターとゲッターをどのように配置する必要があるかを知る必要があります。クラスコンストラクターの直後、またはクラスの下部に配置する必要がありますか?
srinivas

個人的には、コンストラクターの後の一番上にあるのが好きです。しかし、それは本当に重要ではありません。決定する良い方法として、プロジェクトとチームの一貫性をお勧めします。
アラン

べきではないcalculateBonus()の前に来るisFullTimeEmployee()didCompleteBonusObjectives()
winklerrr

@winklerrrそのための議論を見ることができます。私はそれらを使用した場所に配置しました。なぜならisFullTimeEmployee、彼らはそれに垂直に近いはずだからです。しかし、潜在的にそれを呼び出した場所に近づけるために上に移動することができます。残念ながら、関数を呼び出す関数があると、最終的には、完全な順序付けができない問題(他の複数の関数によって呼び出される1つの共有関数など)になります。それはあなたの最高の判断に任されています。これらの問題を軽減するために、クラスと関数を小さく保つことをお勧めします。didCompleteBonusObjectivesisEligibleForBonuscalculateBonus
アラン

2

私は通常、関係と使用順序でメソッドを注文します。

ネットワーククラスを使用して、すべてのTCPメソッドをグループ化し、次にすべてのUDPメソッドをグループ化します。内部TCPでは、最初にセットアップメソッドがあり、2番目に特定のメッセージを送信し、3番目にtcpソケットを閉じます。

明らかに、すべてのクラスがそのパターンに適合するわけではありませんが、それが私の一般的なワークフローです。

私は他の何よりもデバッグするためにそのようにします。問題があり、メソッドにジャンプしたいとき、私はそれがどのようにスペルされているとは思わず、それが何のために責任があると思い、そのセクションに行きます。

この方法は特に、コードをグループ化して表示/使用するサードパーティにとって意味があり、使用されている順序に従うため、クラスで記述するコードはクラスと同じ構造に従います。

に関しては重要ですか?

読みやすくするために、間違いなく。

それ以外は、実際には、特定の言語でメソッドを呼び出すことができない場合にのみ、メソッドが呼び出された場所で上記で定義されている場合を除きます。


0

理想的には、クラスは非常に小さいので重要ではありません。ダースのメソッドしかなく、エディターまたはIDEが折りたたみをサポートしている場合、メソッドのリスト全体が12行に収まるため、問題はありません。

それに失敗すると、トップレベルの区別は、パブリックとプライベートになります。最初にパブリックメソッドをリストします。これらは、クラスが他のコードベースとインターフェイスする方法を定義するため、人々が最も探しているものです。

次に、これらのそれぞれの中で、機能ごとにメソッドをグループ化するのが最も理にかなっています:あるブロックのコンストラクターとデストラクター、別のブロックのゲッター/セッター、オペレーターのオーバーロード、静的メソッド、その他の束。C ++では、operator=コピーコンストラクタと密接に関連しているため、コンストラクタの近くに置きたいので、デフォルトのctor、copy ctor、operator =、dtorのすべて(またはなし)をすばやく見つけることができるようにしたいので存在します。


-1

tl; dr

使用している言語に特定の順序が必要な場合のみ。それ以外は、注文はあなた次第です。一貫性があり、理にかなっているシステムを選択し、可能な限りそれを使用するようにします。


1 順序は重要ですか?

この例のように、作業中の言語で、呼び出される場所よりもファイル内で前に定義された関数が必要な場合のみ

void funcA()
{
   funcB();
}

void funcB()
{
   //do something interesting...
}

funcB()使用する前に呼び出すため、エラーが発生します。これはPL / SQLの問題であり、Cの問題でもあると思いますが、次のような前方宣言を行うことができます。

void funcB();

void funcA()
{
   funcB();
}

void funcB()
{
   //do something interesting...
}

これは、順序が「間違っている」場合、コンパイルすることすらできないという唯一の状況です。

それ以外の場合は、いつでも好きなように並べ替えることができます。あなたはおそらくあなたのためにそれをするためのツールを書くことさえできます(あなたがそこにそれを見つけられないなら)。

2。「最高の」注文はありますか?

言語/環境に順序付けの要件がない場合、「最適な」順序が最適です。私にとっては、すべてのゲッター/セッターを一緒に、通常はクラスの先頭に(ただし、コンストラクター/静的初期化子の後に)、プライベートメソッド、保護、パブリックの順に配置するのが好きです。各スコープベースのグループには、通常順序付けはありませんが、オーバーロードされたメソッドはパラメーターの数の順序で一緒にしようとしています。また、関連する機能を持つメソッドを一緒に保持しようとしますが、そうするためにスコープベースの順序を壊す必要がある場合があります。また、スコープベースの順序を維持しようとすると、グループごとの機能が中断される場合があります。また、私のIDEではアルファベット順のアウトラインビューを表示できるので、それでも問題ありません。;)

C#などの一部の言語には、コンパイルには影響を与えないが、関連する関数をまとめてIDEで非表示/表示するのを容易にする「リージョン」にコードをグループ化する機能があります。以下のようMainMaが指摘し、この悪い習慣考えの方いくつかあります。この方法で使用される地域の良い例と悪い例を見てきましたので、この方法を使用する場合は注意してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.