2015-04-13 1 views
1

Я занимаюсь анализом OO для выяснения отношений между игровыми компонентами, чтобы я мог проектировать классы. Я в конечном итоге на C#.OOAD из двух взаимосвязанных иерархий наследования (игровые компоненты)

Требования

Моя игра будет включать в себя:

Пьесы: блоки, карты, счетчики

Места: Сетки, стеки

Placement штук возможных:

  • Блоки могут быть размещены на Сетки (Нужна сетка, х, у)
  • карты могут быть размещены на стеки (нужны стек, положение)
  • Счетчики могут быть размещены на сетках (Нужна сетка, х, у)
  • Счетчики могут быть также размещены на вершина штабеля (нужны стек, положение)

Анализ

  1. Кусок либо как интерфейс (IPiece) или абстрактный класс (шт). (что лучше?)
  2. Блок, карта & Счетчик как реализация IPiece или подклассов Piece.
  3. Место в виде интерфейса (IPlace) или абстрактного класса (Место). (что лучше?)
  4. Сетка & Стек как реализация IPlace или подклассов места.

Тогда я запутаться ...

Каждый кусок HAS-A Place так есть отношения там. Это взаимно-однозначное отношение, так как каждый кусок должен иметь одно и только одно место.

Но как я могу гарантировать, что Блоки могут работать только на сетках, Карты только на стеках и счетчиках?

Заранее благодарим за любую помощь.

+0

Ваш вопрос о самом языке или о дизайне классов? (Я угадываю второе, но хочу быть уверенным, потому что у меня были некоторые сюрпризы при интерпретации вопросов) – MVCDS

+0

Речь идет о дизайне классов. (Хотя было бы полезно, если дизайн поддается C# - e..g не множественное наследование!) – Adahus

ответ

3

Но как я могу гарантировать, что блоки могут работать только на сетках, карты только на стеках и счетчиках?

Вы можете использовать дженерики и generic type constraints для обеспечения соблюдения этих правил во время компиляции

public interface IPiece<TPlace> where TPlace : IPlace 
{ 
    TPlace Place { get; } 

    void PlaceOn(TPlace place); 
} 

public class Block : IPiece<Grid> 
{ 
    private Grid _place; 

    public Grid Place 
    { 
     get { return _place; } 
    } 

    public void PlaceOn(Grid place) 
    { 
     _place = place; 
    } 
} 
+0

Спасибо dcastro - Я не использовал интерфейсы много, и я тоже не использовал дженерики, так что это на краю моего понимания но очень полезно. – Adahus

0

Часто утверждают, что вы должны favor composition over inheritance. Имея это в виду, я играл с этой идеей немного, и это то, что я придумал:

class Grid 
{ } 

class Stack 
{ } 

abstract class Location 
{ 
    private Location() {} 

    public class GridLocation : Location 
    { 
     public Grid Grid { get; set; } 

     public int X { get; set; } 

     public int Y { get; set; } 
    } 

    public class StackLocation : Location 
    { 
     public Stack Stack { get; set; } 

     public int Position { get; set; } 
    } 
} 

abstract class Visual 
{ 

} 

class Block 
{ 
    public Visual Visual { get; set; } 

    public Location.GridLocation Location { get; set; } 
} 

class Card 
{ 
    public Visual Visual { get; set; } 

    public Location.StackLocation Location { get; set; } 
} 

class Counter 
{ 
    public Visual Visual { get; set; } 

    public Location Location { get; set; } 
} 

class Game 
{ 
    public IEnumerable<Block> Blocks { get; set; } 

    public IEnumerable<Card> Cards { get; set; } 

    public IEnumerable<Counter> Counters { get; set; } 

    public IEnumerable<Tuple<Location.StackLocation, Visual>> StackVisuals 
    { 
     get 
     { 
      var cardVisuals = 
       Cards.Select (c => Tuple.Create(c.Location, c.Visual)); 

      var counterVisuals = 
       Counters.Select (c => Tuple.Create(c.Location, c.Visual)) 
       .OfType<Tuple<Location.StackLocation, Visual>>(); 

      return cardVisuals.Concat(counterVisuals).OrderBy (v => v.Item1.Position); 
     } 
    } 

    public IEnumerable<Tuple<Location.GridLocation, Visual>> GridVisuals 
    { 
     get 
     { 
      var blockVisuals = 
       Blocks.Select (b => Tuple.Create(b.Location, b.Visual)); 

      var counterVisuals = 
       Counters.Select (c => Tuple.Create(c.Location, c.Visual)) 
       .OfType<Tuple<Location.GridLocation, Visual>>(); 

      return blockVisuals.Concat(counterVisuals).OrderBy (v => new { v.Item1.X, v.Item1.Y }); 
     } 
    } 
} 

Я использовал класс заполнителем Visual в примере, чтобы показать, как вы могли бы унифицировать объекты с определенным типом местоположения, когда возникает необходимость (например,StackVisuals и GridVisuals).

+0

Также очень интересно. Благодарю Даниэля. Я не понимаю, что такое дополнение Visuals - не могли бы вы объяснить немного больше? – Adahus

+0

Если вы собираетесь отобразить игровой кусок на игровой доске, вам понадобится (как минимум) положение фигуры и графическое представление пьесы. «Визуальный» является просто заполнитель для некоторого графического представления фрагмента игры. –

+0

Я вижу - спасибо. – Adahus