クラスの大きさはどれくらいですか?


29

私は長年の開発者です(49歳)が、オブジェクト指向開発は初めてです。私はBertrand Meyer's Eiffel以来オブジェクト指向について読んでいますが、オブジェクト指向プログラミングはほとんどしていません。

ポイントは、オブジェクト指向設計に関するすべての本は、ボート、車、または私たちが頻繁に使用する一般的なオブジェクトの例から始まり、属性とメソッドを追加し、オブジェクトの状態をモデル化する方法と何ができるかを説明し始めることですそれ。

そのため、通常、「モデルが優れているほど、アプリケーション内のオブジェクトをより適切に表し、すべてがより優れている」というようなものになります。

これまでのところ非常に良いですが、一方で、「クラスは単一のページに収まる必要があります」などのレシピを提供する複数の著者を見つけました(「どのモニターサイズで?」コードを印刷します!)。

たとえば、PurchaseOrder動作を制御する有限状態マシンとのコレクションを持つクラスを考えてみましょう。PurchaseOrderItemここでの作業の引数の1つは、PurchaseOrderいくつかのメソッド(データクラス以上)を持つ単純なクラスを使用することです。PurchaseOrderFSM「エキスパートクラス」のためのハンドル有限状態マシンというPurchaseOrder

これは、コーディング・ホラーに関するJeff AtwoodのCode Smells投稿の「Feature Envy」または「Inappropriate Intimacy」分類に該当すると言えます。私はそれを常識と呼んでいます。実際の注文書を発行、承認、またはキャンセルできる場合、PurchaseOrderクラスにはissuePOapprovePOおよびcancelPOメソッドが必要です。

それは、私がオブジェクト指向の基礎として理解している「凝集を最大化する」と「結合を最小化する」という古くからの原則とは関係ないのでしょうか?

それに、それはクラスの保守性に役立ちませんか?


17
タイプ名をメソッド名にエンコードする理由はありません。あなたが持っている場合はつまりPurchaseOrder、あなたは自分のメソッドに名前を付けることができissueapprovecancelPOサフィックスは負の値を追加します。
ダッシュトムバン

2
ブラックホールメソッドを避けている限り、問題はありません。:)
マークC

1
私の現在の画面解像度では、145文字と言います。
DavRob60

皆さんのコメントに感謝します。彼らはこの問題を本当に助けてくれました。このサイトで許可されていれば、TMNとlazarusの回答も選択できますが、許可されるのは1つだけなので、Craigに行きました。宜しくお願いします。
ミゲルベロソ

回答:


27

クラスは単一責任原則を使用する必要があります。私が見たほとんどの非常に大規模なクラスは多くのことをします。各メソッドを見て、コードがこのクラスにあるか、別個の重複コードがヒントであるかを決定します。issuePOメソッドがありますが、たとえば10行のデータアクセスコードが含まれていますか?そのコードはおそらくそこにあるべきではありません。


1
クレイグ、私は単一責任の原則を知らず、ただそれについて読みました。私が最初に思いついたのは、責任の範囲は何ですか?POの場合、その状態を管理していると言えます。それがissuePO、approvePO、cancelPOメソッドの理由ですが、POアイテムの状態を処理するPurchaseOrderItemクラスとのやり取りも含まれています。したがって、このアプローチがSRPと一致しているかどうかはわかりません。あなたは何と言うでしょう?
ミゲルベロソ

1
非常に素晴らしく簡潔な答えのために+1。現実的には、クラスの大きさを決定する尺度はありません。SRPを適用すると、クラスが必要なだけの大きさになります
-S.ロビンズ

1
@MiguelVeloso-POの承認には、POの発行とは異なるロジックとルールが関連付けられている可能性があります。その場合、責任をPOApproverとPOIssuerに分けるのが理にかなっています。アイデアは、一方のルールが変更された場合、なぜ他方を含むクラスを混乱させたいのかということです。
マシューフリン

12

私の意見では、変数、関数、メソッドが問題のクラスに関連している限り、クラスのサイズは重要ではありません。


1
一方、サイズが大きいということは、これらのメソッドがおそらく問題のクラスに関連していないことを示しています。
ビリーONeal

5
@Billy:大規模なクラスはコードの匂いだと思いますが、自動的に悪くなるわけではありません。
デビッドソーンリー

@David:それが「おそらく」という言葉がそこにある理由です-それは重要です:)
ビリーONeal

私は同意しなければなりません...私はかなり一貫した命名規則を持つプロジェクトに取り組んでおり、物事はうまくレイアウトされています...しかし、私はまだ5万行のコードであるクラスを持っています。大きすぎる:)
ジェイ

2
1クラスの責務を制限していない場合は、クラスに関連になって、多くの変数、関数、およびメソッドがあるでしょう- - 「地獄への道は」行く正確にどのようにこの回答ショーや、このリードは簡単に持つ、あまりにもそれらの多く。
Doc Brown

7

大きなクラスの最大の問題は、それらのクラスのテスト、または他のクラスとの相互作用です。クラスの大きさ自体は問題ではありませんが、すべての匂いと同様に、潜在的な問題を示しています。一般的に、クラスが大きい場合、それはクラスが1つのクラスよりも多くのことをしていることを示しています。

あなたの車の類推では、クラスcarが大きすぎる場合、おそらくclass engine、class door、およびclassに分割する必要があることを示していますwindshield


8
そして、CarがVehicleインターフェースを実装することを忘れないでください。:-)
クリス

7

クラスが大きすぎるという特定の定義はないと思います。ただし、次のガイドラインを使用して、クラスをリファクタリングするか、クラスのスコープを再定義するかを決定します。

  1. 互いに独立した多くの変数がありますか?(私の個人的な寛容は、ほとんどの場合10〜20前後です)
  2. コーナーケース用に設計された多くの方法がありますか?(私の個人的な寛容は...まあ、それは私が推測する私の気分に大きく依存します)

1に関して、フロントガラスのサイズ、ホイールのサイズ、テールライトの電球モデル、およびクラスの他の100の詳細を定義するとしますcar。それらはすべて車に「関連」していますが、そのようなクラスの動作を追跡することは非常に困難carです。そして、いつの日か同僚が誤って(または、場合によっては意図的に)テールランプモデルに基づいてフロントガラスのサイズを変更した場合、フロントガラスがもはや車に収まらない理由を見つけるのに永遠に時間がかかります。

2に関しては、車がクラスcarで持つ可能性のあるすべての動作を定義しようとしますがcar::open_roof()、コンバーチブルでのみ使用でき、car::open_rear_door()2ドア車には適用されません。コンバーチブルと2ドア車は定義上「車」ですが、クラスにそれらを実装するとクラスがcar複雑になります。クラスは使いにくく、保守が難しくなります。

これらの2つのガイドライン以外に、クラススコープの明確な定義を提案します。スコープを定義したら、その定義に厳密に基づいてクラスを実装します。ほとんどの場合、スコープの定義が不十分なため、またはクラスに追加された任意の機能のために、人々は「大きすぎる」クラスを取得します


7

300行で必要なものを言う

注:この経験則は、「ファイルごとに1つのクラス」アプローチに従うことを前提としています。

絶対的なルールではありませんが、1つのクラスで200行を超えるコードが表示される場合、疑わしいです。300行と警告ベルが鳴ります。他の人のコードを開いて2000行を見つけたとき、最初のページを読まなくてもリファクタリングの時間であることがわかります。

ほとんどの場合、これは保守性にかかっています。オブジェクトが複雑で300行でその動作を表現できない場合、他の人が理解するのは非常に困難になります。

モデル-> ViewModel-> UIページの「動作オブジェクト」を作成するビューワールドでこのルールを曲げていることがわかりました。ページ。しかし、私はまだこのプログラミングモデルに慣れておらず、ページ/ビューモデル間でビヘイビアをリファクタリング/再利用する良い方法を見つけています。


私の個人的なガイドラインも約300行です。+1
マーシー

OPはヒューリスティックを探していると思いますが、これは良い方法です。
-neontapir

おかげで、ガイドラインとして正確な数字を使用することは価値があります。
ウィルシェパード

6

後ろに行きましょう:

それは、私がオブジェクト指向の基礎として理解している「凝集を最大化する」と「結合を最小化する」という古くからの原則とは関係ないのでしょうか?

実際、これはプログラミングの基礎であり、オブジェクト指向はパラダイムの1つにすぎません。

アントワーヌ・ド・サン=テグジュペリにあなたの質問に答えさせていただきます(私は怠け者だからです;)):

追加するものが何もないときではなく、奪うものが何もないとき、完璧が達成されます。

クラスが単純であるほど、正しいと確信できるほど、完全にテストするのが容易になります。


3

Feature Envy分類のOPに参加しています。他のクラスの内部で動作する多くのメソッドを持つ多くのクラスを持つことほど、私をいらいらさせることはありません。OOでは、データとそのデータに対する操作をモデル化することをお勧めします。これは、データとは異なる場所に操作を配置することを意味しません!データとそれらのメソッドを組み合わせて取得しようとしています。

carし、personモデルの普及ので、それがに、電気的接続を行う、あるいはスターターを回すためのコードを置くようになると思いますpersonクラス。これが経験豊富なプログラマにとって明らかに間違っていることを願っています。

理想的なクラスやメソッドのサイズは実際にはありませんが、メソッドと関数を1つの画面に収めておくと、すべてを1か所で見ることができます。(私は60行のウィンドウを開くようにVimを設定していますが、これはたまたま印刷されたページの行数とほぼ同じです。)これがあまり意味をなさない場所がありますが、私には役立ちます。クラス定義についても同じガイドラインに従い、ファイルを数ページの長さに抑えるようにします。300、400行を超えるものは扱いにくいと感じ始めます(主に、クラスが多くの直接的な責任を引き受け始めたためです)。


+1-良い経験則ですが、それについて権威主義的ではありません。ただし、クラスが壊れているからといって、異なるクラスがお互いのプライベートメンバーに触れている必要があるわけではありません。
ビリーONeal

異なるクラスがお互いのプライベートな部分に触れるべきではないと思います。「友人」アクセスは何かを起動して実行するのに便利ですが、(私にとっては)より良いデザインへの足がかりです。別のクラスの内部に依存するのは別のCode Smellであるため、一般的にはアクセサーも避けます。
ダッシュトムバン


1

クラスに対して単一の責任原則を使用することに完全に同意しますが、それが守られて大規模なクラスが生じる場合は、そうする必要があります。真の焦点は、個々のメソッドとプロパティが大きすぎないことであり、循環的複雑さはそこのガイドであり、それに続く多くのプライベートメソッドがクラス全体のサイズを増加させるが、それをきめ細かくレベルで読み取り可能およびテスト可能にする可能性があります。コードが読み取り可能なままであれば、サイズは関係ありません。


1

発注書の発行は、多くの場合、単なる発注書以上のものを含む複雑なプロセスです。この場合、ビジネスロジックを別のクラスに保持し、発注書をより単純にすることがおそらく正しいことです。ただし、一般的には正しいです。「データ構造」クラスは必要ありません。たとえば、「POManager」ビジネスクラスのissue()メソッドは、おそらくPOのissue()メソッドを呼び出す必要があります。その後、POはステータスを「発行済み」に設定して発行日を記録し、POManagerは買掛金勘定に通知を送信して請求書を受け取ることを知らせることができます。予定日。

メソッド/クラスサイズの「ルール」をプッシュする人は、実世界で十分な時間を費やしていないことは明らかです。他の人が述べたように、それはしばしばリファクタリングインジケータですが、時々(特に「データ処理」タイプの作業では)、500行以上にわたる単一のメソッドでクラスを書く必要があります。それに夢中にならないでください。


MVCパターンでは、POManagerが「コントローラー」、PurchaseOrderが「モデル」になると思いますか?
ミゲルベロソ

0

「クラスは1ページだけに収まるはずです」などのレシピを提供する著者をいくつか見つけました

クラスの物理的なサイズだけで言えば、大きなクラスを見ることはコードの匂いを示していますが、必ずしも匂いがあることを意味するわけではありません。(通常はそうですが)パイレーツオブカリビアンの海賊たちが言うように、これは実際のルールよりも「ガイドライン」と呼ぶものです。「クラス内のLOCが100を超えない」に厳密に従うことを一度試みた結果、不必要なオーバーエンジニアリングが大量に発生しました。

そのため、通常、「モデルが優れているほど、アプリケーション内のオブジェクトをより適切に表し、すべてがより優れている」というようなものになります。

私は通常これでデザインを始めます。このクラスは実世界で何をしますか?要件に記載されているように、クラスはこのオブジェクトをどのように反映すべきですか?ここに少し状態を追加し、そこにフィールド、それらのフィールドを操作するメソッドを追加し、さらにいくつか追加します!労働者階級があります。ここで、署名に適切な実装を入力します。これで問題ありません。これで、必要なすべての機能を備えた素晴らしい大きなクラスができました。しかし、これに関する問題は、現実の世界が機能する方法を考慮していないことです。そしてそれは、「変更」を考慮に入れていないことを意味します。

クラスをより小さな部分に分割することが好ましい理由は、既存のロジックに影響を与えたり、新しいバグや2つを意図せずに導入したり、すべてを再利用しにくくすることなく、後で大きなクラスを変更することが難しいためです。これは、リファクタリング、デザインパターン、SOLIDなどが実行される場所であり、最終結果は通常、他のより小さく、より細かいサブクラスで動作する小さなクラスです。

また、この例では、IssuePO、ApprovePO、Cancel POをPurchaseOrderクラスに追加するのは非論理的です。注文書の発行、承認、キャンセルは行われません。POにメソッドが必要な場合、メソッドはクラス全体ではなくその状態で動作する必要があります。実際には、他のクラスが動作する単なるデータ/レコードクラスです。


0

ジョブを実行するために必要なすべてのロジックを含むのに十分な大きさ。

維持可能であり、単一責任原則に従うのに十分なほど小さい。

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