2016-04-13 30 views
0

我有這樣的基本接口,它描述了卡球員的行爲(人力和AI):如何聲明未指定的方法的參數在C#

interface ICardPlayer<T> 
    where T: Carta, new() 
{ 
    // some methods here 
    T Pop(UNSPECIFIED ARGUMENTS); 
} 

流行功能允許玩牌的人從他的甲板棄一張牌,但在這個層面上,我不知道這個玩家是一個人類玩家還是一個AI玩家。 如果是人類玩家,方法將是T Pop(uint index);,但如果它是AI玩家,方法將是T Pop()。在這種情況下,該方法必須沒有參數,因爲AI玩家的Pop功能會調用AI的方法來丟棄正確的卡片。 所以我也有這兩個接口:

interface IHumanCardPlayer<T> : ICardPlayer<T> 
    where T: Carta, new() 
{ 
    // some methods here 
    T Pop(uint index); 
} 

interface IAICardPlayer<T> 
    where T: Carta, new() 
{ 
    // some methods here 
    T Pop(); 
} 

我沒有讓所有2種方法:如果玩家是一個人的球員,他有打電話給Pop方法給它卡的索引他會拋棄它,而且他不能在沒有參數的情況下調用該方法。 同樣是如果它是一個AI玩家:他必須調用Pop方法而不給它任何參數,並且他不能調用方法Pop(index)

那麼,有沒有辦法在ICardPlayer<T>接口中編寫Pop(UNSPECIFIED ARGUMENTS)或者我是否必須編寫2個不同的Pop方法而不使用繼承?

+1

您可以將默認值分配給參數並檢查該值。例如'T Pop(uint index = -1);' – Wjdavis5

+0

接口的要點是隱藏實現的細節並確保每個實例都具有相同的API。爲什麼不用'T Pop(uint index)'定義接口,並且簡單地忽略AI中的參數。 – Toxantron

+0

或使它成爲可空'T Pop(uint?index);' – Wjdavis5

回答

3

這會破壞接口的用途,因爲調用方法是不可能的(任何可能的調用都可能對特定的子類有錯誤的參數)。

你不能那樣做。

+0

我覺得「不能」是不正確和誤導。你當然可以完成OP想要做的事情。它可能不適合'正確'的範例。但在那一點上,它頗有見地。 – Wjdavis5

3

首先,你沒有使用繼承。這不一定是好的或壞的。

其次,你不能,這是一件好事。

接口代表某種公開接口,它公開訪問一組通用功能。一般來說,接口的任何實現應該是同樣有效的 - 它們應該是可以互換的,並且使用這些接口的代碼不應該關心給出的具體實現。很明顯,你的情況並非如此 - 你想根據你正在執行的具體的實現來調用具有不同參數的接口。這與首先​​使用接口(和繼承)的整個想法背道而馳。

然而,你只是不必要地把自己塗在角落裏。可以這麼說,你使接口太大,證明你需要對同一個方法有兩組獨立的參數。

相反,將人類行爲和AI行爲分開在不同的層次上。 ICardPlayer總是int的說法。唯一的區別是參數是如何在不同的地方產生的 - 在人類玩家的情況下,它是UI的產品,要求他選擇一張牌。在AI玩家的情況下,它是由某種算法生成的。

所以你必須,代表行動「中抽取一張牌」的接口:

interface IPlayer 
{ 
    int PickCardToDiscard(); 
} 

,你離開了如何實現:

public class HumanPlayer: IPlayer 
{ 
    private readonly IGui gui; 

    public HumanPlayer(IGui gui) 
    { 
    this.gui = gui; 
    } 

    public int PickCardToDiscard() 
    { 
    return gui.AskForCardSelection("Pick a card to discard."); 
    } 
} 

public class StupidPlayer: IPlayer 
{ 
    public int PickCardToDiscard() 
    { 
    return 42; // Feeling lucky 
    } 
} 

現在你的接口是一致的,並且你已經將具體的實現移到了他們所屬的地方。當實例化ICardPlayer,你總是知道無論你是想要人類玩家還是AI玩家。但那只是只有你關心的地方。抽象的力量 - 精心設計的界面允許你將自己與具體細節區分開來,並專注於抽象(這是一個小得多的問題空間)。當遊戲引擎要挑一張牌,所有它做的是叫

var cardToDiscard = deck.Pop(player.PickCardToDiscard()); 

它不關心玩家是否是一個人或一個AI,和它給你的機會,線材在其他實現爲就好像不同的人工智能策略,或者通過網絡玩的人。請記住,每一段代碼都會更好地爲自己付出代價 - 如果它不是有利的,那麼它就是積極的破壞性的。一般抽象也是如此 - 如果抽象不支付租金,修正或丟失。在你的情況下,抽象顯然是愚蠢的 - 即使你明確設計它的前兩種情況,它也不起作用。如果你有一個像「使用接口編寫代碼」這樣的任務,你可能會做這種事情,而且你不知道如何設計實際爲你的代碼增加價值的接口。爲接口起見,或者爲了抽象而抽象是沒有意義的。 Mkae代碼支付租金。

最後,有些情況下可選參數是有意義的。但關鍵是這些參數必須仍然是合同的一部分,並且所有實施必須同樣有效。例如,你可能有一個日誌接口是這樣的:

interface ILogger 
{ 
    void Log(string message, int? severity); 
} 

您可以指定嚴重程度,也可以使用null - 但選擇不依賴於具體實現的​​,它只取決於主叫 - 有時候,他想指定一個嚴重程度,有時他不會。

+0

非常感謝你,我已經瞭解了更多關於界面的知識:) – Clyky