如何为两个不同的子类创建超类

Posted

技术标签:

【中文标题】如何为两个不同的子类创建超类【英文标题】:How can I create a SuperClass for two different SubClasses 【发布时间】:2019-01-04 15:21:25 【问题描述】:

想象一下: 我有两节课: 节点 A 和节点 B。 NodeA 存储一个 Integer 并具有一个 getter 方法。 NodeB 没有。

现在我想创建一个超类节点, 它可以调用getter-Method。 但是我不希望 NodeB 存储一个整数。 我该如何处理?

下面是我的代码, NodeB 抛出异常。 超类 Node 有一个 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();
    

编辑:此处添加了以下虚假答案的解释。

我的工作是创建一个纸牌游戏。我有 NormalCards 和 Jokers。 NormalCard 有一个价值,而笑话则没有。我想要一个超类的原因是我可以创建一个列表

假设您想遍历列表并对所有值求和。

由于笑话没有价值,我必须检查卡片是否是小丑。如果不将其转换为 NormalCard 并获取值。 e 我的老师说,演员阵容是邪恶的......所以我正在寻找替代方案。

【问题讨论】:

你应该如何处理?很难回答关于不好的实践案例的“最佳实践”问题......超类的目标是包含子类中的共同点,或者强制子类使用“模式”。如果您的目标是不尊重这个超类,那么创建一个超类会很奇怪。 你所有的卡片都有价值。小丑的价值与其他所有卡片的价值不同。也许使用零 (0) 作为百搭值。 【参考方案1】:

简短的回答是:你没有。

稍微长一点的答案:如果您觉得有必要这样做,您应该改进您的设计。这违反了OO设计原则,尤其是LSP。

使用这样的方法进行成像:

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

您如何知道这是否会起作用?在运行时它可能会抛出异常,它可能会起作用,谁知道呢,这显然很糟糕。

你可能更想要的是创建一个界面

public interface ValueProvider 
   Integer getValue();

并且仅针对NodeA 实现此功能。鉴于您的纸牌游戏示例,其中值可能是可选的,您可以考虑在 getValueNodeB 中返回 null。更好的方法可能是使用Optional<Integer> 作为getValue() 的返回类型。

然后你可以在NodeA 中有一个方法,比如:

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

NodeB

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

【讨论】:

我的问题措辞不好。我努力做得更好。 @FelixHohnstein 我已更新以回答您的纸牌游戏示例【参考方案2】:

在基本层面上, 你的设计是错误的; 你正在打破封装。 我称之为LRN2OOP

而不是循环遍历一组卡片并将值相加, 您应该遍历卡片集合,并让每张卡片将其价值添加到累加器中。 具体来说, Card 类的客户端不需要知道 Card 值的内部表示(这是访问者模式)。

这不是一个很好的解决方案, 但这是一个代码示例:

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
  

【讨论】:

【参考方案3】:

如果可能的话,我肯定会在这里重新评估您的设计策略。使用单个类并为 Joker 简单地返回 0 比您尝试做的要容易得多。

但是,如果由于某种原因这不是一个选项,那么接口将是一个不错的选择。创建一个Card 接口并让两个类都实现它。注意在返回类型中使用Integer 而不是int 以允许空值。此外,由于您已经知道非百搭牌的所有可能值,因此枚举可以很好地定义它:

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;

现在我们可以创建我们的类来存储卡片,Deck。我们只对所有卡片使用card.getValue(),并检查生成的Integer 是否为null,然后再添加它的值:

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;
    

这里有一个快速测试来证明它有效:

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


【讨论】:

【参考方案4】:

这个问题的解决方案是不要创建超句。你没问题:这是糟糕的设计。

我们对其进行了更改,并使用两个静态工厂方法创建了一个类 Card: 创建小丑() createNormalCard(int value)

感谢您的所有回答。 几周后你会学到很多东西。

【讨论】:

以上是关于如何为两个不同的子类创建超类的主要内容,如果未能解决你的问题,请参考以下文章

如何为两个不同的帐户使用cmd命令[重复]

Xcode:如何为指向两个不同团队帐户(企业和公司)的两个不同目标选择两个不同的应用程序组

如何为两个可启动的活动提供两个不同的名称?

Java 中的继承——创建子类的对象也会调用超类的构造函数。为啥?

如何为单个 UIView 提供两个不同的类引用?

两个不同子类扩展同一个实体实例时的教义继承策略