2016-02-09 45 views
2

我使用this series中的前兩個視頻來了解一些基本的OOP概念。最近,我主要寫Node,所以我在前端和後端使用原型繼承。但是,這些教程展示了Java的OOP概念。 Java是一種嚴格類型的語言,它使用古典繼承如何處理來自兩個相似子類的繼承?

這個問題屬於古典和原型繼承,但以不同的方式。

這個問題有點難以言喻,所以我會用一個例子:

我已經創建了一個超類中調用動物。然後我創建了兩個動物分類:。現在我的程序需要兩個子類的混合。創建一個mule實際上似乎有點棘手。

起初答案似乎很明顯;創建一個獨立的mule子類。但那種打敗OOP的目的。當我已經具備這些特性時創建一個新的子類是違反DRY原理的

要確認這是一個合適的方法來創建我的騾子我問自己兩個問題:

1)是騾子

2)是騾子

答案似乎是一個響亮的種類傾向於

我完全失去了如何用古典繼承來實現這一點。我不能拿出我認爲的「好」解決方案與接口或抽象類。

在其使用原型繼承像JavaScript語言,我可能拉下僅適用於騾子的方法和實例變量「選擇性繁殖」騾子。但是,這似乎與創建一個全新的子類非常接近。

什麼是「正確」來處理這兩個古典原型繼承這個問題呢?

回答

1

你正在尋找的概念是特質(你實際上已經提到它)。我將使用一個不同的例子,我覺得更合適:

trait Engine { 
    public function startEngine() { 
     echo 'Vrooom'; 
    } 
} 

trait Saddle { 
    public function rideOnSaddle() { 
     echo 'I feel the wind'; 
    } 
} 

interface Vehicle { 
    public function go(); 
} 

class Car extends Vehicle { 
    use Engine; 

    public function go() { 
     echo $this->startEngine(); 
    } 
} 

class Bike extends Vehicle { 
    use Saddle; 

    public function go() { 
     echo $this->rideOnSaddle(); 
    } 
} 

class Motorcycle extends Vehicle { 
    use Engine; 
    use Saddle; 

    public function go() { 
     echo $this->startEngine(); 
     echo $this->rideOnSaddle(); 
    } 
} 

延伸閱讀:Traits in PHPTraits in Javascript

0

起初答案似乎很明顯;創建一個獨立的mule子類。但這種打敗OOP的目的。當我已經具備這些特性時,創建一個新的子類 違反了DRY原則。

分解可能有助於達到DRY目標。

不是很明顯應該被繼承的每個行爲/角色,可能被認爲是被實現爲混入特質。 因此,他們的代碼在不同的地方在課堂級別重複使用,現在通過構圖變得更容易和更優雅。

至於JavaScript,只有代表團。一方面的繼承由隱式委託自動化 通過原型鏈支持,而組合通過通過callapply明確授權功能來實現。

這使事情變得更容易,因爲只需要處理對象/實例和方法/函數對象。 JavaScript中的類/繼承 部分由構造函數和每個構造函數的prototype或作爲原型傳遞給Object.create的(藍圖)對象覆蓋。工廠將在提供API和隱藏上述方法的一個 的優選實現中將是有用的。

但是核心原則保持不變與他們兩個......用)對象函數對象 用b處理)繼承與C)組成

以下提供的示例因此僅選擇ECMAScript-3功能和構造函數。從那裏可以容易地將 傳送(/傳送)到類別語法或Object.create

OP的例子是很好的選擇,因爲騾子既不是馬也不是驢。它仍然屬於Equus屬性,但其特徵爲 其自身染色體對與馬或驢不同。然而它的特點是它們的行爲和可見標記 。因此,如果有的話,繼承是通過Equus來實現的。其他行爲和外觀,要麼是具體的 或通用於每個物種只是將混合成一個騾子。

功能基於混入/特徵/ JavaScript中的人才始終會應用於對象/實例,因此,即使行爲將通過原型鏈繼承 ,可以收集成可再次應用於原型這樣的功能如果必要/適當的話。

以下示例使用此技術並對其進行評論,以便展示在這2個JavaScript代理級別下的DRY-ness和代碼重用 。

var 
 
    INITIAL_STATES_CONFIG = { 
 
    equus: { 
 
     specifics: { 
 
      type: "equus" 
 
     }/*, 
 
     generics: { 
 
     }*/ 
 
    }, 
 
    horse: { 
 
     specifics: { 
 
      type: "horse" 
 
     }/*, 
 
     generics: { 
 
     }*/ 
 
    }, 
 
    donkey: { 
 
     specifics: { 
 
      type: "donkey" 
 
     }/*, 
 
     generics: { 
 
     }*/ 
 
    }, 
 
    mule: { 
 
     specifics: { 
 
      type: "mule" 
 
     }/*, 
 
     generics: { 
 
     }*/ 
 
    } 
 
    }; 
 

 

 

 
function withToeltGait() { // function based mixin/trait/talent. 
 
    this.toelt = function() { 
 
    return "... tölt ..."; 
 
    }; 
 
    return this; 
 
} 
 

 

 
function withEquusGenerics(/* state */) { // function based mixin/trait/talent composite. 
 
    var 
 
    equus = this; 
 

 
    // implementation of equus generics. 
 

 
    equus.walk = function() { 
 
    return "... walk ..."; 
 
    }; 
 
    equus.trot = function() { 
 
    return "... trot ..."; 
 
    }; 
 
    equus.gallop = function() { 
 
    return "... gallop ..."; 
 
    }; 
 
    withToeltGait.call(equus); // composition: use/apply specific equus trait. 
 

 
    return equus; 
 
} 
 
function withEquusSpecifics(state) { // function based mixin/trait/talent. 
 
    var 
 
    equus = this; 
 

 
    // implementation of equus specifics. 
 

 
    equus.valueOf = function() { 
 
    return Object.assign({}, state); 
 
    }; 
 
    equus.toString = function() { 
 
    return JSON.stringify(state); 
 
    }; 
 

 
    return equus; 
 
} 
 

 
function Equus(state) { // constructor, kept generic via mixin/trait/talent composition. 
 
    state = ((typeof state === 'object') && state) || {}; 
 
    var 
 
    equus = this; 
 

 
    withEquusSpecifics.call(equus, state); // composition: use/apply specific equus trait. 
 

 
    return equus; 
 
} 
 
// equus inheritance via trait based generic equus composite object. 
 
Equus.prototype = withEquusGenerics.call(new Equus/*, state */); 
 

 

 
console.log("Equus.prototype.valueOf() : ", Equus.prototype.valueOf()); 
 
console.log("Equus.prototype.toString() : ", Equus.prototype.toString()); 
 

 
console.log("Equus.prototype.walk() : ", Equus.prototype.walk()); 
 
console.log("Equus.prototype.trot() : ", Equus.prototype.trot()); 
 
console.log("Equus.prototype.toelt() : ", Equus.prototype.toelt()); 
 
console.log("Equus.prototype.gallop() : ", Equus.prototype.gallop()); 
 

 
console.log("\n"); 
 

 

 
var equus = new Equus(INITIAL_STATES_CONFIG.equus.specifics); 
 

 
console.log("equus.valueOf() : ", equus.valueOf()); 
 
console.log("equus.toString() : ", equus.toString()); 
 

 
console.log("equus instanceof Equus ? ", (equus instanceof Equus)); 
 

 

 
console.log("+++ +++ +++\n\n"); 
 

 

 

 
function withHorseGenerics(/* state */) { // function based mixin/trait/talent. 
 
    /* 
 
    implementation of horse generics. 
 
    */ 
 
    var 
 
    horse = this; 
 

 
    // almost all of today's horse breeds lost theirs genetic tölt predisposition. 
 
    horse.toelt = function() {}; 
 

 
    horse.alwaysAlertedAndFleeQuickly = function() { 
 
    return "... always alerted and flee quickly ..."; 
 
    }; 
 
    return horse; 
 
} 
 
function withHorseSpecifics(/* state */) { // function based mixin/trait/talent. 
 
    /* 
 
    implementation of horse specifics. 
 
    */ 
 
    return this; 
 
} 
 

 
function Horse(state) { // constructor, kept generic via mixin/trait/talent composition. 
 
    state = ((typeof state === 'object') && state) || {}; 
 
    var 
 
    horse = this; 
 

 
    Equus.call(horse, state);     // - fulfilling proper equus composition. 
 
    withHorseSpecifics.call(horse/*, state */); // - composition: use/apply specific horse trait. 
 

 
    return horse; 
 
} 
 
// equus inheritance together with generic horse trait composition. 
 
Horse.prototype = withHorseGenerics.call(new Equus/*, state */); 
 

 

 
var horse = new Horse(INITIAL_STATES_CONFIG.horse.specifics); 
 

 
console.log("horse.valueOf() : ", horse.valueOf()); 
 
console.log("horse.toString() : ", horse.toString()); 
 

 
console.log("horse instanceof Horse ? ", (horse instanceof Horse)); 
 
console.log("horse instanceof Equus ? ", (horse instanceof Equus)); 
 

 
console.log("horse.walk() : ", horse.walk()); 
 
console.log("horse.trot() : ", horse.trot()); 
 
console.log("horse.toelt() : ", horse.toelt()); 
 
console.log("horse.gallop() : ", horse.gallop()); 
 

 
console.log("horse.alwaysAlertedAndFleeQuickly() : ", 
 
    (horse.alwaysAlertedAndFleeQuickly && horse.alwaysAlertedAndFleeQuickly()) 
 
); 
 
console.log("horse.beAttentiveCalculateAndRatherFight() : ", 
 
    (horse.beAttentiveCalculateAndRatherFight && horse.beAttentiveCalculateAndRatherFight()) 
 
); 
 
console.log("\n"); 
 

 

 
var toeltingHorse = new Horse(INITIAL_STATES_CONFIG.horse.specifics); 
 
withToeltGait.call(toeltingHorse); 
 

 
console.log("toeltingHorse.valueOf() : ", toeltingHorse.valueOf()); 
 
console.log("toeltingHorse instanceof Horse ? ", (toeltingHorse instanceof Horse)); 
 
console.log("toeltingHorse instanceof Equus ? ", (toeltingHorse instanceof Equus)); 
 
console.log("toeltingHorse.toelt() : ", toeltingHorse.toelt()); 
 

 
console.log("+++ +++ +++\n\n"); 
 

 

 

 
function withDonkeyGenerics(/* state */) { // function based mixin/trait/talent. 
 
    /* 
 
    implementation of donkey generics. 
 
    */ 
 
    var 
 
    donkey = this; 
 

 
    // donkey breeds, as far as I know, still have the genetic 
 
    // predisposition for tölt, but they need to get trained. 
 
    // 
 
    // donkey.toelt = function() {}; 
 

 
    donkey.beAttentiveCalculateAndRatherFight = function() { 
 
    return "... be attentive, calculate and rather fight ..."; 
 
    }; 
 
    return donkey; 
 
} 
 
function withDonkeySpecifics(/* state */) { // function based mixin/trait/talent. 
 
    /* 
 
    implementation of donkey specifics. 
 
    */ 
 
    return this; 
 
} 
 

 
function Donkey(state) { // constructor, kept generic via mixin/trait/talent composition. 
 
    state = ((typeof state === 'object') && state) || {}; 
 
    var 
 
    donkey = this; 
 

 
    Equus.call(donkey, state);     // - fulfilling proper equus composition. 
 
    withDonkeySpecifics.call(donkey/*, state */); // - composition: use/apply specific donkey trait. 
 

 
    return donkey; 
 
} 
 
// equus inheritance together with generic donkey trait composition. 
 
Donkey.prototype = withDonkeyGenerics.call(new Equus/*, state */); 
 

 

 
var donkey = new Donkey(INITIAL_STATES_CONFIG.donkey.specifics); 
 

 
console.log("donkey.valueOf() : ", donkey.valueOf()); 
 
console.log("donkey.toString() : ", donkey.toString()); 
 

 
console.log("donkey instanceof Donkey ? ", (donkey instanceof Donkey)); 
 
console.log("donkey instanceof Equus ? ", (donkey instanceof Equus)); 
 

 
console.log("donkey.walk() : ", donkey.walk()); 
 
console.log("donkey.trot() : ", donkey.trot()); 
 
console.log("donkey.toelt() : ", donkey.toelt()); 
 
console.log("donkey.gallop() : ", donkey.gallop()); 
 

 
console.log("donkey.alwaysAlertedAndFleeQuickly() : ", 
 
    (donkey.alwaysAlertedAndFleeQuickly && donkey.alwaysAlertedAndFleeQuickly()) 
 
); 
 
console.log("donkey.beAttentiveCalculateAndRatherFight() : ", 
 
    (donkey.beAttentiveCalculateAndRatherFight && donkey.beAttentiveCalculateAndRatherFight()) 
 
); 
 
console.log("+++ +++ +++\n\n"); 
 

 

 

 
function withMuleGenerics(/* state */) { // function based mixin/trait/talent composite. 
 
    /* 
 
    implementation of mule generics. 
 
    */ 
 
    var 
 
    mule = this; 
 

 
    withDonkeyGenerics.call(mule/*, state */); // composition: use/apply generic donkey trait. 
 
    /* 
 
    add or delete mule generic properties afterwards. 
 
    */ 
 

 
    withHorseGenerics.call(mule/*, state */); // composition: use/apply generic horse trait. 
 
    /* 
 
    add or delete mule generic properties afterwards. 
 
    */ 
 

 
    // a mules genetic predisposition for tölt is inherited by its mother horse. 
 
    // therefore via calling `withHorseGenerics` this trait gets disabled too by default. 
 

 
    // when facing danger a mule behaves like a donkey; it rather will fight than flee. 
 
    mule.alwaysAlertedAndFleeQuickly = function() {}; 
 

 
    return mule; 
 
} 
 
function withMuleSpecifics(/* state */) { // function based mixin/trait/talent composite. 
 
    /* 
 
    implementation of mule specifics. 
 
    */ 
 
    var 
 
    mule = this; 
 

 
    withDonkeySpecifics.call(mule/*, state */); // composition: use/apply specific donkey trait. 
 
    /* 
 
    add or delete mule specific properties afterwards. 
 
    */ 
 

 
    withHorseSpecifics.call(mule/*, state */); // composition: use/apply specific horse trait. 
 
    /* 
 
    add or delete mule specifics properties afterwards. 
 
    */ 
 

 
    return mule; 
 
} 
 

 
function Mule(state) { // constructor, kept generic via mixin/trait/talent composition. 
 
    state = ((typeof state === 'object') && state) || {}; 
 
    var 
 
    mule = this; 
 

 
    Equus.call(mule, state);     // - fulfilling proper equus composition. 
 
    withMuleSpecifics.call(mule/*, state */); // - composition: use/apply specific mule trait. 
 

 
    return mule; 
 
} 
 
// equus inheritance together with generic mule trait composition. 
 
Mule.prototype = withMuleGenerics.call(new Equus/*, state */); 
 

 

 
var mule = new Mule(INITIAL_STATES_CONFIG.mule.specifics); 
 

 
console.log("mule.valueOf() : ", mule.valueOf()); 
 
console.log("mule.toString() : ", mule.toString()); 
 

 
console.log("mule instanceof Mule ? ", (mule instanceof Mule)); 
 
console.log("mule instanceof Equus ? ", (mule instanceof Equus)); 
 

 
console.log("mule instanceof Donkey ? ", (mule instanceof Donkey)); 
 
console.log("mule instanceof Horse ? ", (mule instanceof Horse)); 
 

 
console.log("mule.walk() : ", mule.walk()); 
 
console.log("mule.trot() : ", mule.trot()); 
 
console.log("mule.toelt() : ", mule.toelt()); 
 
console.log("mule.gallop() : ", mule.gallop()); 
 

 
console.log("mule.alwaysAlertedAndFleeQuickly() : ", 
 
    (mule.alwaysAlertedAndFleeQuickly && mule.alwaysAlertedAndFleeQuickly()) 
 
); 
 
console.log("mule.beAttentiveCalculateAndRatherFight() : ", 
 
    (mule.beAttentiveCalculateAndRatherFight && mule.beAttentiveCalculateAndRatherFight()) 
 
); 
 
console.log("\n"); 
 

 

 
var toeltingMule = new Mule(INITIAL_STATES_CONFIG.mule.specifics); 
 
withToeltGait.call(toeltingMule); 
 

 
console.log("toeltingMule.valueOf() : ", toeltingMule.valueOf()); 
 
console.log("toeltingMule instanceof Mule ? ", (toeltingMule instanceof Mule)); 
 
console.log("toeltingMule instanceof Equus ? ", (toeltingMule instanceof Equus)); 
 
console.log("toeltingMule.toelt() : ", toeltingMule.toelt()); 
 

 
console.log("+++ +++ +++\n\n");

側面說明 - 從2014年4月從2011

  • The many talents of JavaScript for generalizing Role Oriented Programming approaches like Traits and Mixins可以根據混入/特徵/人才在JavaScript

    此外,我建議閱讀一些列在SO上的答案,這些答案也與本主題相關。