2015-04-27 34 views
6

我在使用C#編寫的遊戲中碰到過問題。這是一個簡單的基於瓷磚的匹配遊戲,並且問題已經出現,我正試圖通電:在C#中使用GetType/instanceof與替代方案

假設我們有基本瓷磚類型,圓形正方形和菱形,它們都是Tile的子類。我試圖將抽象的「匹配」行爲提取到抽象的Tile方法:canMatchWith(Tile t),而不是將圓圈與圓相匹配。 瓷磚也有兩種方法來添加/刪除他們可以匹配的瓷磚。

所以說我們在遊戲中間有一個圓形的方塊,並且我們有一個提示說:「圓形方塊可以和這個方塊相匹配」。我會穿過所有的圓形瓷磚,並說circleTile.addCanMatchWith(typeof(Square))。在內部,我們有一個List canMatchWith。

然後,我想說「圓形不能再與方塊匹配」,並簡單地說circleTile.removeCanMatchWith(typeOf(Square))。

這是我當前的解決方案,它的工作原理沒有我注意到的性能缺陷(這是一個基於拼貼的匹配遊戲,因此這些類型只能按「移動」而不是逐幀評估)。然而,我腦海中的聲音告訴我,這是一種完成這種行爲的不好方法。所以我有一些替代品:

  1. 枚舉...每個Tile可以用Tiletype類型變量組成。這將在構造函數中初始化,並設置爲Type.SQUARE for squares,依此類推。然後,每個Tile將有一個List canMatchWith,並且功能與我原來的實現相同。除了在這種情況下,這有點棘手。假設我有一些圈子子類,橢圓形和橢圓形。我希望橢圓能夠匹配只有廣場,但elipses可以匹配所有的圈子,而不是正方形。

這裏的問題是冗餘,我的枚舉現在也會有OVAL和ELIPSE,並且Elipse類會有(CIRCLE,OVAL,ELIPSE TileTypes)作爲它可以匹配的類型。這完全是多餘的,我只想說我可以用類型的「圓」。我想瓷磚可以有TileType baseType和TileType實際類型。

  1. 某些形式的行爲組合。忘記Tile子類,只給Tiles方法和List的實例變量。然後,在運行時我們可以說someTile.addCanMatch(新的CircleMatchBehavior())。這看起來很愚蠢,因爲我會有一堆班只是說你可以匹配一個特定的形狀。

總之,我試圖完成的是讓多個對象類型能夠與任意數量的不同類型進行交互。問題是,我應該使用什麼類型。在這裏使用GetType可以嗎?枚舉?或者有人會推薦更好的策略?我試圖儘可能普遍,這些瓷磚不應該對其他瓷磚有任何硬編碼的依賴關係,並且必須能夠改變他們可以與之進行交互的人員。假設我製作了一個新的Tile子類,五角形...... Pentagons可以與Squares,Circles和Pentagons匹配。我的實施很簡單,但有些事情告訴我這是一個骯髒的面向對象操作。

我覺得我必須使用Types/Enums,因爲我不是想說thisTile.addCanMatch(Tile someOtherObject)。這太具體了,我希望thisTile能夠匹配所有類的實例的所有圖塊。

+0

我怕我不是不夠熟練設計師提出一個完整的設計給你,但如果沒有的方式圓實際* *的功能差異,橢圓操作,最好讓所有的實例都是'Tile'類,然後設置某種'Behavior'屬性。董事會上的所有圈子都可以共享相同的「行爲」對象,然後當規則改變一個回合時,您可以在該行爲對象上設置更改。如果我瞭解情況,這可能會導致更少的代碼重複。 – Katana314

+0

如果我正確理解你,這有點像我的#2行爲組合。我的恐懼是行爲爆炸。例如,MatchesWithCirclesAndSquaresBehavior。看看我在做什麼?如果有5種類型的瓷磚,則我們有5種選擇1加5選2加5選3加5選4加5選5種行爲組合。這似乎比GetType更糟糕。不過,我可能會誤解你的建議。瓷磚本身可以做任何他們想做的事情,也可以做爆炸動畫等等。這隻有當瓷磚可以與其他瓷磚相匹配時纔會這樣做。 – user2045279

+0

我不是在暗示行爲是一個枚舉或一個不可修改的類。你可以用不同的方式對它進行子類化,但主要是我希望它有一個內部集合來表示它的類型(Circle)和它可以匹配的類型(Square)。然後,您可以以某種面向數據的方式更改它。我絕對同意,任何會導致大量if/else/switch塊的東西都應該避免,以利於良好的對象設置,但是我們也希望避免編寫一個新類來解決一些不太新的行爲變體。 – Katana314

回答

2

如果相似類型的所有形狀總是共享一個行爲,那麼不要將該行爲存儲在「每個實例」級別上。相反,你可以有一個'CanMatchManager',它存儲一個由形狀類型索引的列表字典。然後,當一個圓圈試圖比較一個匹配時,它會向匹配管理器請求匹配的類型。或者,MatchManager可以採用兩種形狀,並確定它們是否匹配。這是Mediator Pattern

+1

有趣的是,CanMatchManager :: canMatch(Tile one,Tile two){return dictionary [one] .contains(two)}其中dictionary將Tile Type映射到可匹配類型的列表。然後,如果我想添加交互,我可以說tileManager.addInteraction(Tile 1,Tile 2),它的更新類似{dict [one] .add(two),dict [two] .add(one)}。就所需的內存而言,這似乎比我所做的更有效率(從實例中拿出來,交給經理)。但它仍然使用GetType的權利? – user2045279

+0

我真正的問題是「是否可以使用GetType」來解決這類問題。或者是否有必要重構我的程序以便不能使用類名(類型)。我的一部分內容是「如果它的工作原理,而且代碼不復雜,在這種情況下可能就可以」,但也有「實例惡化,設計不好」。爲了詳細說明第一條評論,詞典將是詞典<類型,列表>地圖。我們如何獲得類型? someObject.GetType()和typeOf(SomeClass) – user2045279

+0

它仍在使用GetType,但可以輕鬆地使用Dictionary。至少,因爲它是中心的,你可以很容易地將它的工作方式轉換爲Enum。我對Enum類型的一個大問題是,沒有一個好的方法讓一個類說出Enum是靜態的,並且在某些時候,感覺就像複製了一個已經存在的系統。此外,對於類型,您始終可以使用反射來允許您的經理使用繼承以及... 我編輯了我的答案以鏈接到模式文檔。 – Psymunn

2

我知道這個問題已經被回答和接受,但我做了一次這樣的事情,我想我只是在這裏發佈代碼。

public class TypeMatchManager 
    { 
     private Dictionary<Type, List<Type>> savedMatches = new Dictionary<Type, List<Type>>(); 

     public TypeMatchManager() { } 

     public void AddMatch(Type firstType, Type secondType) 
     { 
      this.addToList(firstType, secondType); 
      this.addToList(secondType, firstType); 
     } 

     public void DeleteMatch(Type firstType, Type secondType) 
     { 
      this.deleteFromList(firstType, secondType); 
      this.deleteFromList(secondType, firstType); 
     } 

     public bool CanMatch(Type firstType, Type secondType) 
     { 
      List<Type> firstTypeList = this.findListForType(firstType); 
      List<Type> secondTypeList = this.findListForType(secondType); 
      return (firstTypeList.Contains(secondType) || secondTypeList.Contains(firstType)); 
     } 

     private void addToList(Type firstType, Type secondType) 
     { 
      var matchingTypes = this.findListForType(firstType); 
      if (!matchingTypes.Contains(secondType)) 
      { 
       matchingTypes.Add(secondType); 
      } 
     } 

     private void deleteFromList(Type firstType, Type secondType) 
     { 
      var matchingTypes = this.findListForType(firstType); 
      if (matchingTypes.Contains(secondType)) 
      { 
       matchingTypes.Remove(secondType); 
      } 
     } 

     private List<Type> findListForType(Type type) 
     { 
      foreach (var keyValuePair in savedMatches) 
      { 
       if (keyValuePair.Key == type) 
       { 
        return keyValuePair.Value; 
       } 
      } 
      savedMatches.Add(type, new List<Type>()); 
      return findListForType(type); 
     } 
    } 

該類的設計使得它無論您提供什麼類型的參數都無關緊要;它檢查type1.list有type2和type2.list有類型。

一個簡單的例子:

 typeManager.AddMatch(a, b); 
     Console.WriteLine(typeManager.CanMatch(a, b)); // True 
     typeManager.DeleteMatch(b, a); 
     Console.WriteLine(typeManager.CanMatch(a, b)); // False 
     Console.WriteLine(typeManager.CanMatch(a, c)); // False 
     typeManager.AddMatch(a, c); 
     Console.WriteLine(typeManager.CanMatch(a, c)); // True 
     Console.WriteLine(typeManager.CanMatch(a, d)); // False 
     typeManager.AddMatch(b, d); 
     Console.WriteLine(typeManager.CanMatch(a, d)); // False 
     Console.WriteLine(typeManager.CanMatch(d, b)); // True 
     typeManager.DeleteMatch(d, b); 
     Console.WriteLine(typeManager.CanMatch(d, b)); // False 
     Console.WriteLine(typeManager.CanMatch(b, d)); // False 
+0

這基本上就是我想如何做的。非常簡單,優雅,而且很小。感謝您的輸入! – user2045279