[英]Delegate Method or Abstract Class
這是我的計划:
class Program
{
//DESIGN 1
abstract class AFoo
{
public string Bar { get; set; }
public abstract string SayHi();
}
class LoudFoo : AFoo
{
public override string SayHi()
{
return this.Bar.ToUpper();
}
}
class QuietFoo : AFoo
{
public override string SayHi() { return this.Bar.ToLower(); }
}
//DESIGN 2
class Foo{
public string Bar { get; set; }
public Func<Foo, string> SayHi { get; set; }
}
static void Main(string[] args)
{
//USING DESIGN 1
var quietFoo2 = new QuietFoo{ Bar = "Mariane"};
var loudFoo2 = new LoudFoo{ Bar = "Ginger"};
Console.WriteLine(quietFoo2.SayHi());
Console.WriteLine(loudFoo2.SayHi());
//USING DESIGN 2
var quietFoo = new Foo
{
Bar = "Felix",
SayHi = (f) => { return f.Bar.ToLower(); }
};
var loudFoo = new Foo
{
Bar = "Oscar",
SayHi = (f) => { return f.Bar.ToUpper(); }
};
Console.WriteLine(quietFoo.SayHi(quietFoo));
Console.WriteLine(loudFoo.SayHi(loudFoo));
}
}
我可以完成“同樣的事情” - 實際上不完全相同的事情,但類似的事情走兩條不同的路線。
設計1)我可以創建一個抽象類,強制該類的實現者如何SayHi()
- 要么 -
設計2)我可以創建一個類來定義一個SayHi屬性,它是一個函數。 (我稱之為代表 - 但我不確定這是否是正確的術語)
設計1困擾我,因為它可以導致課程的豐富
然而....
設計2困擾我,因為當我必須讓Foo實際上是SayHi()時感覺多余。
felix.SayHi(felix)
我的問題是使用Design 1還是Design 2--或者兩者都不是更好。 當我說得更好時,我說的是能夠維持我的程序更實用。 當我創建不同的類時,我遇到了這個問題,這些類將用於從不同的雲API(Google Drive,Box.com,DropBox)下載文件 - 起初我創建了單獨的類,但后來我走了另一條路。
當談到這些類型的設計選擇時,我發現根據您嘗試建模的問題域來考慮對象會有所幫助。 您已經將LoudFoo和QuietFoo顯示為單個行為不同,但這是一個故意簡化的示例。 在實際系統中,您可能有令人信服的理由將兩個對象視為概念上不同的。
在前一版本中,SayHi是類行為的一個內在部分,如果該行為的性質以某種方式與其內部狀態相互作用,這是恰當的。 也許SayHi的實現依賴於特定於派生類類型的對象的屬性。
在后一版本中,SayingHi更像是一個可以分發給各種實例的工具。 當沒有其他理由來區分不同類型的Foo實例時,這是合適的。
Stream是前一種模式的一個很好的例子,它提供的各種方法對於流操作的本質是固有的。 各種派生類將使用不同的狀態來實現其方法。
Comparer是后一種模式的一個很好的例子,其中許多不同的對象類型想要使用比較的概念來操作。 除了想要使用此特定類型的行為之外,使用此功能的類不需要具有任何其他共同點。
關於提出這個問題的具體應用,那么多類方法又有什么尷尬? 如果存在冗余蔓延,則可能表明可以以更好地模擬問題的不同方式考慮責任。 如果不知道有關特定問題的其他詳細信息,很難說更多,但很可能一個好的方法是你提出的兩個方法的組合,一些單個類負責操作的排序和一個單獨的層次結構(或一組接口實現) )實施特定於每項服務的操作。 本質上,接口(或基類)將分別傳遞的所有各種代理組合在一起。 這類似於StreamReader如何獲取Stream並使用在Stream上運行的其他行為來增強它。
在設計1中,您的行為是在類中實現的,但在設計2中,您要求調用者定義行為。
我傾向於設計1,因為它使行為實現在類中保持黑盒子。 只要有人實例化一個新對象,設計2就可以改變你的實現。 我也不喜歡實現是調用者的責任。
如果您如何實現SayHi
更改,您只需在設計1中更改一個地方,但如果您使用設計2,則可能在您的代碼中有多個位置可以更改它。
根據經驗:較少的代碼==更易於維護。
在具體的情況下,你也有一個脫鈎的設計 - 如何將SayHi
與說出它的類分開的邏輯,讓你可以選擇組合行為。 低耦合也是通常要維護的代碼的標志。
我傾向於第二種設計。
第一個設計更標准,邏輯是一致的(意味着任何其他使用LoudFoo
(或QuietFoo
)的類在任何地方都會有相同的結果。但是,它是可重用的,但僅限於其繼承的路徑。表示來自LoudFoo
子類(比如DerivedLoudFoo
不能使用QuietFoo
定義的SayHi
邏輯)。
這可能聽起來很簡單,但以后可能會很麻煩。 你可以在這里閱讀我的答案,了解真實案例。
第二個是更可擴展的,但缺點是它可能有不同的行為。 不要將其用於核心業務流程(例如插入/更新/刪除),因為它很難調試或修改。 但是,對於某些方法(如OnAfterInsert
, OnAfterSubmit
,最好在Framework
級別中使用。
假設這不僅僅是一個完全構成的例子,而且實際上可以被翻譯成真正的代碼(我很難弄清楚它可能是什么),我發現選項2很糟糕。
你幾乎可以給SayHi
分配任何東西,包括一個與Bar
無關的lambda,這似乎不是你原來的意圖。
你基本上是想把一個精心制作的功能釘掛在一個好的舊的面向對象的洞里。 使用lambda將數據( Bar
)與在其上運行的行為分開,這是有效的功能實踐,但是通過使Foo.SayHi
成為屬性,您將回到OO樣式,嘗試將其中的兩個封裝回到同班。 似乎有點做作。
設計2困擾我,因為當我必須讓Foo實際上是SayHi()時感覺多余。
如果將Foo
類重新定義為
class Foo
public property Bar as string
public property SayHi as func(of string)
end class
然后可以使用閉包來創建和調用SayHi
函數,而不將Foo
作為函數參數傳遞:
dim Bar = "Felix"
dim Felix as new Foo with {
.Bar = Bar,
.SayHi = function() Bar.toLower
}
dim FelixSays = Felix.SayHi()
我傾向於設計1,因為它使行為實現在類中保持黑盒子。
設計2總是准備好進行黑盒行為實現,例如在工廠方法中:
function CreateQuietFoo(Bar as string) as Foo
return new Foo with {
.Bar = Bar,
.SayHi = function() Bar.toLower
}
end function
dim Felix = CreateQuietFoo("Felix")
dim Oscar = CreateQuietFoo("Oscar")
這樣調用者不必提供SayHi
方法來創建一個安靜的Foo
實例,他只是使用CreateQuietFoo
工廠方法。
我的問題是使用Design 1還是Design 2--或者兩者都不是更好。
如果您更喜歡Composition over Inheritance,請使用Design 2。 它使代碼更靈活。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.