Java расширяет вопрос - PullRequest
       11

Java расширяет вопрос

1 голос
/ 23 июня 2011

У меня есть серия классов и расширений:

Class Base1{  .... }

Class Base1Extend1 extends Base1 {.....}

Class Base1Extend2 extends Base1 {......}

Теперь у меня есть другой набор, который использует их:

Class Base2 {
   Base1 base1; 
   }

Class Base2Extend1{
   base1 = new Base1Extend1();}

Class Base2Extend1{
   base1 = new Base1Extend2();}

Теперь Base2 выполняет некоторую работу по созданию экземпляра Base1.

Кроме того, Base2Extend1 и Base2Extend2 должны выполнить работу над своим конкретным расширенным классом.Например, они вызывают определенные методы, которые существуют только в подклассе.Есть ли способ сделать это, не используя каждый раз разыгрывание в расширенных классах Base2?


Точный случай таков:

Это карточная игра, которая имеет2 типа игроков: пользователь и «компьютер».Существует много базовых методов, которые применяются к ним обоим (pickupCards (), discardCards () и т. Д.).Затем существуют методы, специфичные только для пользователя (checkMove () и т. Д.) И методы, специфичные для «компьютера» (doTurn () и т. Д.).

Далее идет объект playerHand, в котором хранятся карты пользователя.,Опять же, есть базовые методы, которые имеют дело с рукой (addCards (), removeCards () и т. Д.).Кроме того, существуют специальные методы только для руки пользователя (checkCards () и т. Д.), А также методы, специфичные для «компьютера» (defineWhatToDo () и т.просто объединить классы вместе.Объедините Base1 и Base2 в один класс, а затем выполните эквивалентное объединение для подклассов.

Ответы [ 4 ]

4 голосов
/ 23 июня 2011

В этом случае вам придется каждый раз выполнять приведение или создавать поле / переменную более определенного типа.

Я не уверен, где вы находитесь, но будьте осторожны с этим дизайном. Часто это признак того, что вы неправильно использовали наследование.

Если вам нужно вызвать новый метод для подкласса, потому что он нужен для какой-то задачи, определенной в суперклассе, это означает, что подкласс нарушает контракт своего родителя (принцип подстановки Google Liskovs).

Если у подкласса действительно есть какая-то новая функциональность, которая не переплетается с родительской функциональностью, вы можете явно использовать подкласс с самого начала (то есть объявление переменной). Или, если это не совсем связано, вам, вероятно, следует использовать новый класс и состав вместо наследования.


Это довольно широко, но как насчет этого дизайна для карточной игры. Я предполагаю, что это сложная игра с несколькими ходами, которая должна проверить, действителен ли ход, и кто его использует. Я не совсем уверен, какую игру вы имеете в виду, выбрасывая, добавляя или удаляя карты.

  • Создайте класс Player, но у него не будет намного больше, чем имя. Это также может иметь коллекцию карт.
  • Другим классом является GameController, который координирует происходящее и проверяет правила. Это могут быть такие методы как:
    • playCard (игрок, карта), которая проверяет ход и добавляет карту к уловке, помня, кто ее сыграл.
    • score (), который вычисляет счет в конце каждого хода.
  • Другой класс - CPUController. Вот где живет ИИ.
  • Наконец, основной цикл.

Основной цикл может работать так:

controller.shuffle();
// Either of:
// 1. push: controller.shuffle() calls player.pickupCards(cards)
// 2. pull: main loop calls player.pickupCards() which in turn calls controller.giveMeMyCards()

while(playersHaveMoreCards()) {
    awaitPlayerMove();
    // Now is the only time when player can make a move. If there is some
    // GUI the main loop could `wait()` on a mutex (until it's awoken when
    // player makes move), in text mode it could simply await input from keyboard.

    // When player makes a move, it calls controller.playCard(player, card)

    cpuController.move();
    // Again, when it controller calculates its move, eventually it simply calls controller.playCard()

    controller.score();
}
announceWinner(controller.getScore());

Теперь вот что вы получаете с этим подходом:

  • Все, что связано с правилами игры и подсчетом очков, находится в одном месте, и это единственное, что есть в GameController. Это очень легко понять и поддерживать.
  • То же самое относится к процессору AI. Это все в одном месте, и единственное, что есть в CPUController - это реализация AI.
  • Снаружи контроллер точно такой же, независимо от того, есть ли у вас человек против процессора, процессор против процессора или человек против человека. Если вам нужен многопользовательский режим, единственное, что вы можете изменить здесь, - это основной цикл (и это тривиальное изменение).
  • Если вы хотите реализовать еще одну хитрую игру, возможно, единственное, что вы измените, это GameController (разные правила) и CPUController (другой AI).
  • Если вам нужна другая реализация AI (скажем, более умный алгоритм), вы можете сделать это, только заменив CPUController.

В последних двух случаях наследование имело бы смысл. Вы хотите, чтобы CPUController и GameController имели одинаковый интерфейс (разный для каждого), чтобы основной цикл и Player могли беспрепятственно работать с различными реализациями контроллеров.

Если вы хотите узнать больше об этих принципах проектирования, воспользуйтесь Google для SOLID. Все кончено в этом примере. : -)

1 голос
/ 23 июня 2011

Чтобы избежать приведений, подумайте о дженериках:

class Base2<T extends Base1> { T base1; }

// base1 does not need to be cast to Base1Extends1
class Base2Extends1 extends Base2<Base1Extends1> { base1 = new Base1Extends1(); }

// base1 does not need to be cast to Base1Extends2
class Base2Extends2 extends Base2<Base1Extends2> { base1 = new Base1Extends2(); }
1 голос
/ 23 июня 2011

Нет, нет.Вы должны выполнить приведение, иначе компилятор не может сказать, что функция существует.

Если функции подкласса одинаковы, вы можете реализовать их в интерфейсе, а подклассы реализовать их и расширить базовый класс.,Тогда вы можете просто привести к интерфейсу, но вы все равно должны выполнить приведение.

0 голосов
/ 23 июня 2011

Возможно, вы захотите параметризовать свой класс, например, , используя поле T base1. Тогда компилятор знает, что вы используете T => Base1Extend1 или Base1Extend2, и вам не нужно приводить.

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