給一個巨大的繼承體系的基類增加新的屬性,如何降低重構強度?

需要給一個巨大的繼承體系(語法樹節點)的基類Node增加parent屬性,並且在每個派生類的構造器中,對每個基類為Node的成員設置parent屬性為this,有沒有什麼好的方法,讓我可以不去挨個修改這94個class的構造函數?

語言是js,如果js有什麼特別方便的方法來做這件事,那再好不過了。

代碼舉例,應很多人要求換成javascript了

之前:

class Type {
constructor() {
}
}

class Pointer extends Type{
/**
* @param {Type} t
*/
constructor(t) {
super();
this.referenceType = t;
}
}

class Array extends Type{
/**
* @param {Type} t
*/
constructor(t) {
super();
this.elementType = t;
}
}

//更多其它的派生類

現在:

class Type {
constructor() {
/**
* @type {Type}
*/
this.parent = null; //&<==========這裡 } } class Pointer extends Type{ /** * @param {Type} t */ constructor(t) { super(); this.referenceType = t; t.parent = this; //&<==========這裡 } } class Array extends Type{ /** * @param {Type} t */ constructor(t) { super(); this.elementType = t; t.parent = this; //&<==========這裡 } } //更多其它的派生類也要修改

有沒有什麼好方法避免我挨個去修改Type的所有派生類?


我們不妨假設你原來的 JS (我就用 TS 寫了,更方便)是這樣:

class Type {
parent: Type;
}

class Pointer extends Type {
public type: Type;
constructor(t: Type) {
super();
this.type = t;
}
}

class Array_ extends Type {
public type: Type;
constructor(t: Type) {
super();
this.type = t;
}
}

思路是這樣的,你的 `t` 都在構造函數的參數列表裡,我們可以通過 `arguments.callee.caller` 獲取一個函數的調用著,在這裡 Type 的構造函數的調用者就是子類的構造函數,那麼我們又可以獲取它的參數列表。這樣如果你要改的東西都是參數的話,我們就可以遍歷參數列表來改它(假如就一個,位置固定啥的,那就直接改):

class Type {
parent: Type;

constructor () {
const parentArguments = arguments.callee.caller.arguments;
for (let i = 0; i &< parentArguments.length; ++i) { parentArguments[i].parent = this; } } } class Pointer extends Type { public type: Type; constructor(t: Type) { super(); this.type = t; } } class Array_ extends Type { public type: Type; constructor(t: Type) { super(); this.type = t; } }

我們可以檢驗一下:

const t = new Type();
const pointer = new Pointer(t);
console.log(pointer.type.parent === pointer) // true


不大規模修改是不可能的。你只有兩種選擇:

1、修改每一個類

2、把每一處new這些類的地方都改為函數調用,which is 更好但是更煩,可能需要改一千個地方

3、不理,然後在一些特殊的地方統一修改parent

因為有parent就代表子樹是被獨佔的。所以你還要做一個檢查:如果你用了一個已經有parent的東西去調用這些構造函數,那麼就地崩潰。所以這不僅僅是修改parent這麼簡單的事情,你還要再改之前看看他是不是undefined。

你可以選擇在整棵樹弄好之後,專門寫一個RecursivelySetParentBecauseTheCodeIsStupid函數,一整棵樹遞歸進去,把每一個東西的parent都賦值好。我猜你是在寫parser/codegen,在這種情況下,調用RecursivelySetParentBecauseTheCodeIsStupid就是最好的方案了,因為構造AST的出口不會太多。


唔,題主想要的是這個效果么?

但我覺得這樣很糟糕


struct Type {
Type* parent = this; //&<==========這裡 }; struct Pointer : Type{ Type* type; Pointer(Type* t) { type = t; } };

這樣不行么, 不過這裡是C++的語法


推薦閱讀:

這個例子中的if else也要重構掉嗎?
為什麼軟體開發需要重構?

TAG:JavaScript | 重構 | C | 編譯原理 | ECMAScript2015 |