Java成员初始化模式

时间:2010-07-06 13:55:28

标签: java oop

我有一个MyClass类:

public class MyClass {
  private MyComplexType member1;
}

我必须在member1上做一些非常激烈的初始化。足够它很容易保证自己的方法,从MyClass构造函数调用。

我的问题是,以下哪种格式最适合此方法?

private MyComplexType initMyComplexType() { 
  MyComplexType result = new MyComplexType();  
  // extensive initialization on result...
  return result;
}

这样叫:

public MyClass() {
  member1 = initMember1();
}

private void initMember1() {
  member1 = new MyComplexType();
  // extensive initialization on member1...
}

这样叫:

public MyClass() {
  initMember1();
}

对于私有成员,哪种风格更好?为什么呢?

7 个答案:

答案 0 :(得分:6)

我会选择第一个选项,因为它更清楚地表达了init方法的用途,并明确地显示了数据流。

更不用说它使init方法中的代码可以重用。如果您以后需要初始化另一个变量,您可以再次调用该方法而不必担心副作用。此外,如果该另一个变量位于另一个类中,您可以轻松地将该方法移动到两个位置都可访问的位置。

沿着这一行,我还会考虑将init方法保留为doExtensiveComplexCalculation之类的东西,以将其与实际的成员变量分离。

答案 1 :(得分:4)

第二种方法的另一个缺点是字段member1可能会将部分初始化的MyComplexType暴露给另一个线程。

回复JörnHorstmann的重写受保护静态方法示例:

public class StaticOverrideParent {

 protected static void doSomething() {
  System.out.println("Parent doing something");
 }
}

public class StaticNoOverride extends StaticOverrideParent {

 public static void main(String[] args) {
  doSomething();
 }
}

public class StaticOverride extends StaticOverrideParent {

 protected static void doSomething() {
  System.out.println("Doing something");
 }

 public static void main(String[] args) {
  doSomething();
 }
}

运行StaticNoOverride会打印“Parent doing something”。 运行StaticOverride会打印“做某事”。

答案 2 :(得分:3)

选择选项1.除了彼得提到的理由之外,这是一种更好的做法,因为这样你有一个计算密集但无副作用的函数init(),以及较轻但状态修改构造函数。将这两个特征分开是一种很好的做法。

此外,使用模板/工厂方法打开以进行扩展。在子类中更容易覆盖它(或者如果你使用模板方法,它的部分)。再次,这要归功于计算与状态修改的分离。

修改:正如其他人所说,也考虑将initComplexMember()重命名为buildContextMember()

答案 3 :(得分:3)

只有第一个允许你将结果分配给最终成员,这对我来说足够了。

答案 4 :(得分:2)

已经给出了非常好的理由(最终成员分配,多线程问题,提高了可读性),技术性非常强对我来说足够了。我将只添加Java教程中的一些摘录:

  

Initializing Instance Members

     

通常,你会把代码放到   初始化一个实例变量   构造函数。那里有两个   使用构造函数的替代方法   初始化实例变量:   初始化程序块和最终方法。

     例如,

初始化程序块   变量看起来就像静态   初始化程序块,但没有   static keyword:

{

    // whatever code is needed for initialization goes here
}
     

Java编译器复制初始化程序   阻塞到每个构造函数中。   因此,可以使用这种方法   共享一段代码   多个构造函数。

     

无法覆盖最终方法   一个子类。这将在讨论中讨论   关于接口和继承的课程。   这是使用决赛的一个例子   初始化实例的方法   变量:

class Whatever {
    private varType myVar = initializeInstanceVariable();

    protected final varType initializeInstanceVariable() {

        //initialization code goes here
    }
}
     

如果,这尤其有用   子类可能希望重用   初始化方法。方法是   决赛因为非最终决定   实例初始化期间的方法   会引起问题。约书亚布洛赫   在中更详细地描述了这一点   Effective Java

我倾向于支持上述样式(除非我不希望在所有构造函数中进行初始化,但这很不寻常)。

答案 5 :(得分:1)

我更喜欢Spring IoC container到你所描述的那种复杂的硬编码初始化。它可以更好地分离关注点,是一个更好的单元测试环境。

答案 6 :(得分:1)

其他评论员已经给出了很好的理由来使用辅助函数来初始化变量。我只是想补充一点,我实际上更喜欢使用私有或受保护的静态功能。这非常清楚,这只是一个初始化助手,它不能修改对象的其他状态,也不能被子类覆盖。