假設我在抽象/父類中定義了一個方法的實作,然后創建了一個預期使用該方法的派生類。如果派生類沒有覆寫方法的實作,那么為了讓閱讀代碼的人清楚起見,仍然定義它并呼叫 super 是一種好習慣嗎?
class Foo{
speak() { console.log('Hi') }
}
class Bar extends Foo {
// Should I include this? :
speak(){
super.speak()
}
}
這樣做的唯一好處是可讀性和斷言 aBar是預期的speak(),但它也是多余的,因為它在實作方面沒有添加任何東西。所以我想知道宣告派生類時的最佳實踐是什么?
uj5u.com熱心網友回復:
這樣做沒有意義,extends Foo很明顯Bar可能是從 繼承特性Foo,而且它引入了維護危險:
- 假設您決定
speak應該回傳一個值并Foo進行相應的編輯?然后Bar.speak是錯誤的,因為它不回傳呼叫的結果super.speak()。 - 假設您向
Foo? 現在,如果你要遵循應該在子類中重新定義它的規則,你必須找到每個子類來添加一個新方法——不可避免地會遺漏一些。
還有效率問題:您無緣無故地在呼叫堆疊中引入了另一層。
雖然在這些事情上經常有意見的空間,但我認為對此的共識是普遍的:除非您需要自定義方法的行為,否則不要覆寫方法。
uj5u.com熱心網友回復:
就個人而言,我認為,如果實作是相同的,那么在你的問題中再次宣告它是沒有意義的。對我來說,它并沒有使代碼更具可讀性,因為它只是一個沒有目的的額外代碼塊。
我認為您應該只聲??明具有與其超類不同的實作的方法,或者根本不宣告拋出 NotImplemented 的實作。
*編輯:正如另一個答案所提到的,沒有任何實際目的的覆寫方法可能會產生維護問題,這通常應該避免。
uj5u.com熱心網友回復:
哪個更具可讀性?
什么時候應該使用繼承?
class A {
one(a, b) {
//implementation
}
}
class B extends A {
two(e, f, g) {
//implementation
}
}
class C extends B {
three(l, m, n) {
//implementation
}
}
class D extends C {
four(p, q) {
//implementation
}
}
class E extends D {
five(x, y, z) {
//implementation
}
}
或者什么時候不是?
class A {
one(a, b) {
//implementation
}
}
class B extends A {
one(a, b) {
return super.one(a, b);
}
two(e, f, g) {
//implementation
}
}
class C extends B {
one(a, b) {
return super.one(a, b);
}
two(e, f, g) {
super.two(e, f, g);
}
three(l, m, n) {
//implementation
}
}
class D extends C {
one(a, b) {
return super.one(a, b);
}
two(e, f, g) {
return super.two(e, f, g);
}
three(l, n, m) {
return super.three(l, m, n);
}
four(p, q) {
//implementation
}
}
class E extends D {
one(a, b) {
return super.one(a, b);
}
two(e, f, g) {
return super.two(e, f, g);
}
three(l, m, n) {
return super.three(l, m, n);
}
four(p, q) {
return super.three(p, q);
}
five (x, y, z) {
//implementation
}
}
也許我對代碼塊的描述有點過頭了,但這里有另一個問題:你發現錯誤了嗎?雖然第二個塊重新宣告了方法,但其中一個塊是錯誤的。在C方法two中不回傳值super.two()。這意味著繼承的方法與父方法不同。即使現在 B#two()(繼承的方法)沒有回傳值,將來也可能。這是更糟糕的情況。
重新宣告所有方法的建議方法會導致很多無用的樣板。上面的例子可能是一個例外值(在大多數情況下繼承往往相當淺),但在現實世界的代碼庫中并非不可能找到。但即使是只有 3 級繼承(更常見)的較淺層次結構也容易受到與此相同的代碼膨脹的影響。
重復的代碼使閱讀和理解正在發生的事情變得更加困難。此外,它還帶來了高昂的維護成本。即使現在沒有錯誤,您如何更改繼承鏈上的任何方法?您還需要更改下游的所有子項。one()想象一下需要向in添加第三個引數A- 您需要修改五個簡單地重新宣告它的地方。
如果宣告一個方法接受可變引數然后轉發它們,則有一種解決方法。但這也有缺點。首先,這是它的外觀:
foo(...args) {
return super.foo(...args);
}
此方法將與父方法相同。如果使用三個引數呼叫,它將轉發所有三個引數。如果用四個呼叫,它將轉發四個。并且總是回傳與 super 方法回傳的相同。話雖如此,它并不更具可讀性。考慮命名引數:
foo(stockId, newPrice, validFrom) {
//some implementation
}
僅查看引數名稱就可以更清楚地了解如何使用此方法。使用剩余引數的可變引數宣告模糊了該含義。并且每次都重新輸入引數并沒有好多少 - 然后你會在重構鏈時遇到同樣的問題。將名稱更改為更具描述性,例如,validFrom仍validFromISODate需要更新多個位置。
在每個孩子中重新宣告方法效率極低。這是一個維護問題,它具有引入細微錯誤的高風險,并且在引入清晰度的目標上失敗了。
如果到目前為止我寫的內容還不夠令人信服,那么您是否發現了我示例中的第二個錯誤?D 類交換繼承方法中的兩個引數。或者它根本就是一個錯誤?這可能是正確的和故意的嗎?我們實際上不能說,因為無論如何都重新宣告了所有方法。可能是錯字,可能是重構問題,可能是故意的。很難弄清楚意圖。重新宣告絕對無助于澄清。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/496617.html
標籤:javascript 哎呀
