如何重构一个超大的类[转载]
转载自 https://zhuanlan.zhihu.com/p/143361527
如何维护和重构超大类,类是会增长和变化的。
理想的类实际工作中的类
一个类是一个清晰的抽象,只处理明确的责任,
但在实际工作中,类会不断成长扩展。随着责任不断增加,类就会变成一团乱麻。
提炼信号
-
拥有大量函数和数据的类,太大且不易理解。
-
如子类化方式影响类的部分特性,或需要多种子类化方式处理。
提炼方法
-
分离某些一起出现、同时变化、彼此相依的数据和函数。
-
自我测试:如果搬移了某些字段和函数,会发生什么事?其他字段和函数是否因此变得无意义?
-
逐步重构:确保重构可随时停下且不影响原功能使用。
如何保持类的优雅
给类添加一项新责任时,为这项责任分离出一个独立的类。
// 重构前
class Person{
get name() {return this._name;}
set name(arg) {this._name = arg;}
get telephoneNumber() {return `(${this.officeAreaCode}) ${this.officeNumber}`;}
get officeAreaCode() {return this._officeAreaCode;}
set officeAreaCode(arg) {this._officeAreaCode = arg;}
get officeNumber() {return this._officeNumber;}
set officeNumber(arg) {this._officeNumber = arg;}
}
// 重构 第一步 分离新类
class Person{
constructor() {
this._telephoneNumber = new TelephoneNumber();
}
get name() {return this._name;}
set name(arg) {this._name = arg;}
get telephoneNumber() {return this._telephoneNumber.telephoneNumber;}
get officeAreaCode() {return this._telephoneNumber.officeAreaCode;}
set officeAreaCode(arg) {this._telephoneNumber.officeAreaCode = arg;}
get officeNumber() {return this._telephoneNumber.officeNumber;}
set officeNumber(arg) {this._telephoneNumber.officeNumber = arg;}
}
class TelephoneNumber {
get officeAreaCode() {return this._officeAreaCode;}
set officeAreaCode(arg) {this._officeAreaCode = arg;}
get officeNumber() {return this._officeNumber;}
set officeNumber(arg) {this._officeNumber = arg;}
get telephoneNumber() {return `(${this.officeAreaCode}) ${this.officeNumber}`;}
}
// 重构 第二步 优化类命名
class Person{
constructor() {
this._telephoneNumber = new TelephoneNumber();
}
get name() {return this._name;}
set name(arg) {this._name = arg;}
get telephoneNumber() {return this._telephoneNumber.toString();}
get officeAreaCode() {return this._telephoneNumber.areaCode;}
set officeAreaCode(arg) {this._telephoneNumber.areaCode = arg;}
get officeNumber() {return this._telephoneNumber.number;}
set officeNumber(arg) {this._telephoneNumber.number = arg;}
}
class TelephoneNumber {
get areaCode() {return this._areaCode;}
set areaCode(arg) {this._areaCode = arg;}
get number() {return this._number;}
set number(arg) {this._number = arg;}
toString() {return `(${this.areaCode}) ${this.number}`;}
}
一个类的构造函数太多的解决办法
- 将有联系的参数联合起来
参考 https://stackoverflow.com/questions/439574/whats-the-best-way-to-refactor-a-method-that-has-too-many-6-parameters
The best way would be to find ways to group the arguments together. This assumes, and really only works if, you would end up with multiple "groupings" of arguments.
For instance, if you are passing the specification for a rectangle, you can pass x, y, width, and height or you could just pass a rectangle object that contains x, y, width, and height.
Look for things like this when refactoring to clean it up somewhat. If the arguments really can't be combined, start looking at whether you have a violation of the Single Responsibility Principle.
先考虑把一些结构联合起来, 比如xywh 结合为Rect
这些工作做完之后, 如果仍然还是有很多成员, 这个时候, 你的类可能已经违反了单一职责原则
- 使用下面这种构造方法
struct ColorspaceConversion {
unsigned width;
unsigned height;
#include "common/builder.h"
BUILDER_MEMBER(ColorspaceDefinition, csp_in)
BUILDER_MEMBER(ColorspaceDefinition, csp_out)
BUILDER_MEMBER(double, peak_luminance)
BUILDER_MEMBER(bool, approximate_gamma)
BUILDER_MEMBER(bool, scene_referred)
BUILDER_MEMBER(CPUClass, cpu)
#undef BUILDER_MEMBER
ColorspaceConversion(unsigned width, unsigned height);
std::unique_ptr<graphengine::Filter> create() const;
};
colorspace::ColorspaceConversion conv{ m_state.planes[0].width, m_state.planes[0].height };
conv.set_csp_in(m_state.colorspace)
.set_csp_out(csp)
.set_approximate_gamma(params.approximate_gamma)
.set_scene_referred(params.scene_referred)
.set_cpu(params.cpu);