从构造函数调用抽象方法的安全替代方法

时间:2014-04-18 19:20:39

标签: java oop design-patterns

我知道这是一个坏主意,它会导致错误。问题是,我需要"打算"行为。

"低":

// simplified example
abstract class Low {        
    String name;

    public Low(String name) {
        this.name = name;
    }       

    public Low(int id) {
        this.name = getNameForId(id);
    }  

    public Low() {} // will be loaded later

    @Override
    public String toString()
    {
        return name;
    }   

    public void load(InputStream in) {
        // --- grab ID from stream ---
        this.name = getNameForId(id);
    }

    protected abstract String getNameForId(int id);
}

和"高":

class High extends Low {        
    public High(int id) { super(id); }      
    public High(String name) { super(name); } 
    public High() {} // will be loaded later

    @Override
    protected String getNameForId(int id)
    {
        return Registry.getName(id);
    }
}

请注意,在这种特殊情况下,它会正常工作。但是,一旦最重要的方法需要使用某些字段,它就会崩溃。

如何做得更好?

2 个答案:

答案 0 :(得分:1)

您可以通过添加public void load(int id)方法(就像对InputStream一样)并删除Constructor(int id)来避免调用抽象方法。

您可能希望添加一些Factory功能,以确保在没有正确名称值的情况下永远无法访问构造的实例。

答案 1 :(得分:1)

您想要从Low和High对象中分离出ID的名称。引入一个用于加载ID的接口的接口。

public interface NameProvider
{
  String getNameForId(String id);
}

为每个名称来源添加特定的实现。

public class InputStreamNameProvider implements NameProvider
{
  private InputStream inputStream;

  // Constructor 

  public String getNameForId(String id)
  {
     // return name loaded via inputStream
  }
}

public class RegistryNameProvider implements NameProvider
{
  public String getNameForId(String id)
  {
     return Registry.getName(id);
  }
}

然后,您可以向Low添加一个新构造函数,该构造函数将NameProvider和String id作为参数

public Low(NameProvider provider, String id)
{
  this(provider.getNameForId(id));
}

甚至在构造Low或High实例之前使用名称提供程序。主要思想是分离从低和高对象加载ID的名称。