Java扩展问题

发布于 2024-11-16 14:10:27 字数 863 浏览 2 评论 0原文

我有一系列类和扩展:

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() 等)和特定于“计算机”的方法(decideWhatToDo() 等)。

现在我想也许简单地将类合并在一起会更正确。 将 Base1 和 Base2 合并为一个类,然后对子类进行等效合并。

I have a series of classes and extentions:

Class Base1{  .... }

Class Base1Extend1 extends Base1 {.....}

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

I now have another set which uses those:

Class Base2 {
   Base1 base1; 
   }

Class Base2Extend1{
   base1 = new Base1Extend1();}

Class Base2Extend1{
   base1 = new Base1Extend2();}

Now, Base2 performs some work on the instantiation of Base1.

Also, Base2Extend1 and Base2Extend2 need perform work on their specific extended class. For example, they invoke specific methods that exist only in the subclass. Is there any way to do this without having to use cast every time in the extended classes of Base2?


The exact case is like this:

It is a card game which has 2 types of players: the user and the "computer".
There are many base methods which apply to both of them (pickupCards(), discardCards() etc.).
Then there are methods specific to the user only (checkMove() etc) and those specific to the "computer" (doTurn() etc.).

Next there is the object playerHand which holds the cards of the user.
Again there are base methods that deal with the hand (addCards(), removeCards() etc.).
Then there are specific methods for the user hand only (checkCards() etc.) and those specific to the "computer" (decideWhatToDo() etc.).

Now I am thinking that maybe it would be more correct to simply merge the classes together.
Make Base1 and Base2 into one class and then do the equivalent merge for the subclasses.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

甜味拾荒者 2024-11-23 14:10:28

您可能希望使用字段 T base1 来参数化您的类。那么编译器就知道你正在使用 T =>; Base1Extend1 或 Base1Extend2,并且您不必进行强制转换。

You might want to parameterize your class with e.g. , using a field T base1. Then the compiler knows that you are using T => Base1Extend1 or Base1Extend2, and you don't have to cast.

望喜 2024-11-23 14:10:27

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

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

如果您需要在子类上调用新方法,因为它需要它来完成超类中定义的某些任务,则意味着子类违反了其父类的契约(谷歌里氏替换原则)。

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


这是相当广泛的,但是卡牌游戏的这种设计怎么样?我认为这是一个多回合的技巧游戏,需要检查一步是否有效以及谁采取了技巧。我不太确定你所说的丢弃、添加或删除牌是什么类型的游戏。

  • 创建一个 Player 类,但它除了一个名称之外就没有太多内容。它还可以有一系列卡片。
  • 另一个类是 GameController,它协调正在发生的事情并检查规则。它可以有这样的方法:
    • playCard(Player, Card),验证移动并将卡片添加到记住谁玩过的游戏中。
    • 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 中唯一的东西。它非常容易掌握和维护。
  • CPU AI也是如此。一切都集中在一处,CPUController 中唯一的东西就是 AI 实现。
  • 从外部看,控制器是完全相同的,无论是人与 CPU、CPU 与 CPU 还是人与人。如果您想要多人游戏,那么您在这里唯一需要更改的是主循环(这是一个微不足道的更改)。
  • 如果你想实现另一个拿技巧的游戏,你可能唯一需要改变的是 GameController(不同的规则)和 CPUController(不同的 AI)。
  • 如果您想要 AI 的另一种实现(例如更智能的算法),您只需替换 CPUController 即可。

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

如果您想了解有关这些设计原则的更多信息,请搜索 SOLID。这个例子就全部讲完了。 :-)

In this case you either have to cast every time, or create a field/variable of more specific type.

I'm not sure where you are, but be careful with this design. Often it's a sign that you used inheritance incorrectly.

If you need to call the new method on subclass because it needs it for some task defined in superclass, it means the subclass violates its parent's contract (google Liskovs substitution principle).

If the subclass indeed has some new functionality that does not intertwine with that of the parent's, you may want to use the subclass explicitly from the very beginning (that is, variable declaration). Or, if it's not really related, you probably should use a brand new class and composition instead of inheritance.


This is quite broad, but how about this design for the card game. I assume it's a trick-tacking game with several turns, that needs to check if a move is valid and who takes the trick. I'm not really sure what kind of game you mean with discarding, adding or removing cards.

  • Make a Player class, but it wouldn't have much more than a name. It also could have a collection of Cards.
  • Another class is GameController that coordinates what's going on and checks rules. It could have such methods as:
    • playCard(Player, Card), that validates move and adds card to the trick remembering who played it.
    • score() that calculates score at the end of each turn.
  • Another class is CPUController. That's where the AI lives.
  • Finally, a main loop.

The main loop could work like this:

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

Now, here's what you gain with this approach:

  • All things related to game rules and scoring are in one place, and that's the only thing that is in GameController. It's very easy to grasp and maintain.
  • The same is true for CPU AI. It's all in one place, and the only stuff that's in CPUController is AI implementation.
  • From outside the controller is exactly the same, regardless of whether you have human vs. CPU, CPU vs. CPU or human vs. human. If you wanted multiplayer, the only thing you would change in here is the main loop (and that's a trivial change).
  • If you wanted to implement another trick-taking game, it's possible the only thing you would change would be the GameController (different rules) and CPUController (different AI).
  • If you wanted another implementation of the AI (say a smarter algorithm), you could do it by only substituing CPUController.

The last two cases are where inheritance would make sense. You want CPUController and GameController to have the same interface (of course different for each), so that the main loop and Player can seamlessly work with different implementations of controllers.

If you feel like learning more about these design principles, google for SOLID. It's all over this example. :-)

丶情人眼里出诗心の 2024-11-23 14:10:27

不,没有。您必须执行强制转换,否则编译器无法判断该函数是否存在。

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

No there isn't. You must perform the cast or the compiler can't tell the function exists.

If the functions of the subclass are the same then you could implement them in an interface and have the subclasses implement those and extend the base class. Then you could simply cast to the interface but you must still do the casting.

峩卟喜欢 2024-11-23 14:10:27

为了避免强制转换,请考虑泛型:

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

In order to avoid casts, think about generics:

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(); }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文