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)
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个职责.
s- 无论是什么RoadInfo实例这些责任中的每一个(除了最后一个)都应该分成它们自己的类(IMO),并将RoadInfoFactory它们全部编排在一起.
构造函数的目的是建立类不变量,即将新创建的对象置于允许客户端使用它们的状态.通常,对象在构造之后依赖于额外的初始化是不好的做法.您要避免的是将这些内容写入文档中:
...创建一个类的实例后
X,记得总是打电话initX(),否则会发生坏事!
虽然在某些情况下很难避免它,但构造函数可能变得非常混乱.例如,在构造函数中加载外部文件是有问题的.
在这些情况下,您可以做两件事:
public RoadInfo(String cityName, Document cityDatabase, double lat, double lng) {...}当然,您可以更进一步,直接需要值,s让调用者执行XPath搜索.请注意,所有这些步骤都会使班级承担单一责任,这被视为一件好事.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()泄露的参考this来SomeOtherClass,可能允许SomeOtherClass使用Bar不应该存在,甚至实例.