首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何避免java中的多次继承

如何避免java中的多次继承
EN

Stack Overflow用户
提问于 2019-10-06 18:23:54
回答 2查看 530关注 0票数 2

在这种情况下,我试图实现一个(相对简单的)抽象语法树。所有节点都从一个名为SimpleNode的类型继承,该类型包含一些代码,用于存储行和列信息并接受访问者。

现在,一些节点也应该是可命名的,而另一些节点应该具有“可访问”的属性(例如。(公共或私人)。有些节点甚至应该支持这两个接口。

我最好使用虚拟继承来实现这一点,并编写两个类NameableNode和AccessibleNode,但是Java不支持MI。

例如,NameableNode可能有字段"name“,并为该字段实现简单的getter和setter。类似地,AccessibleNode也可能有一个字段“可访问性”和getter/setter。

实现这一点并避免在代码库的很大一部分中引入代码重复的好方法是什么?

小代码示例:

代码语言:javascript
复制
public class SimpleNode {
    private int line = 0;
    private int column = 0;

    /* Getters and setters for line/column. */
    /* ... */
}

public class NameableNode extends SimpleNode {
    private String name = "";

    /* Getters and setters for name */
}

public class AccessibleNode extends SimpleNode {
    private boolean isPublic = false;

    /* Getters and setters for accessibility */
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-10-06 21:57:55

你在找作文。有很多种口味--我会提出一种,根据我对你想要建立的东西的理解,应该适合你的目标。

首先,让我们为您的Node创建一些接口:

代码语言:javascript
复制
public interface Nameable {
    /* Getters and setters for name */
}

public interface Accessible {
     /* Getters and setters for accessibility */
}

接下来,您可能不希望对每个Node重复相同的实现,所以让我们创建这些实现:

代码语言:javascript
复制
public class NameDelegate() {
    private String name = "";

    /* Getters and setters for name */    
}

public class AccessDelegate() {
    private boolean isPublic = false;

    /* Getters and setters for accessibility */

}

现在,让我们把所有的东西放在一起:

代码语言:javascript
复制
public class SomeNodeA extends SimpleNode implements Nameable {

   private NameDelegate nameDelegate;

   public SomeNodeA(NameDelegate nameDelegate) {
      this.nameDelegate = nameDelegate;
   }

   @Override
   public String getName() {
       return nameDelegate.getName();
   }

   @Override
   public String setName(String name) {
       nameDelegate.setName(name);
   }
}

您还可以在一个单独的类中具有这两种行为:

代码语言:javascript
复制
public class SomeNodeB extends SimpleNode implements Nameable, Accessible {

   private NameDelegate nameDelegate;
   private AccessDelegate accessDelegate;

   public SomeNodeB(NameDelegate nameDelegate, AccessDelegate accessDelegate) {
      this.nameDelegate = nameDelegate;
      this.accessDelegate = accessDelegate;
   }

   @Override
   public String getName() {
       return nameDelegate.getName();
   }

   @Override
   public String setName(String name) {
       nameDelegate.setName(name);
   }

   @Override
   public boolean getAccessibility() {
       return accessDelegate.getAccessibility();
   } 

    /* etc... */
}

其思想是,您可以将不同“功能”的状态和功能打包到单独的委托中,并将它们作为相应的接口公开在节点中。

此外,在对Node进行操作时,如果需要知道给定的Node实例是否支持特定特性,则可以使用instanceof --例如:

代码语言:javascript
复制
if (someNode instanceof Nameable) {
   // do naming stuff
}
票数 2
EN

Stack Overflow用户

发布于 2019-10-06 21:07:13

在这种情况下,我将使用组合方法而不是继承:

代码语言:javascript
复制
public class Node {
    private int line = 0;
    private int column = 0;

    /* Getters and setters for line/column. */
    /* ... */

    private String name = null;
    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this._name = name;
    }    

    private Boolean _isPublic = null;
    public String isPublic() {
        return this.name;
    }

    public void setIsPublic(boolean isPublic) {
        this._isPublic = isPublic;
    }

    public boolean hasAccessibility() {
        return this._isPublic != null;
    }

    public boolean hasName() {
        return this._name != null;
    }
}

另一个我更喜欢的解决方案是使用一个HashMap和一个枚举动态地创建这些属性,它指示节点的所有可能属性。这种方法更通用,因为它需要编写更少的代码来支持新属性,但它也较少使用typesafe(ish),因为附加的属性需要在运行时丢弃:

代码语言:javascript
复制
import java.util.HashMap;

enum NodeAttribute {
  NAME,
  ACCESSIBILTY
}

enum NodeAccessibility {
  PUBLIC,
  PRIVATE
}

public class Node {
    private int line = 0;
    private int column = 0;

    // Notice that this Object usage might involve some boxing for attributes of premitive type 
    private HashMap<NodeAttribute, Object> additionalAttributes = new HashMap<NodeAttribute, Object>();

    /* Getters and setters for line/column. */
    /* ... */

    public boolean hetAttribute(NodeAttribute attribute) {
        return this.additionalAttributes.containsKey(attribute);        
    }

    public <T> T getAttributeValue(NodeAttribute attribute, Class<T> attributeClass) {
        Object attributeValue = this.additionalAttributes.get(attribute);

        // You may want to wrap the ClassCastException that may be raisen here to a more specfic error 
        T castedAttributeValue = attributeClass.cast(attributeValue);
        return castedAttributeValue;
    }

    public void setAttributeValue(NodeAttribute attribute, Object value) {
        // Notice that this implemintation allows changing the type of an existing attribute,
        // If this is invalid behavior in your case you can throw an exception instead
        this.additionalAttributes.put(attribute, value);        
    }
}

// Example usage
public class Program {
    public static void main(String[] args) {
        Node nodeWithNameOnly = new Node();        
        nodeWithNameOnly.setAttributeValue(NodeAttribute.NAME, 'node1');

        Node nodeWithBoth = new Node();
        nodeWithBoth.setAttributeValue(NodeAttribute.NAME, 'node2');
        nodeWithBoth.setAttributeValue(NodeAttribute.ACCESSIBILTY, NodeAccessibility.PRIVATE);

        Program.doStuffWithNode(nodeWithNameOnly);
        /* output:
            Node name: node1
        */
        Program.doStuffWithNode(nodeWithBoth);
        /* output:
            Node name: node2
            Node is public: False
        */
    }

    public static void doStuffWithNode(Node node) {
        if (nodeWithNameOnly.hetAttribute(NodeAttribute.NAME)) {
            String nodeName = nodeWithNameOnly.getAttributeValue(NodeAttribute.NAME, String.class);
            system.out.println("Node name: " + nodeName);
        }

        if (nodeWithNameOnly.hetAttribute(NodeAttribute.ACCESSIBILTY)) {
            NodeAccessibility nodeAccessibilty =
                nodeWithNameOnly.getAttributeValue(NodeAttribute.ACCESSIBILTY, NodeAccessibility.class);
            boolean nodeIsPublic = nodeAccessibilty == NodeAccessibility.PUBLIC;
            system.out.println("Node is public: " + String.valueOf(nodeIsPublic));
        }
    }
}

在任何情况下,这都是主要的经验法则--继承应该用于“是”关系,而组合则应该用于“有”关系。

例如:

  • Fish 扩展了,因为鱼是动物。
  • Post 保存注释,因为Post 有注释。

在我们的例子中,节点有一个名称和一个可访问性级别的,因此它应该保存它们。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58260119

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档