Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Oop 如果可以,我们应该使用接口方法吗?_Oop_Interface - Fatal编程技术网

Oop 如果可以,我们应该使用接口方法吗?

Oop 如果可以,我们应该使用接口方法吗?,oop,interface,Oop,Interface,这个问题有点假设性。假设我有一个绘制树的应用程序。My structure controller实现了ITree接口,该接口使用诸如getChildren()、getParent()之类的方法来描述普通树。。。应用程序的某些部分只需要知道结构实现了ITree,这样它就可以查找它的子级或父级。因此,getParent()的高度足以返回ITree类型的对象。因此getChildren()可以返回ITree对象的集合 但是:) 如果在假设的TreeNode类中,我想对节点的子节点执行一些特定的操作,该

这个问题有点假设性。假设我有一个绘制树的应用程序。My structure controller实现了ITree接口,该接口使用诸如getChildren()、getParent()之类的方法来描述普通树。。。应用程序的某些部分只需要知道结构实现了ITree,这样它就可以查找它的子级或父级。因此,getParent()的高度足以返回ITree类型的对象。因此getChildren()可以返回ITree对象的集合

但是:)

如果在假设的TreeNode类中,我想对节点的子节点执行一些特定的操作,该怎么办。如果我使用ITree的getChildren()函数,我必须将它们转换为TreeNode,以便能够调用适当的实例函数(ITree未定义)。或者,我可以使用自己的迭代器遍历子节点,并尽我所能(因为这种方式实际上是树节点)

所以问题是:是否建议在每次可能的时候都使用接口函数,或者特定于对象的操作有时有意义

更新:[添加插图] (这是AS3语法,但重点是结构。)

下面是ITree:

public interface ITree {
  function getParent():ITree;
}
这是我的TreeNode课程:

public class TreeNode implements ITree {
  private var parent:TreeNode

  public function getParent():ITree {
    return parent;
  }

  function foo():void {}

  function bar():void {
    // Version 1 - using the interface
    (getParent() as TreeNode).foo();

    // Version 2 - using custom object accessor
    parent.foo();
  }

}
哪一个更好:版本1还是版本2


谢谢

您需要随时随地使用ITree接口,以减少耦合。如果您需要接口未提供的功能,请使用TreeNode对象,但尽量避免强制转换操作

一旦通过将对象作为其超级类型之一传递而忘记了对象的真实类型,就没有安全的方法将其取回,因为:

子类型实例是一个超类型实例

但是

超类型实例不是子类型实例

因此,从逻辑上讲,您不能从超级类型实例返回到子类型实例——至少在没有任何安全保证的情况下

唯一的解决方法是在需要执行子类型操作时记住子类型。如果您的语言支持协变返回类型,则可以使用协变返回类型来改进接口定义,这样子类型就不会不必要地丢失:

In ITree:
    public function getParent():ITree

In TreeNode:
    public function getParent():TreeNode
这样,当您在TreeNode上调用getParent()时,您得到的是TreeNode,而不是ITree。当然,如果在声明为ITree的同一个实例上调用getParent(),则返回一个ITree。一旦忘记了类型信息,就太晚了

在类内部,您可以选择处理实类型。您仍然可以在接口中与外部世界对话(故意丢失超级类型信息),但实现可以处理实类型而不是接口

我在用Java做一些代码的实验。如果您感兴趣,请点击这里:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.junit.Assert;
import org.junit.Test;

public class TestTree {
    @Test
    public void testGetDepth() {
        ITreeNode bigTree = TreeNode.create(null, 10);       
        ITreeNode left    = bigTree.getChildren().get(0);

        Assert.assertEquals(10, bigTree.getHeight());
        Assert.assertEquals(9,  left.getHeight());

        Assert.assertEquals(0, bigTree.getDepth());
        Assert.assertEquals(1, left.getDepth());
    }
}

interface ITree {
    List<? extends ITree> getChildren();
    ITree                 getParent();
}

interface ITreeNode extends ITree {
    @Override
    List<? extends ITreeNode> getChildren();

    int getDepth();
    int getHeight();
}

class TreeNode implements ITreeNode {
    private ITreeNode m_parent;

    private final TreeNode m_left;
    private final TreeNode m_right;
    private final List<ITreeNode> m_children;

    public static ITreeNode create(ITreeNode parent, int depth) { 
        TreeNode node = createDescendants(depth);

        node.setParents(parent);

        return node;
    }

    private static TreeNode createDescendants(int depth) { 
        if (depth == 0) {
            return new TreeNode(null, null, null);
        }
        else {
            return new TreeNode(null, TreeNode.createDescendants(depth - 1), TreeNode.createDescendants(depth - 1));
        }
    }

    private TreeNode(ITreeNode parent, TreeNode left, TreeNode right) {
        m_parent = parent;
        m_left   = left;
        m_right  = right;

        List<ITreeNode> children = new ArrayList<ITreeNode>();
        children.add(left);
        children.add(right);
        m_children = Collections.unmodifiableList(children);
    }

    private void setParents(ITreeNode parent)
    {
        m_parent = parent;

        if (m_left != null)
            (m_left).setParents(this);

        if (m_right != null)
            m_right.setParents(this);
    }

    public List<? extends ITreeNode> getChildren() {
        return m_children;
    }

    public ITreeNode getParent() {
        return m_parent;
    }

    public int getDepth() {
        int depth = 0;

        if (m_parent != null) {
            depth = m_parent.getDepth() + 1;
        }

        return depth;
    }

    public int getHeight() {

        int leftHeight  = (m_left == null)  ? 0 : m_left.getHeight() + 1;
        int rightHeight = (m_right == null) ? 0 : m_right.getHeight() + 1;

        return Math.max(leftHeight, rightHeight);
    }
}
import java.util.ArrayList;
导入java.util.Collections;
导入java.util.List;
导入org.junit.Assert;
导入org.junit.Test;
公共类测试树{
@试验
公共void testGetDepth(){
ITreeNode bigTree=TreeNode.create(null,10);
ITreeNode left=bigTree.getChildren().get(0);
Assert.assertEquals(10,bigTree.getHeight());
Assert.assertEquals(9,left.getHeight());
Assert.assertEquals(0,bigTree.getDepth());
Assert.assertEquals(1,left.getDepth());
}
}
接口ITree{

列表您如何看待定制继承接口,例如:

public interface ITree {
  function getParent():ITree;
}
如果我有一个CustomTreeNode类(,它实现了ICustomTreeNode),那么我将创建一个新的ITree子接口:

public interface ICustomTree extends ITree {
  function getCustomParent():CustomTreeNode;
}

这样,我就可以使用正确类型的对象。

很难想象你在讨论什么。你能发布代码/伪代码来说明你在说什么吗?我同意减少耦合并避免强制转换,这就是为什么这不是一个明显的问题,至少对我来说。我想应该有第三种方法来解决它,如果它有ge的话一般方法。如果需要使用函数foo(),则版本2更可取或将此函数添加到接口。接口假设任何对象都可以实现它,因此如果有人添加CustomTreeNode,则会导致错误。我不确定是否将这些函数添加到接口。例如,受保护的方法不应可见,尽管它们正在执行重要的内部任务。Tha不幸的是,在AS3中,没有办法实现Java、C#等泛型类型,也不能使用使用子类型的重写函数。