Как избежать «заполнения» универсального класса атрибутами? - PullRequest
0 голосов
/ 01 декабря 2010

Я пытаюсь перевести игру в покер на правильную модель ООП.
Основы:

class Hand
{
    Card cards[];
}
class Game
{
    Hand hands[];
}

Я получаю игры и руки из текстового файла. Я анализирую текстовый файл несколько раз по нескольким причинам:

  • получить информацию (причина 1)
  • вычислить некоторые характеристики (причина 2)
  • ...

По причине 1 мне нужны некоторые атрибуты (a1, b1) в классе Hand. По причине 2 мне нужны некоторые другие атрибуты (a2, b2). Я думаю, что грязный путь будет:

class Hand
{
    Card cards[];
    Int a1,b1;
    Int a2,b2;
}

Я хотел бы сказать, что некоторые атрибуты бесполезны большую часть времени. Итак, чтобы быть чище, мы могли бы сделать:

class Hand
{
    Card cards[];
}
class HandForReason1 extends Hand
{
    Int a1,b1;
}

Но мне хочется использовать молоток ...

Мой вопрос: есть ли промежуточный путь? Или решение молотка является хорошим? (в таком случае, что будет правильной семантикой?)

PS: приветствуются шаблоны проектирования: -)
PS2: шаблон стратегии - это молот, не так ли?

* РЕДАКТИРОВАТЬ * Вот приложение:

// Parse the file, read game infos (reason 1)  
// Hand.a2 is not needed here !
class Parser_Infos
{  
     Game game;  
     function Parse()  
     {  
          game.hands[0].a1 = ...  
     }  
 }  
// Later, parse the file and get some statistics (reason 2)  
// Hand.a1 is not needed here !
class Parser_Stats  
{  
    Game game;  
    function Parse()  
    {  
         game.hand[0].a2 = ...  
    }  
} 

1 Ответ

0 голосов
/ 01 декабря 2010

Использование цепочки ответственности для распознавания покерной руки - вот что я бы сделал.Поскольку каждая рука имеет свои особенности, вы не можете просто иметь общую руку.

Что-то вроде

abstract class Hand {
   protected Hand next;

   abstract protected boolean recognizeImpl(Card cards[]);

   public Hand setNext(Hand next) {
      this.next = next;
      return next;
   }

   public boolean Hand recognize(Card cards[]) {
      boolean result = ;
      if (recognizeImpl(cards)) {
         return this;
      } else if (next != null) {
         return next.recognize(cards);
      } else {
         return null;
      }
   }
}

А потом ваша реализация

class FullHouse extends Hand {
    protected boolean recognizeImpl(Card cards[]) {
        //...
    }
}
class Triplet extends Hand {
    protected boolean recognizeImpl(Card cards[]) {
        //...
    }
}

Затем создайте свою цепочку

// chain start with "best" hand first, we want the best hand
// to be treated first, least hand last
Hand handChain = new FullHouse();
handChain
  .setNext(new Triplet())
  //.setNext(...)     /* chain method */
;

//...

Hand bestHand = handChain.recognize(cards);
if (bestHand != null) {
   // The given cards correspond best to bestHand
}

Кроме того, с каждой стороны этосвой класс, вы можете инициализировать, а затем хранить и вычислять очень конкретные вещи.Но поскольку вы должны как можно больше манипулировать Hand классами (чтобы оставаться как можно больше ОО), вам следует избегать приведения рук к определенному классу рук.

** ОБНОВЛЕНИЕ **

Хорошо, поэтому, чтобы ответить на ваш оригинальный вопрос ( sig ), класс Hand предназначен для манипулирования и лечения "рук".Если вам нужно вычислить другую статистику или другие потребности, перенос класса Hand может оказаться не очень хорошей идеей, поскольку в итоге вы получите составной класс, что нежелательно (для удобства обслуживания и для парадигмы ООП).

По причине 1 нормально иметь разные виды рук, как показывает цепочка ответственности;Вы можете читать свой файл, создавать разного рода руки с множеством параметров, как требуется.

По причине 2 вы можете взглянуть на другие решения.Одним из них может быть то, что ваши Hand классы запускают события (например, когда они распознаются), и ваше приложение может зарегистрировать эти руки в каком-то другом классе для прослушивания событий.Этот другой класс также должен отвечать за сбор необходимых данных из файлов, которые вы читаете.Поскольку рука не (или не должна) отвечать за сбор статистических данных, суть в том, что вам нужно что-то еще обрабатывать.

Один пакет = согласованные API и функциональные возможности

Один класс = когерентные функции (рука - это рука, а не статистический контейнер)

Один метод = (одна) функциональность (если метод должен обрабатывать более одной функции, разбейте эти функции на отдельные частные методы и вызовите их из открытого метода)

Я даю вам общий ответ, потому что причина 1 и причина 2 не является конкретной.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...