![](/img/trans.png)
[英]c# - How to do MULTIPLE “mixins” correctly with Interfaces and/or Abstract Classes
[英]C# has abstract classes and interfaces, should it also have “mixins”?
我經常遇到一個案例,我希望所有類的集合都擁有類似的邏輯。 例如,也許我希望Bird
和Airplane
能夠Fly()
。 如果您正在考慮“戰略模式”,我會同意,但即使采用策略,有時也無法避免重復代碼。
例如,讓我們說以下適用(這與我最近遇到的真實情況非常相似):
Bird
和Airplane
需要保存一個實現IFlyBehavior
的對象實例。 OnReadyToFly()
時, Bird
和Airplane
需要向IFlyBehavior
實例詢問Fly()
。 OnReadyToLand()
時, Bird
和Airplane
需要向IFlyBehavior
實例詢問Land()
。 OnReadyToFly()
和OnReadyToLand()
是私有的。 Bird
繼承了Animal
和Airplane
繼承了PeopleMover
。 現在,假設我們稍后添加了Moth
, HotAirBalloon
和其他16個對象,讓我們說它們都遵循相同的模式。
我們現在需要20份以下代碼:
private IFlyBehavior _flyBehavior;
private void OnReadyToFly()
{
_flyBehavior.Fly();
}
private void OnReadyToLand()
{
_flyBehavior.Land();
}
我不喜歡這兩件事:
它不是很干(相同的九行代碼一遍又一遍地重復)。 如果我們發現了一個錯誤或將BankRight()
添加到IFlyBehavior
,我們需要將更改傳播到所有20個類。
沒有任何方法可以強制所有20個類一致地實現這種重復的內部邏輯。 我們不能使用接口,因為接口只允許公共成員。 我們不能使用抽象基類,因為對象已經繼承了基類,並且C#不允許多重繼承(即使這些類還沒有繼承類,我們以后可能希望添加一個實現的新行為,比如ICrashable
,所以抽象基類並不總是一個可行的解決方案。
如果...?
如果C#有一個新的構造,比如pattern
或template
或[在這里填寫你的想法],它就像一個接口,但允許你在成員上放置私有或受保護的訪問修飾符怎么辦? 您仍然需要為每個類提供一個實現,但是如果您的類實現了PFlyable
模式,那么您至少應該有一種方法來強制每個類都有必要的樣板代碼來調用Fly()
和Land()
。 而且,使用像Visual Studio這樣的現代IDE,您將能夠使用“實現模式”命令自動生成代碼。
就個人而言,我認為只是擴展界面的含義以涵蓋任何合同,無論是內部(私人/受保護)還是外部(公共)更有意義,但我建議首先添加一個全新的結構,因為人們似乎非常堅定關於“界面”一詞的含義,我不希望語義成為人們答案的焦點。
問題:
無論你怎么稱呼它,我想知道我在這里建議的功能是否有意義。 我們是否需要某種方法來處理我們無法抽象出我們想要的代碼的情況,因為需要限制性訪問修飾符或者程序員無法控制的原因?
更新
根據AakashM的評論,我相信我要求的功能已有一個名稱:一個Mixin 。 所以,我想我的問題可以簡化為:“C#應該允許Mixins嗎?”
您描述的問題可以使用訪問者模式解決(一切都可以使用訪問者模式解決,所以要小心!)
訪問者模式允許您將實現邏輯移向新類。 這樣,您不需要基類,並且訪問者在不同的繼承樹上工作得非常好。
總結一下:
有關參考,請參閱訪客模式
Visual Studio已經在“窮人形式”中提供了代碼片段。 此外,使用重構工具la ReSharper(甚至可能是Visual Studio中的本機重構支持),您可以在確保一致性方面獲得很長的成功。
[編輯:我沒有想到擴展方法,這種方法更進一步(你只需要將_flyBehaviour保持為私有變量)。 這使我的答案的其余部分可能已經過時了......]
然而; 只是為了討論:如何改進? 這是我的建議。
可以想象未來版本的C#編譯器將支持以下內容:
// keyword 'pattern' marks the code as eligible for inclusion in other classes
pattern WithFlyBehaviour
{
private IFlyBehavior_flyBehavior;
private void OnReadyToFly()
{
_flyBehavior.Fly();
}
[patternmethod]
private void OnReadyToLand()
{
_flyBehavior.Land();
}
}
您可以使用以下內容:
// probably the attribute syntax can not be reused here, but you get the point
[UsePattern(FlyBehaviour)]
class FlyingAnimal
{
public void SetReadyToFly(bool ready)
{
_readyToFly = ready;
if (ready) OnReadyToFly(); // OnReadyToFly() callable, although not explicitly present in FlyingAnimal
}
}
這會有所改善嗎? 大概。 是不是真的值得嗎? 也許...
我們不能使用擴展方法
public static void OnReadyToFly(this IFlyBehavior flyBehavior)
{
_flyBehavior.Fly()
}
這模仿了你想要的功能(或Mixins)
我將使用擴展方法來實現代碼顯示的行為。
讓Bird
與Plane
對象實現了產權IFlyBehavior
對象的接口IFlyer
public interface IFlyer { public IFlyBehavior FlyBehavior } public Bird : IFlyer { public IFlyBehaviour FlyBehavior {get;set;} } public Airplane : IFlyer { public IFlyBehaviour FlyBehavior {get;set;} }
為IFlyer
創建擴展類
public IFlyerExtensions { public void OnReadyToFly(this IFlyer flyer) { flyer.FlyBehavior.Fly(); } public void OnReadyToLand(this IFlyer flyer) { flyer.FlyBehavior.Land(); } }
你能通過在.NET 4.0中使用新的ExpandoObject來獲得這種行為嗎?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.