整合重复的初始化逻辑
我们在开发中会在一个类中定义不同版本的构造函数,这些构造函数往往存在相同的初始化逻辑。遇到这种情况一部分开发人员会在每个构造函数中编写相同的代码,这种编写代码的方式是比较低级的,正确的做法应该是将相同的初始化逻辑提取到一个公用的构造函数中,并让其他构造函数直接或间接的调用。这样既可以减少重复的代码,又可以使得编译器根据初始化命令生成更高效的目标代码。下面我们就根据上面所说的内容来编写一下代码。
public class Demo
{
private List<int> data;
private string name;
public Demo()
:this(0,"")
{}
public Demo(int initCount)
:this(initCount,string.Empty)
{}
public Demo(int initCount,string name)
{
data = initCount>0?new List<int>(initCount):new List<int>();
this.name=name;
}
}
我们使用上面的方式来编写构造函数的话,我们需要考虑不同参数的组合形式,也就是说我们需要提供无参的构造函数、只包含 initCount 参数的构造函数、只包含 name 参数的构造函数,以及同时包含这两个参数的构造函数。那么,如果类中新增了一个新的成员,这时我们就需要编写更多的构造函数来适应更多的参数组合。遇到这种情况我们就应该在开发中编写带有默认值的构造函数,这样调用方使用我们编写的构造函数的时候会很灵活。对于开发这个类的开发人员来说减少了代码的编写量。下面我们就来改写一下上面的代码。
pblic class Demo
{
private List<int> data;
private string name;
public Demo()
:this(0,string.Empty)
{}
public Demo(int initCount=0,string name="")
{
data = initCount>0?new List<int>(initCount):new List<int>();
this.name=name;
}
}
Tip: 这里需要注意的是,如果我们想让调用方通过 new Demo() 的写法调用函数的话就必须显示定义无参构造函数。
带有默认值参数的构造函数虽好,但是他也有缺点。他会令客户端代码和该类高耦合,尤其是会令型形参的名称和默认值成为公共接口的一部分,如果修改了默认值那么客户端就必须重新编译一遍才能使那些使用旧默认值的代码转而使用新的默认值,那么这时使用重载构造函数的方法是最好的。对于上面的代码来说使用参数默认值机制来编写构造函数是最好的方式,但是有些 API 会使用反射创建对象,这时就需要依赖于无参构造函数,但是这又出现了文章一开始所提到的问题–代码重复。要避免这个问题我们可以使用链式调用,也就是说用一个构造函数去调用本类的另一个构造函数,看到这里一定会有读者会说我们可以将公用代码提取到一个方法中,构造函数调用这个方法,何必用链式调用呢?这种方式虽然和链式调用的效果类似但是效率很低,这是因为编译器会在每个构造函数里都执行同一个操作,也就是说要添加语句来设定各种变量的初始值并调用基类的构造函数,进而在每个构造函数里都执行一遍这个代码。而是用链式调用的方法编译器不会在每个构造函数里都去调用基类的构造函数,也不会把初始化成员变量所需要的逻辑在每个构造函数中重复一遍,它只会在最后那个构造函数里调用基类的构造函数。