简体   繁体   中英

How can I create a SuperClass for two different SubClasses

Imagine sth like this: I have two classes: NodeA and NodeB. NodeA stores an Integer and has a getter-method. NodeB doesn't.

Now I want to create a Superclass Node, which can call the getter-Method. However I don't want NodeB to store an Integer. How should I handle this?

With my code below, NodeB throws an Exception. The superclass Node has an Optinal.

public abstract class Node {
    public Integer getValue();
}

public class NodeA extends Node {

    public Integer value;

    public NodeA() {}

    @Override   
    public Integer getValue() {
        return this.value;
    }
}

public class NodeB extends Node {

    public NodeB() {}

    @Override   
    public Integer getValue() {
        throw new Exception();
    }
}

Edit: Explanation from faux answer below added here.

My Job is to create a Card game. I have NormalCards and Jokers. The NormalCards have a Value, the Jokes dont. The reason I want a Superclass is so I can create a List

Imagine you want to travers the List and sume all Values.

Since Jokes have no values, I have to check wheter a Card is a Joker. If not cast it to a NormalCard and get the Value. e My Teacher said,that casts are evil....so I am looking for an alternative.

Short answer is: You don't.

Slightly longer answer: If you feel the need to do this, you should improve your design. This violates OO design principles, especially the LSP .

Just imaging having a method like this:

void printValue(Node node) {
   System.out.println(node.getValue());
}

How are you going to know whether or not this will work? At runtime it might throw an exception, it might work, who knows, which is clearly bad.

What you maybe rather want is to create an interface

public interface ValueProvider {
   Integer getValue();
}

and only implement this for NodeA . Given your example of a card game where the value might be optional, you could consider returning null in getValue of NodeB . A slightly better approach might be to use Optional<Integer> as a return type for getValue() .

Then you can have a method in NodeA like:

@Override   
public Optional<Integer> getValue() {
    return Optional.of(this.value);
}

and in NodeB

@Override   
public Optional<Integer> getValue() {
    return Optional.empty();
}

At a basic level, you're design is wrong; you are breaking encapsulation. I call this LRN2OOP

Instead of looping through a collection of cards and adding up the value, you should loop through the collection of cards and have each card add its value to an accumulator. Specifically, there is no need for the client of the Card class to know about the internal representation of the Card value (this is the Visitor pattern).

This is not a great way solution, but here is a code example:

public interface CardScoreBlammy
{
  void addToScore(int cardScore);
}

public interface MyCard
{
   void accumulateScore(ScoreBlammy);

   ... other shared Card functionality.
}

public class CardHolder
implements CardScoreBlammy
{
  private int accumumatedScore = 0;
  private List<MyCard> cardList;
  ... populate the cardList somehow.

  public void addToScore(final int cardScore)
  {
    accumulatedScore += cardScore;
  }

  public int getScoreTotal()
  {
    accumulatedScore = 0;

    for (final MyCard currentCard : cardList)
    {
      currentCard.accumulateScore(this);
    }

    return accumulatedScore;
  }
}


public class NotAJoker
implements MyCard
{
  private int myValue;

  public void accumulateScore(final ScoreBlammy scoreBlammy)
  {
    scoreBlammy.addToScore(myValue)
  }
}

public class IRJoker
implements MyCard
{
  public void accumulateScore(final ScoreBlammy scoreBlammy)
  {
    // do nothing
  }
}

I would definitely re-evaluate your design strategy here if possible. Using a single class and simply returning 0 for Joker is so much easier than what you are trying to do.

If, however, that is not an option for some reason, then an interface would be a good option. Create a Card interface and have both classes implement it. Note the use of Integer and not int in the return type to allow for null values. Also, since you already know all the possible values for a non-joker card an enum works well for defining that:

public interface Card {
    public Integer getValue();
}

public class Joker implements Card {

    @Override
    public Integer getValue() {
        return null;
    }

}

public class NotJoker implements Card {
    private CARD_TYPE cardType;

    public NotJoker(CARD_TYPE type) {
        this.cardType = type;
    }

    public CARD_TYPE getType() {
        return cardType;
    }

    @Override
    public Integer getValue() {
        return cardType.getValue();
    }

}

public enum CARD_TYPE {
    ACE(11), KING(10), QUEEN(10), JACK(10),
    TEN(10), NINE(9), EIGHT(8), SEVEN(7),
    SIX(6), FIVE(5), FOUR(4), THREE(3),
    TWO(2);

    private final int value;

    CARD_TYPE(int value) {
        this.value = value;
    }

    public int getValue() {return value;}
}

Now we can create our class to store the cards, Deck . We just use card.getValue() for all Cards, and check whether the resulting Integer is null before we add its value:

public class Deck {

    private ArrayList<Card> cardList;

    public Deck() {
        cardList = new ArrayList<Card>();
    }

    public void addCard(Card card) {
        cardList.add(card);
    }

    public int getTotalScore() {
        int totalScore = 0;

        for(Card card : cardList) {
            Integer value = card.getValue();
            if(value != null) {
                totalScore += value;
            }
        }
        return totalScore;
    }
}

And here is a quick test to prove it works:

public class CardGame {

    public static void main(String[] args) {

        Deck deck = new Deck();

        deck.addCard(new Joker());
        deck.addCard(new NotJoker(CARD_TYPE.FIVE));
        deck.addCard(new NotJoker(CARD_TYPE.FOUR));
        deck.addCard(new NotJoker(CARD_TYPE.ACE));
        deck.addCard(new NotJoker(CARD_TYPE.KING));
        deck.addCard(new Joker());
        deck.addCard(new NotJoker(CARD_TYPE.SEVEN));

        //total score: 37
        System.out.println("total score: " + deck.getTotalScore());
    }

}

The solution for this Problem was to dont create a superclase. You were all right: This was bad Design.

We changed it and create one class Card with two static factory methods : createJoker() createNormalCard(int value)

Thanks for all your answers. You learn a lot in a few weeks.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM