用Creation Method替换构造函数
当类中有多个构造函数,使用过程中很容易遇到不知道调用哪一个,此时用能够说明意图的返回对象实例的Creation Method替换构造函数
考虑如下的类Loan,C++要求其构造函数的名字必须与它所属的类相同。当构造函数慢慢变多,问题也会越来越明显,当希望实例化一个Loan时,程序员不得不研究需要传入什么参数,琢磨这些构造函数代码,已选择应该调用哪一个构造函数。构造函数本身无法有效和高效地表达意图。越多的构造函数不仅容易造成选择错误,还会减缓开发速度。此外,随着迭代,许多构造函数不再使用但仍留在代码中,大多数情况下没有人会去管这些死掉的构造函数。你是否与我一样嗅到了一丝代码的坏味道呢,没错,死构造函数会使类膨胀。
在这里,作者提出以Creation Method来解决这些问题。所谓Creation Method,就是类中的一个静态或非静态的负责实例化类的新实例的方法。
Creation Method 和 Factory Method
很多人都了解或知道一点“Factory Method”,其来源于《设计模式》DP一书中的一个创建型模式。但并非所有的创建对象的方法都是工厂模式。作者所提Creation Method来指代创建类的实例的静态方法或非静态方法。即所有Factory Method都是Creation Method,但反之则不尽然。
回到上面的类Loan,其可以用来表示贷款方式,这里有三种分别为定期贷款(term loan),循环贷款(revoler),以及循环信用定期贷款(revolving credit term loan,RCTL)。
很多同学或许会跟我一样,在面对这样的情况下很容易想到通过将Loan设计为一个抽象超类,用子类标识各种贷款。所以我希望在对这一系列构造函数进行重构之前,通过以下解释先达成一些共识:
- 区分不同贷款的知识计算资金(capital),收益(income)和期限(duration)等数值。与其为了支持定义贷款(term loan)而创建三个子类,不如通过strategy模式设置3个不同的Strategty类。
- 不同种类贷款切换时,修改Loan实例中的几个字段要比Loan不同子类实例中切换容易的多。
有了共识后我们要对Loan类中的所有构造函数进行重构,先来看Loan类的构造函数:
class Loan {
public:
Loan(double commitment, int riskRating, Date* maturity) {
Loan(commitment, 0.00, riskRating, maturity, nullptr);
}
Loan(double commitment, int riskRating, Date* maturity, Date* expiry) {
Loan(commitment, 0.00, riskRating, maturity, expiry);
}
Loan(double commitment, double outstanding, int riskRating, Date* maturity, Date* expiry) {
Loan(nullptr, commitment, outstanding, riskRating, maturity, expiry);
}
Loan(CapitalStrategy* capitalStrategy, double commitment, int riskRating, Date* maturity, Date* expiry) {
Loan(capitalStrategy, commitment, 0.00, riskRating, maturity, expiry);
}
Loan(CapitalStrategy *capitalStrategy, double commitment, double outstanding, int riskRating, Date *maturity, Date *expiry) {
_commitment = commitment;
_outstanding = outstanding;
_riskRating = riskRating;
_capitalStrategy.reset(capitalStrategy);
_maturity.reset(maturity);
_expiry.reset(expiry);
if (!_capitalStrategy.get()) {
if (!_expiry.get()) {
_capitalStrategy = make_shared<CapitalStrategy>(CapitalStrategyTermLoan());
} else if (!_maturity.get()) {
_capitalStrategy = make_shared<CapitalStrategy>(CapitalStrategyRevolver());
} else {
_capitalStrategy = make_shared<CapitalStrategy>(CapitalStrategyRCTL());
}
}
};
}
在动手重构之前,我还是会忍不住提醒你记住重构成功的秘籍——小步快跑,而保证跑的的方向对的手段是构建测试用例:
class CapitalCalculateTest {
public:
CapitalCalculateTest() {
...
Loan termLoan = new Loan(commitment, riskRating, maturity);
...
}
};
接下来应用Creation Method替换构造函数进行重构:
- 提炼函数:
class CapitalCalculateTest {
public:
CapitalCalculateTest() {
...
Loan termLoan = new CreateTermLoan(commitment, riskRating, maturity);
...
}
static Loan CreateTermLoan(double commitment, int riskRating, Date* maturity) {
return new Loan(commitment, riskR