39

コードの完全な本の段落について、私はやや混乱しています。

「避けるべきクラス」セクションには、次のように書かれています。

「動詞にちなんで名付けられたクラスを避ける 動作のみを持ち、データを持たないクラスは、一般的に実際にはクラスではありません。DatabaseInitialization() や StringBuilder() のようなクラスを他のクラスのルーチンに変えることを検討してください」

私のコードは、主にデータのない動詞クラスで構成されています。請求書リーダー、価格計算機、メッセージビルダーなどがあります。クラスをそれぞれ 1 つのタスクに集中させるためにこれを行います。次に、他の機能のために他のクラスに依存関係を追加します。

段落を正しく理解していれば、次のようなコードを使用する必要があります

class Webservice : IInvoiceReader, IArticleReader {
    public IList<Invoice> GetInvoices();
    public IList<Article> GetArticles();
}

それよりも

class InvoiceReader : IInvoiceReader {
    public InvoiceReader(IDataProvider dataProvider);
    public IList<Invoice> GetInvoices();
}

class ArticleReader : IArticleReader {
    public ArticleReader(IDataProvider dataProvider);
    public IList<Article> GetArticles();
}

編集 返信ありがとうございます。

私の結論は、私の現在のコードは OO よりも SRP ですが、「貧血ドメイン モデル」にも悩まされているということです。

これらの洞察は将来私を助けると確信しています。

4

10 に答える 10

20

InvoiceReader、PriceCalculator、MessageBuilder、ArticleReader、InvoiceReader などのクラス名は、実際には動詞名ではありません。それらは実際には「名詞エージェント-名詞」クラス名です。エージェント名詞を参照してください。

動詞クラス名は、Validate、Operate、Manage などのようなものになります。明らかに、これらはメソッドとして使用する方が適切であり、クラス名としては非常に厄介です。

「名詞-エージェント-名詞」クラス名の最大の問題は、クラスが実際に何をするかについてほとんど意味を持たないことです (例: UserManager、DataProcessor など)。その結果、肥大化して内部の結束を失う可能性が高くなります。(単一責任の原則を参照)。

したがって、IInvoiceReader および IArticleReader インターフェイスを持つ WebService クラスは、おそらくより明確で意味のある OO 設計です。

これにより、単純で明白な名詞クラス名「WebService」と、WebService クラスが呼び出し元に対して何ができるかを明確に宣伝する「名詞エージェント名詞」インターフェース名が得られます。

おそらく、PaymentWebService などの別の名詞を前に付けることで、実際のクラスにより多くの意味を与えることもできます。

ただし、インターフェイスは、クラスが呼び出し元に対して何ができるかをより具体的に説明するという点で、単一のクラス名よりも常に優れています。クラスがより複雑になるにつれて、意味のある名前を付けた新しいインターフェイスを追加することもできます。

于 2010-01-19T16:13:49.440 に答える
11

私は個人的にこの「ルール」を無視します。.NET Framework自体は、「動詞」クラスでいっぱいです。、、、、、、、、、、、TextReader...フレームワークの設計が不十分だと思われる場合は、このような名前を使用しないでください。BinaryWriterXmlSerializerCodeGeneratorStringEnumeratorHttpListenerTraceListenerConfigurationManagerTypeConverterRoleProvider

スティーブの意図は理解できます。特定のタスクを実行するためだけに数十のクラスを作成していることに気付いた場合は、貧血ドメインモデルの兆候である可能性がありますが、これらのことを自分で実行できるはずのオブジェクトはそうではありません。しかし、ある時点で、「純粋な」OOPとSRPのどちらかを選択する必要があります。

私の提案は次のとおりです。単一の「名詞」クラスに作用する「動詞」クラスを作成していることに気付いた場合は、「名詞」クラスがそれ自体でアクションを実行できるかどうかを正直に考えてください。ただし、動詞クラスを回避することのみを目的として、神オブジェクトの作成を開始したり、意味のない/誤解を招くような名前を思いついたりしないでください。

于 2010-01-19T16:06:22.890 に答える
5

盲目的にアドバイスに従わないでください。これらは単なるガイドラインです。

とはいえ、論理オブジェクトをモデル化する限り、名詞は非常に優れたクラス名になります。「Person」クラスはすべての「Person」オブジェクトの設計図であるため、「Person」と呼ぶのは非常に便利です。なぜなら、次のような推論が可能になるからです。「ユーザーの入力から Person を作成しますが、最初にそれを検証するために…」

于 2010-01-19T16:24:12.047 に答える
3

「避ける」という言葉の使用に注意してください。あなたがそれらを使用したとしても、それは地獄で排除したり、根絶したり、燃やしたりすることはありません。

著者が意味するのは、すべて動詞にちなんで名付けられたクラスがたくさんあり、それらのクラスを静的に作成し、1つの関数を呼び出してそれらを忘れるだけの場合は、おそらく少し分離していることを示しています。クラスの懸念が少し多すぎます。

ただし、同じアクションに対して異なる戦略がある場合など、アクションを実装するためのクラスを作成することが良い場合があります。非常に良い例はIComparer<>です。2つのことを比較するだけですが、いくつかの方法で比較できます。

著者が示唆したように、そのような場合、それを行うための良い方法は、インターフェースを作成して実装することです。IComparer<>が再び頭に浮かびます。

もう1つの一般的な状況は、ファイルのロードなど、アクションの状態が重い場合です。状態をクラスにカプセル化することが正当化される可能性があります。

于 2010-01-19T16:07:08.093 に答える
2

基本的にこの本が言っていることは、OO 設計はオブジェクト (名詞) を抽出し、それらのオブジェクト上およびオブジェクト間で発生する操作 (動詞) を識別することであるということです。

名詞はオブジェクトになり、動詞はこれらのオブジェクトを操作するメソッドになります。

アイデアは、

プログラムが現実世界の問題をモデル化するほど、プログラムはより良くなります。

実際には、オブジェクトの便利な点は、特定の状態を表すことができることです。次に、このクラスのいくつかの異なるインスタンスをそれぞれ異なる状態に保持して、問題のある側面を表すことができます。

InvoiceReader クラスの場合

  • 作成するインスタンスは 1 つだけです
  • それが表す唯一の状態は、dataProvider を含む状態です。
  • 1つのメソッドのみが含まれています

オブジェクトに配置する利点はありません。

于 2010-01-19T15:42:53.423 に答える
1

ステートメント動作のみを持ち、データを持たないクラスは、通常、実際にはクラスではありません。は明らかに間違っています。

動作を別のクラスに抽出することは、リファクタリングでよく行われる一般的なことです。状態を持つことができますが、持つ必要もありません。きれいなインターフェースを用意し、必要に応じて実装する必要があります。

また、ステートレス クラスは、短期間だけ必要な計算に最適です。それらをインスタンス化 (または、それらを取得するために何らかの種類の Factory を要求) し、必要な計算を実行してから、それらをガベージにスローします。適切な「バージョン」の動作をいつでもどこでも利用できるようにすることができます。

通常、インターフェイスのさまざまな実装には何らかの状態 (たとえば、コンストラクターで設定) があることがわかりますが、クラスの型によってその動作が完全に決定される場合があります。

例えば:

public interface IExporter
{
    /// <summary>
    /// Transforms the specified export data into a text stream.
    /// </summary>
    /// <param name="exportData">The export data.</param>
    /// <param name="outputFile">The output file.</param>
    void Transform(IExportData exportData, string outputFile);
}

として実装することができます

class TabDelimitedExporter : IExporter { ... }
class CsvExporter : IExporter { ... }
class ExcelExporter : IExporter { ... }

(それが何であれ) CSV ファイルへのエクスポートを実装するIExportDataには、おそらく状態はまったく必要ありません。ExcelExporter一方、エクスポート オプションのさまざまなプロパティを持つことができますが、ステートレスにすることもできます。

[編集]

GetInvoicesクラスに移動GetArticlesするWebServiceということは、それらの実装を WebService タイプに結び付けることを意味します。それらを別々のクラスにすることで、請求書と記事の両方に異なる実装を持たせることができます。全体として、それらを別々にした方が良いようです。

于 2010-01-19T16:32:41.393 に答える
0

オブジェクト指向における「動詞 vs 名詞」の典型的な例を次に示します。

http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html

于 2010-01-21T10:36:48.443 に答える
0

この本は、次のようなデザインを提案していると思います。

class Article : IIArticleReader
{
    // Article data...

    public IList<Article> GetArticles(); 
}
于 2010-01-19T15:50:04.280 に答える
0

名前にあまり焦点を当てません。名前に関する規則は、悪い習慣を示す経験則にすぎません。重要な点は次のとおりです。

動作のみを持ち、データを持たないクラスは、通常、実際にはクラスではありません

あなたの場合、クラスにはデータと動作の両方があり、「請求書」と「記事」と呼ばれているように見えます。

于 2010-01-19T15:42:01.243 に答える
0

場合によります。クラスは、読み取りまたは書き込みを行うデータ ソースへの接続を作成、維持、および表すため、Read 動詞と Write 動詞にちなんで名付けられたクラスが多数存在します。あなたのクラスがそうしているなら、それらを別々にしておくのがおそらく最善です。

Reader オブジェクトに解析ロジックが含まれているだけの場合は、クラスをユーティリティ メソッドに変換するのが適切な方法です。ただし、Webservice よりもわかりやすい名前を付けたいと思います。

于 2010-01-19T15:42:21.130 に答える