从构造函数中调用方法

rma*_*aik 3 java methods constructor function

我编写了下面的代码,如您所见,在构造函数中,我调用了一些方法来执行某些操作.现在我要问的是,从构造函数中调用这些方法是一个好习惯还是将这些方法声明为public并从类info中实例化一个对象,让对象调用这些方法?这有什么好的做法?

码:

class Info {
public RoadInfo(String cityName, double lat, double lng) throws FileNotFoundException, SAXException, IOException, XPathExpressionException {
    // TODO Auto-generated constructor stub
    this.cityname = cityName;
    this.lat = lat;
    this.lng = lng;

    this.path = "c:"+File.separatorChar+this.cityname+".xml";

    System.out.println(path);

    this.initXPath();
    this.method1()
    this.method2()
    ..

    this.expr = "//node[@lat='"+this.lat+"'"+"]/following-sibling::tag[1]/@v";
    this.xPath.compile(this.expr);
    String s = (String) this.xPath.evaluate(this.expr, this.document, XPathConstants.STRING);
    System.out.println(s);
}
Run Code Online (Sandbox Code Playgroud)

Dan*_*try 7

TLDR在我看来,使用构造函数内部的方法是糟糕设计的标志.如果你不是在寻找设计建议,那么答案是"没有任何问题,从技术上讲,只要你避免调用非最终方法"应该没问题.如果您正在寻找设计建议,请参阅下文.

我认为你的示例代码根本不是好的做法.在我看来,构造函数应该只接收与它相关的值,并且不需要对这些值执行任何其他初始化.你无法测试你的构造函数是否与所有这些额外的步骤一起工作 - 你所能做的就是构造对象并希望一切都以正确的状态结束.此外,您的构造函数最终会有多个更改原因,这违反了SRP.

class Info {
public RoadInfo(String cityName, double lat, double lng) throws FileNotFoundException, SAXException, IOException, XPathExpressionException {
    // TODO Auto-generated constructor stub
    this.cityname = cityName;
    this.lat = lat;
    this.lng = lng;

    this.path = "c:"+File.separatorChar+this.cityname+".xml";

    System.out.println(path);

    this.initXPath();
    this.method1()
    this.method2()
    ..

    this.expr = "//node[@lat='"+this.lat+"'"+"]/following-sibling::tag[1]/@v";
    this.xPath.compile(this.expr);
    String s = (String) this.xPath.evaluate(this.expr, this.document, XPathConstants.STRING);
    System.out.println(s);
}
Run Code Online (Sandbox Code Playgroud)

所以,例如,这个构造函数正在加载一个文件,在XPath中解析它.如果我想创建一个RoadInfo对象,我现在只能通过加载文件并且不必担心抛出异常来实现.因为现在你不能测试这一类现在也变得欢快难以进行单元测试this.initXPath()的隔离方法,例如-如果this.initXPath(),this.method1()this.method2()有任何故障,那么你的测试用例每个人都将失败.坏!

我希望它看起来像这样:

class RoadInfoFactory {
  public RoadInfo getRoadInfo(String cityName, double lat, double lng) {
    String path = this.buildPathForCityName(cityName);
    String expression = this.buildExpressionForLatitute(lat);
    XPath xpath = this.initializeXPath();
    XDocument document = ...;

    String s =  (String) xpath.evaluate(expression, document, XPathConstants.STRING);
    // Or whatever you do with it..
    return new RoadInfo(s);
  }
}
Run Code Online (Sandbox Code Playgroud)

别介意你在这里至少有5个职责.

  • 构建与OS无关的路径
  • 为纬度/经度构建XPath表达式
  • 创建XPath文档
  • 检索s- 无论是什么
  • 创建新RoadInfo实例

这些责任中的每一个(除了最后一个)都应该分成它们自己的类(IMO),并将RoadInfoFactory它们全部编排在一起.

  • 是的,但这将是一个很多额外工作的地狱..而且非常脆弱...所以我不知道你为什么会这样做.你会为自己做更多的工作. (3认同)

biz*_*lop 5

构造函数的目的是建立类不变量,即将新创建的对象置于允许客户端使用它们的状态.通常,对象在构造之后依赖于额外的初始化是不好的做法.您要避免的是将这些内容写入文档中:

...创建一个类的实例后X,记得总是打电话 initX(),否则会发生坏事!

虽然在某些情况下很难避免它,但构造函数可能变得非常混乱.例如,在构造函数中加载外部文件是有问题的.

在这些情况下,您可以做两件事:

  1. 重写您的构造函数,因此它需要文件的内容而不是名称.让呼叫者进行加载.主要区别在于您需要调用者创建对象之前执行某些操作,并且您可以使用构造函数的签名来表达它:public RoadInfo(String cityName, Document cityDatabase, double lat, double lng) {...}当然,您可以更进一步,直接需要值,s让调用者执行XPath搜索.请注意,所有这些步骤都会使班级承担单一责任,这被视为一件好事.
  2. 但是现在你需要调用者在构建你的之前执行许多步骤RoadInfo.这是您可以使用工厂的地方,这些工厂也会执行额外的初始化并返回完全构建的RoadInfo对象.

但最重要的是构造函数不能调用正在构造的可被覆盖的对象的任何方法.调用私有方法很好,调用公共方法this不是一个好主意,除非方法或类本身被标记为final.

如果你调用这样的方法,那么重写方法的类总是有可能会破坏你的功能,比如this在构造完成之前暴露给外面的世界.这是一个例子:

public abstract class Foo {
    public Foo(String param) {
       if (this.processParam(param) == null)
          throw new IllegalArgumentException( "Can't process param.");
    }

    protected abstract processParam(String param);
}

public class Bar extends Foo {
    public Bar(String param) {super(param);}

    protected processParam(String param) {
        SomeOtherClass.registerListener(this); // Things go horribly wrong here
        return null; 
    }
}
Run Code Online (Sandbox Code Playgroud)

如果现在调用new Bar("x"),构造函数Foo将抛出异常,因为它认为参数无效.但Bar.processParam()泄露的参考thisSomeOtherClass,可能允许SomeOtherClass使用Bar不应该存在,甚至实例.