2012-09-11 41 views
6

我想我會在我的代碼中使用一個案例研究作爲我的意思的例子。.Net中的接口繼承/層次結構 - 有更好的方法嗎?

現在,我正在研究基於組件的遊戲系統的行爲/操作系統。 GameObjects可以有一個IBehaviorComponent以及附屬於他們的IActionComponent,其中,只顯示相關細節,暴露了以下內容:

public interface IBehaviorComponent : IBaseComponent 
{ 
    IBehavior Behavior { get; } 
} 

public interface IActionComponent : IBaseComponent 
{ 
    IAction Action { get; } 
    void ExecuteAction(IGameObject target = null); 
} 

現在,這是所有罰款至今(至少對我來說!)。但是,當我查看IActionComponent的實現時,麻煩開始釀成。

例如,一個簡單的IActionComponent實現:

public class SimpleActionComponent : IActionComponent 
{ 
    public IAction Action { get; protected set; } 

    public void ExecuteAction(IGameObject target = null) 
    { 
     Action.Execute(target); 
    } 

    public void Update(float deltaTime) { } //from IBaseComponent 
} 

但是,讓我們說,我想介紹一個更復雜的IActionComponent實現,它允許在一個定時的時間表來執行的動作:

public class TimedActionComponent : IActionComponent 
{ 
    public IAction Action { get; protected set; } 

    public float IdleTime { get; set; } 

    public float IdleTimeRemaining { get; protected set; } 

    public void ExecuteAction(IGameObject target = null) 
    { 
     IdleTimeRemaining = IdleTime; 
    } 

    public void Update(float deltaTime) 
    { 
     if (IdleTimeRemaining > 0f) 
     { 
     IdleTimeRemaining -= deltaTime; 
     } 
     else 
     { 
     Action.Execute(target); 
     } 
    } 
} 

現在,我們假設我想公開IdleTime,以便可以通過外部影響來改變它。我起初的想法是創建一個新的接口:

public interface ITimedActionComponent : IActionComponent 
{ 
    float IdleTime { get; set; } 

    float IdleTimeRemaining { get; set; } 
} 

然而,這裏的問題是,我的組件系統存儲從IBaseComponent一個級別了一切。因此,GameObject的操作組件被檢索爲IActionComponent,而不是像ITimedActionComponent或IRandomizedActionComponent,甚至是ICrashTheProgramActionComponent。我希望這個原因很明顯,因爲我希望能夠查詢GameObject的某個組件,而不必知道組件的基本類型(IActionComponent,IRenderableComponent,IPhysicsComponent等)的確切需求。

是否有一個更簡潔的方式來處理這個問題,這將允許我公開在子類中定義的這些屬性,而不必將所有檢索到的IActionComponent轉換爲它感興趣的類型?或者,這只是實現這一點的唯一/最好的方式。喜歡的東西:

public void IWantYouToAttackSuperSlow(IGameObject target) 
{ 
    //Let's get the action component for the gameobject and test if it's an ITimedActionComponent... 
    ITimedActionComponent actionComponent = target.GetComponent<IActionComponent>() as ITimedActionComponent; 

    if (actionComponent != null) //We're okay! 
    { 
     actionComponent.IdleTime = int.MaxValue; //Mwhaha 
    } 
} 

現在,我想這是必由之路,但我想我會看看有沒有躲在我是無知的,或木製品的模式,如果任何人都可以提出一個更好的方法來完成這個開始。

謝謝!

+0

我的另一個想法是內部使用另一個對象,如IActionComponentExecutor,這將決定何時調用Action.Execute(),但我認爲這讓我回到了圓桌會議的方式 - 外部對象將不得不追蹤IActionComponentTimedExecutor(哦〜我們現在正在開始enterprise-y !)更改IdleTime。 – Terminal8

+0

出於興趣 - ActionComponent.IdleTime是否存儲在某種通用包中?如果是這樣,你可以在你的基本界面上使用'應用(BagData bag)'和'存儲(BagData bag)'方法。也就是說,您可能還需要考慮一些元數據類型系統,您可以使用它來查詢屬性,但需要知道派生類型。 「TypeDescriptor」是物業網格等東西使用的現成捲起點。 –

+0

不,目前不是。我對這個概念並不完全熟悉 - 有沒有可能提供的參考? – Terminal8

回答

0

你說得對,通過轉換爲子類型來測試特定的實現並不是一個好主意。從你提供的例子看來,你有一個IGameObject,你想執行一些操作。而不是讓你的IActionComponent公開一種以IGameObject作爲參數的方法,而不是讓IGameObject採取一種或多種方式(如果您願意的話),也可以採用其他方式來做,例如可以執行的操作。

interface IActionComponent 
{ 
    ExecuteAction(); 
} 

interface IGameObject 
{ 
    IActionComponent ActionComponent { get; } 
} 

然後在執行的方法做

public void IWantYouToAttackSuperSlow(IGameObject target) 
{ 
    target.ActionComponent.ExecuteAction(); 
} 

取決於你如何實現IActionComponent,多態性將確保正確的代碼被執行。

有一種叫做Command Pattern的圖案,似乎符合你的需要。

+0

啊,我在這裏提供的案例研究只是我正在運行的這個接口繼承問題的一個實例。當我開發一個基於組件的系統時,IGameObject僅僅是一個極其簡單的組件容器,絕對沒有關於任何「真實」組件的邏輯或知識。實質上,它只是一個GUID,它允許覆蓋給定「實體」的組件綁定到該「實體」(但我已將該概念封裝在接口/類中以提供簡單的組件檢索)。 – Terminal8

+0

@Icelight:我無法完全理解你的架構。然而,你的句子*「IGameObject只是一個非常簡單的組件容器,絕對沒有邏輯......」*聽起來很奇怪。每當我聽說一門「應該只是一包屬性,沒有任何邏輯」的課程,我就會想起我已經學習了關於面向對象編程的第一件事:它將**行爲**放在**數據**是。所以,在我看來,無邏輯類有點代碼味道。 –

+0

@sJhonny:我明白你在做什麼。然而,在這種情況下,爲了我的方便,我們使用了一個GameObject,而不是需要。在一個基於「真實」組件的遊戲系統中,所有組件都將在漂亮的大型組件雲中浮現(或者,更可能的是,坐在專用於其組件類型的管理器中),只有像GUID綁定的組件類型一起去同一個實體。我沒有采用這種方法,而是使用一個包含同一實體的所有組件的GameObject - 方便,因爲這種方法更容易爲我自己顯示。 – Terminal8

1

你的代碼表明,投射下的IActionComponentITimedActionComponent是(據我可以看到)無可避免時,你必須要知道的IdleTime特性,以便使用它們,對不對?

我認爲這裏的訣竅是隱藏那個不太好看的代碼,其中使用你的IActionComponent的類不需要處理它。
我如何做到這一點首先想到的是用一個Factory

public void IWantYouToAttackSuperSlow(IGameObject target) 
{ 
    //Let's get the action component for the gameobject 
    IActionComponent actionComponent = ActionComponentsFactory.GetTimedActionComponentIfAvailable(int.MaxValue); 
} 

的,工廠的方法:

public IActionComponent GetTimedActionComponentIfAvailable(IGameObject target, int idleTime) 
{ 
var actionComponent = target.GetComponent<IActionComponent>() as ITimedActionComponent; 

    if (actionComponent != null) //We're okay! 
    { 
     actionComponent.IdleTime = int.MaxValue; //Mwhaha 
    } 
return (actionComponent != null)? actionComponent : target.GetComponent<IActionComponent>(); 
} 
+0

關於你的第一句話 - 我確實同意,我看不出有任何真正的方法。我想我正在釣魚/祈禱一些可以修復一切的奇蹟建築。但是我想我們還沒有完成;)對於Factory方法,我喜歡這個方法,至少可以幫助清理一些東西。我將會看看這種方式與我目前的工作有多好。 – Terminal8