Java扩展了问题

时间:2011-06-23 11:02:15

标签: java extend

我有一系列的课程和扩展:

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的扩展类中使用强制转换?


具体情况如下:

这是一种纸牌游戏,有两种类型的玩家:用户和“计算机”。 有许多基本方法适用于它们(pickupCards(),discardCards()等)。 然后有特定于用户的方法(checkMove()等)和特定于“计算机”(doTurn()等)的方法。

接下来是对象playerHand,它持有用户的牌。 同样有基本方法处理手(addCards(),removeCards()等)。 然后只有用户手的具体方法(checkCards()等)和特定于“计算机”的那些(决定是什么(等)。

现在我想,简单地将这些类合并在一起可能更为正确。 将Base1和Base2合并为一个类,然后为子类执行等效合并。

4 个答案:

答案 0 :(得分:4)

在这种情况下,您必须每次都进行转换,或者创建更具体类型的字段/变量。

我不确定你在哪里,但要小心这个设计。通常,这表明您使用的是继承错误。

如果你需要在子类上调用new方法,因为它需要它来处理超类中定义的某个任务,这意味着子类违反了它的父类合同(google Liskovs替换原则)。

如果子类确实具有一些不与父类的功能交织的新功能,那么您可能希望从一开始就明确地使用子类(即变量声明)。或者,如果它没有真正相关,你可能应该使用一个全新的类和组合而不是继承。


这是相当广泛的,但这种卡片游戏的设计如何。我认为这是一个带有几个回合的特技游戏,需要检查移动是否有效以及谁接受了这个技巧。我不确定你在丢弃,添加或删除卡片时使用什么类型的游戏。

  • 制作一个Player类,但它不会只是一个名字。它也可能有一系列卡片。
  • 另一个类是GameController,用于协调正在进行的操作并检查规则。它可以有这样的方法:
    • playCard(播放器,卡片),验证移动并添加卡片以记住播放它的人。
    • 得分(),计算每回合结束时的分数。
  • 另一个类是CPUController。这就是AI生活的地方。
  • 最后,一个主循环。

主循环可以这样工作:

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中唯一的内容。这很容易掌握和维护。
  • CPU AI也是如此。它集中在一个地方,CPUController中唯一的东西就是AI实现。
  • 从控制器外部来看,无论你是人还是CPU,CPU与CPU还是人与人,都是完全相同的。如果你想要多人游戏,那么你唯一要改变的就是主循环(这是一个微不足道的变化)。
  • 如果你想要实现另一个特技游戏,你唯一可能改变的就是GameController(不同的规则)和CPUController(不同的AI)。
  • 如果你想要AI的另一个实现(比如一个更聪明的算法),你可以通过替换CPUController来实现。

最后两种情况是继承有意义的地方。您希望CPUController和GameController具有相同的接口(当然每个接口都不同),因此主循环和Player可以无缝地使用不同的控制器实现。

如果您想了解有关这些设计原则的更多信息,请访问Google for SOLID。这就是这个例子。 : - )

答案 1 :(得分:1)

不,没有。您必须执行强制转换或编译器无法告诉函数是否存在。

如果子类的函数相同,那么您可以在接口中实现它们,并让子类实现它们并扩展基类。然后你可以简单地转换到接口,但你仍然必须进行转换。

答案 2 :(得分:1)

为了避免演员表,请考虑泛型:

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(); }

答案 3 :(得分:0)

您可能希望使用以下方法对您的课程进行参数化,使用字段T base1。然后编译器知道您正在使用T =&gt; Base1Extend1或Base1Extend2,你不必强制转换。