[英]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框架本身就充滿了“動詞”類: TextReader
, BinaryWriter
, XmlSerializer
, CodeGenerator
, StringEnumerator
, HttpListener
, TraceListener
, ConfigurationManager
, TypeConverter
, RoleProvider
......如果你認為框架設計不當,然后通過各種手段,不要使用這些名稱。
史蒂夫的意圖是可以理解的。 如果你發現自己只是為了執行一項特定的任務而創建了幾十個類,這可能是一個貧血領域模型的標志, 應該能夠自己做這些事情的對象不是。 但在某些時候,你必須在“純粹的”OOP和SRP之間做出選擇。
我的建議是這樣的:如果你發現自己創建一個“動詞”類作用於單個“名詞”類,請誠實地思考“名詞”類是否可以自己執行動作。 但是,不要開始創造God Objects,或者為了避免使用動詞類而提出毫無意義/誤導性的名稱。
不要盲目聽從任何建議。 這些只是指導方針。
也就是說,只要名詞模擬邏輯對象, 名詞就會成為非常好的類名 。 由於“Person”類是所有“Person”對象的藍圖,因此將其稱為“Person”非常方便,因為它允許您這樣推理:“我將根據用戶的輸入創建Person,但首先我需要驗證它......“
請注意使用“避免”一詞。 如果你曾經使用它,它不會消除,根除或在地獄中燃燒。
作者的意思是,如果你發現自己有一堆所有以動詞命名的類,你所要做的就是靜態創建類,調用一個函數而忘記它們,這可能是你分開一點點的標志有點太多關注。
但是,有些情況下,創建實現操作的類是一件好事,例如當您對同一操作有不同的策略時。 一個很好的例子是IComparer <>。 它只是比較兩件事,但有幾種比較方法。
正如作者所建議的那樣,在這些情況下,一個好方法是創建一個接口並實現它。 IComparer <>再次浮現在腦海中。
另一種常見情況是當操作處於繁重狀態時,例如加載文件。 將狀態封裝在類中可能是合理的。
本書的基本內容是OO設計是關於提取對象(名詞)和識別在這些對象之間和之間發生的操作(動詞)。
名詞成為對象,動詞成為對這些對象進行操作的方法。
這個想法是
越接近程序模擬現實世界的問題,程序就越好。
實際上,對象的有用之處在於它可以表示特定的狀態。 然后,您可以擁有此類的幾個不同實例,每個實例都持有不同的狀態來表示問題的某些方面。
在InvoiceReader類的情況下
將它放在一個物體中沒有任何好處。
語句只有行為但沒有數據的類通常不是一個類。 是完全錯的。
將行為提取到單獨的類中是重構中的一個好常見的事情。 它可以有狀態,但也不需要有狀態。 您需要擁有干凈的界面,並在您認為必要時實施它們。
此外,無狀態類非常適合您在短時間內進行的計算。 您實例化它們(或者,請求某種類型的工廠來獲取它們),進行必要的計算,然后將它們扔到垃圾箱中。 您可以隨時隨地獲得適當的行為“版本”。
通常我發現接口的不同實現具有某種狀態(例如,在構造函數中設置),但有時類的類型可以完全確定它的行為。
例如:
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
可以具有導出選項的各種屬性,但也可以是無狀態的。
[編輯]
將GetInvoices
和GetArticles
移動到WebService
類意味着您將其實現與WebService類型聯系起來。 將它們放在單獨的類中將允許您對發票和文章進行不同的實現。 總的來說,將它們分開似乎更好。
更少關注名稱。 關於名稱的規則只是一個不良做法的經驗法則指標。 重點是:
只有行為但沒有數據的類通常不是一個類
在您的情況下,看起來您的類同時具有數據和行為,並且它們也可以稱為“發票”和“文章”。
這取決於。 許多類以Read和Write動詞命名,因為這些類還創建,維護和表示與它們正在讀取或寫入的數據源的連接。 如果您的課程正在這樣做,最好將它們分開。
如果Reader對象只包含解析邏輯,那么將類轉換為實用方法是可行的方法。 不過,我會使用比Webservice更具描述性的名稱。
我認為這本書提出了如下設計:
class Article : IIArticleReader
{
// Article data...
public IList<Article> GetArticles();
}
這是OO中'動詞與名詞'的經典對象:
http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.