Search code examples
javaextend

Java extends question


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.


Solution

  • 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. :-)