簡體   English   中英

要避免的類(代碼完成)

[英]Classes to avoid (code complete)

我對代碼完整書中的段落感到有些困惑。

在“要避免的類”一節中,它寫着:

“避免使用動詞命名的類只有行為但沒有數據的類通常不是一個類。考慮將類似DatabaseInitialization()或StringBuilder()的類轉換為其他類的例程”

我的代碼主要由沒有數據的動詞類組成​​。 有發票閱讀器,價格計算器,消息構建器等。我這樣做是為了將每個類集中到一個任務。 然后我將依賴關系添加到其他類以獲取其他功能。

如果我正確理解了段落,我應該使用代碼

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,但它也受到“貧血領域模型”的影響。

我相信這些見解將來會幫助我。

InvoiceReader,PriceCalculator,MessageBuilder,ArticleReader,InvoiceReader等類名實際上不是動詞名稱。 它們實際上是“名詞代理 - 名詞”類名。 代理商名詞

動詞類名稱將類似於Validate,Operate,Manage等。顯然,這些更好地用作方法,並且作為類名稱會非常笨拙。

“名詞代理 - 名詞”類名的最大問題在於它們對類的實際作用(例如UserManager,DataProcessor等)幾乎沒有意義。 結果他們更容易臃腫並失去內部凝聚力。 (參見單一責任原則 )。

因此,具有IInvoiceReader和IArticleReader接口的WebService類可能是更清晰,更有意義的OO設計。

這為您提供了簡單明了的名詞類名“WebService”,以及“名詞代理 - 名詞”接口名稱,它清楚地宣傳了WebService類可以為調用者做些什么。

您可能還可以通過為另一個名詞添加前綴來為實際類賦予更多含義,例如PaymentWebService。

但是,在更具體地描述類可以為調用者做什么時,接口總是比單個類名更好。 隨着類變得越來越復雜,新接口也可以添加有意義的名稱。

我個人無視這個“規則”。 .NET框架本身就充滿了“動詞”類: TextReaderBinaryWriterXmlSerializerCodeGeneratorStringEnumeratorHttpListenerTraceListenerConfigurationManagerTypeConverterRoleProvider ......如果你認為框架設計不當,然后通過各種手段,不要使用這些名稱。

史蒂夫的意圖是可以理解的。 如果你發現自己只是為了執行一項特定的任務而創建了幾十個類,這可能是一個貧血領域模型的標志, 應該能夠自己做這些事情的對象不是。 但在某些時候,你必須在“純粹的”OOP和SRP之間做出選擇。

我的建議是這樣的:如果你發現自己創建一個“動詞”類作用於單個“名詞”類,請誠實地思考“名詞”類是否可以自己執行動作。 但是,不要開始創造God Objects,或者為了避免使用動詞類而提出毫無意義/誤導性的名稱。

不要盲目聽從任何建議。 這些只是指導方針。

也就是說,只要名詞模擬邏輯對象, 名詞就會成為非常好的類名 由於“Person”類是所有“Person”對象的藍圖,因此將其稱為“Person”非常方便,因為它允許您這樣推理:“我將根據用戶的輸入創建Person,但首先我需要驗證它......“

請注意使用“避免”一詞。 如果你曾經使用它,它不會消除,根除或在地獄中燃燒。

作者的意思是,如果你發現自己有一堆所有以動詞命名的類,你所要做的就是靜態創建類,調用一個函數而忘記它們,這可能是你分開一點點的標志有點太多關注。

但是,有些情況下,創建實現操作的類是一件好事,例如當您對同一操作有不同的策略時。 一個很好的例子是IComparer <>。 它只是比較兩件事,但有幾種比較方法。

正如作者所建議的那樣,在這些情況下,一個好方法是創建一個接口並實現它。 IComparer <>再次浮現在腦海中。

另一種常見情況是當操作處於繁重狀態時,例如加載文件。 將狀態封裝在類中可能是合理的。

本書的基本內容是OO設計是關於提取對象(名詞)和識別在這些對象之間和之間發生的操作(動詞)。

名詞成為對象,動詞成為對這些對象進行操作的方法。

這個想法是

越接近程序模擬現實世界的問題,程序就越好。

實際上,對象的有用之處在於它可以表示特定的狀態。 然后,您可以擁有此類的幾個不同實例,每個實例都持有不同的狀態來表示問題的某些方面。

在InvoiceReader類的情況下

  • 你只會創建一個實例
  • 它所代表的唯一狀態是包含dataProvider的狀態
  • 它只包含一種方法

將它放在一個物體中沒有任何好處。

語句只有行為但沒有數據的類通常不是一個類。 是完全錯的。

將行為提取到單獨的類中是重構中的一個好常見的事情。 它可以有狀態,但也不需要有狀態。 您需要擁有干凈的界面,並在您認為必要時實施它們。

此外,無狀態類非常適合您在短時間內進行的計算。 您實例化它們(或者,請求某種類型的工廠來獲取它們),進行必要的計算,然后將它們扔到垃圾箱中。 您可以隨時隨地獲得適當的行為“版本”。

通常我發現接口的不同實現具有某種狀態(例如,在構造函數中設置),但有時類的類型可以完全確定它的行為。

例如:

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 { ... }

要實現從IExportData (無論可能是什么)導出到CSV文件,您可能根本不需要任何狀態。 另一方面, ExcelExporter可以具有導出選項的各種屬性,但也可以是無狀態的。

[編輯]

GetInvoicesGetArticles移動到WebService類意味着您將其實現與WebService類型聯系起來。 將它們放在單獨的類中將允許您對發票和文章進行不同的實現。 總的來說,將它們分開似乎更好。

更少關注名稱。 關於名稱的規則只是一個不良做法的經驗法則指標。 重點是:

只有行為但沒有數據的類通常不是一個類

在您的情況下,看起來您的類同時具有數據和行為,並且它們也可以稱為“發票”和“文章”。

這取決於。 許多類以Read和Write動詞命名,因為這些類還創建,維護和表示與它們正在讀取或寫入的數據源的連接。 如果您的課程正在這樣做,最好將它們分開。

如果Reader對象只包含解析邏輯,那么將類轉換為實用方法是可行的方法。 不過,我會使用比Webservice更具描述性的名稱。

我認為這本書提出了如下設計:

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

    public IList<Article> GetArticles(); 
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM