mardi 18 mai 2021

How do you hide a class behind an interface when that class contains a package-level method?

How do you hide the following class behind an interface (and instantiate it using a factory)?:

public class TreeNode {
    private List<TreeNode> children;
    private TreeNode parent;

    public void addChild(TreeNode newChild) {
        children.add(newChild);
        newChild.setParent(this);
    }

    public void removeChild(TreeNode child) {
        children.remove(child);
        child.setParent(null);
    }

    void setParent(TreeNode newParent) {
        if(parent != null) {
            parent.removeChild(this);
        }
        parent = newParent;
    }
}

Suppose you were to rename the class to TreeNodeObj and have it implement the TreeNode interface:

class TreeNodeObj implements TreeNode {
    private List<TreeNode> children;
    private TreeNode parent;

    public void addChild(TreeNode newChild) {
        children.add(newChild);
        newChild.setParent(this);
    }

    public void removeChild(TreeNode child) {
        children.remove(child);
        child.setParent(null);
    }

    void setParent(TreeNode newParent) {
        if(parent != null) {
            parent.removeChild(this);
        }
        parent = newParent;
    }
}

public interface TreeNode {
    public void addChild(TreeNode newChild);
    public void removeChild(TreeNode child);
}

public class NodeFactory {
    public static TreeNode createTreeNode() {
        return new TreeNodeObj();
    }
}

This code doesn’t compile, because setParent() isn’t defined in the TreeNode interface (since it should not be called directly and should not be exposed outside the package).

The only solution I can think of is to make the following modification:

public interface TreeNode extends TreeNodePackageAccess {
    public void addChild(TreeNode newChild);
    public void removeChild(TreeNode child);
}

interface TreeNodePackageAccess {
    void setParent(TreeNode newParent);
}

class TreeNodeObj implements TreeNode {
    …[previous code]…

    public void setParent(TreeNode newParent) { 
        // this method is made public in order to implement TreeNodePackageAccess
        …[previous code]…
    }
}

Is there a better way to accomplish this than with the above strategy? (Also, with the above strategy, setParent() is still accessible from outside the package via reflection, so technically, you don't even get package-level encapsulation with it.)

How do you hide the original class behind an interface, given that it requires a package-level method that shouldn’t be exposed?

Aucun commentaire:

Enregistrer un commentaire