Java成员初始化模式

Java成员初始化模式,java,oop,Java,Oop,我有一个班级,我的班级: public class MyClass { private MyComplexType member1; } 我必须在member1上进行一些非常密集的初始化。它很容易保证自己的方法,从MyClass构造函数调用 我的问题是,以下哪种格式最适合这种方法 private MyComplexType initMyComplexType() { MyComplexType result = new MyComplexType(); // extensiv

我有一个班级,我的班级:

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();
}
public MyClass() {
  initMember1();
}

private void initMember1() {
  member1 = new MyComplexType();
  // extensive initialization on member1...
}
这样称呼:

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

对于私人会员来说,哪种风格更好?为什么?

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

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


沿着这条线,我也会考虑将init方法保留到如<代码> doExsisivCeFinePosith以将它与实际的成员变量解耦。

选项1。除了Peter提到的原因之外,这是一个更好的实践,因为这样您就有了一个计算密集但无副作用的函数

init()
,以及更轻但状态修改的构造函数。将这两个特性分开是公认的良好实践


此外,使用模板/工厂方法对扩展打开。在子类中重写它(或者使用模板方法重写它的一部分)更容易。同样,这要归功于计算与状态修改的分离


<>编辑:正如其他人所陈述的,也考虑重命名<代码> InCuffic MeCube()/<代码> <代码> Bug DistabeMeMeNe()/< >

< P>只有第一个允许您将结果分配给最终成员,这对我来说已经足够了。

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

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

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打印“家长正在做某事”。
运行StaticOverride打印“正在做某事”

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

通常,您会将代码放在 初始化实例中的实例变量 构造器。有两个 使用构造函数来 初始化实例变量: 初始化程序块和final方法

例如,初始值设定项阻塞 变量看起来就像静态的 初始值设定项阻塞,但没有 静态关键字:

{

    // whatever code is needed for initialization goes here
}
Java编译器复制初始值设定项 块插入每个构造函数。 因此,可以使用这种方法 在用户之间共享一段代码 多个构造函数

无法在中重写最终方法 子类。本节将对此进行讨论 关于接口和继承的课程。 下面是一个使用期末考试的示例 用于初始化实例的方法 变量:

class Whatever {
    private varType myVar = initializeInstanceVariable();

    protected final varType initializeInstanceVariable() {

        //initialization code goes here
    }
}
这在以下情况下特别有用: 子类可能希望重用 初始化方法。方法是 final,因为调用非final 实例初始化期间的方法 可能会引起问题。约书亚·布洛克 在中对此进行了更详细的描述


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

我更喜欢的是,而不是您描述的那种复杂的硬编码初始化。它可以更好地分离关注点,并为单元测试提供更好的环境。

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

同意,但是:我不会将其命名为
initMember1()
,因为它不仅初始化它,而且还创建对象。更好的名称是,根据具体情况,
createMyComplexType()
createMember1()
“使用模板/工厂方法可以扩展。在子类中重写它(或使用模板方法时重写它的一部分)更容易。”但是,(直接或间接)这样做不是一个好主意从构造函数调用虚拟方法。我甚至没有想到这一点。在这种特殊情况下,这不应该是一个问题,但仍然是:好的观点!这不是“覆盖”,就jvm而言,这是两个完全独立的函数。有关详细信息,请参见。也许您可以举个例子?我不认为复杂的初始化可以在Spring applicationContext中通过声明方式完成。对于复杂到不能直接放入声明式应用程序上下文的对象,可以提供FactoryBean使对象适应IoC容器。受保护的静态方法可以由子类重写,请参见我前面答案中的示例