如何编写适当的OOP代码以避免出现字段初始化问题?

时间:2011-10-14 15:19:13

标签: java oop

运行这段代码将打印 null

public class Weird {

    static class Collaborator {
        private final String someText;

        public Collaborator(String text) {
            this.someText = text;
        }

        public String asText() {
            return this.someText;
        }
    }

    static class SuperClass {
        Collaborator collaborator;

        public SuperClass() {
            initializeCollaborator();
        }

        protected void initializeCollaborator() {
            this.collaborator = new Collaborator("whatever");
        }

        public String asText() {
            return this.collaborator.asText();
        }
    }

    static class SubClass extends SuperClass {
        String someText = "something";

        @Override
        protected void initializeCollaborator() {
            this.collaborator = new Collaborator(this.someText);
        }
    }

    public static void main(String[] arguments) {
        System.out.println(new Weird.SubClass().asText());
    }
}

(这里也是GitHub Gist

现在,我知道为什么会发生这种情况(这是因为超类的字段已初始化,然后在子类的字段初始化之前调用超类的构造函数

问题是:

  1. 这里的设计问题是什么?从OOP的角度来看,这种设计有什么问题,所以结果看起来对程序员来说很奇怪?通过这种设计打破了哪些OOP原则?
  2. 如何重构,使它不起作用'怪异'并且是正确的OOP代码?

3 个答案:

答案 0 :(得分:3)

设计有什么问题:你从构造函数中调用实例方法,然后在子类中重写它。不要那样做。理想情况下,只从构造函数体调用private或final实例方法或静态方法。您还在SuperClass之外公开了一个字段(这是一个实现细节),这不是一个好主意 - 但编写受保护的setCollaborator方法并从initializeCollaborator调用该方法会给出同样的问题。

至于如何解决它 - 你真正想要达到的目标并不是很清楚。为什么你需要initializeCollaborator方法呢?可以有各种方法来解决这个问题,但它们实际上取决于 eaxctly 你想要实现的目标。 (哎呀,在某些情况下,最好的解决方案是不要首先使用继承。首选组合而不是继承,以及所有这些:)

答案 1 :(得分:1)

不使用intializeCollaborator方法,而是将Collaborator作为构造函数的参数,并使用super(new Collaborator(..))从子项中调用它。

答案 2 :(得分:1)

我的设计问题是someText字符串应该是Collaborator对象或SubClass的显式依赖项(或“协作者”),或者显式是全局上下文的一部分(所以是常量或共享上下文对象的属性)。

理想情况下,Collaborator应负责检索其依赖项;或者,如果SubClass对此负责,它应该someText作为依赖项(即使它始终初始化为相同的值),并且只在设置someText时初始化Collaborator。

从概念上讲,设计中对象之间的依赖关系强加了初始化的部分排序。实现此排序的机制应始终在您的设计中明确,而不是依赖于Java语言的实现细节。

(过度设计)的例子:

interface ICollaboratorTextLocator {
    String getCollaboratorText();
}

class ConstantCollaboratorTextLocator implements ICollaboratorTextLocator {
    String text;

    ConstantCollaboratorTextLocator(String text) {
        this.text = text;
    }
}

class SuperClass {
    Collaborator collaborator;

    public setCollaboratorTextLocator(ICollaboratorTextLocator locator) {
        collaborator = new Collaborator(locator.getCollaboratorText());
    }

    SuperClass() {
       setCollaboratorTextLocator(new ConstantCollaboratorTextLocator("whatever"));
    }
}

class SubClass {
    String text = "something";

    SubClass() {
        setCollaboratorTextLocator(new ConstantCollaboratorTextLocator(text));
    }
}

(现在请原谅,在写完ConstantCollaboratorTextLocator之后,我需要站在瀑布下。)