【问题标题】:How to handle inheritance from two similar sub-classes?如何处理来自两个相似子类的继承?
【发布时间】:2016-05-18 22:20:49
【问题描述】:

我使用了this series 中的前两个视频来了解一些基本的 OOP 概念。

最近,我主要用 Node 编写代码,所以我在前端和后端使用 原型继承。但是,这些教程展示了 Java 的 OOP 概念。 Java 是一种使用经典继承的严格类型语言。

这个问题涉及经典和原型继承,但方式不同。

这个问题有点难用语言表达,所以我举个例子:

我创建了一个名为 animal 的超类。然后我创建了两个动物子类:horsedonkey。现在我的程序需要两个子类的混合。创建 mule 实际上似乎有点棘手。

起初答案似乎很明显;创建一个独立的 mule 子类。但这违背了 OOP 的目的。当我已经拥有特征时创建一个新的子类是违反 DRY 原则

为了确认这是创建我的 mule 的合适方式,我问了自己两个问题:

1) 骡子是吗?

2) 骡子是吗?

答案似乎是一个响亮的一种,倾向于

我完全不知道如何通过经典继承来实现这一点。我想不出我认为具有接口或抽象类的“好”解决方案。

在像 JavaScript 这样使用 原型继承 的语言中,我可以通过仅下拉应用于>骡子。但是,这似乎与创建一个全新的子类相当接近。

经典原型继承中处理此问题的“正确”方法是什么?

【问题讨论】:

    标签: class oop inheritance composition traits


    【解决方案1】:

    您正在寻找的概念是traits(您实际上提到了它)。我将使用另一个我认为更合适的示例:

    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

    【讨论】:

      【解决方案2】:

      起初答案似乎很明显;创建一个独立的 mule 子类。 但这违背了 OOP 的目的。创建一个新的子类 当我已经拥有这些特征时,就违反了 DRY 原则。

      分解可能有助于达到 DRY 目标。

      每个明显不应该被继承的行为/角色,都可能被认为是实现为 mixintrait。 因此,他们的代码在类级别的不同位置重用现在通过组合变得更加容易和优雅。

      对于 JavaScript,只有委托。一方面继承由隐式委托自动性支持 通过原型链,而组合是通过callapply 显式委派功能来实现的。

      这使事情变得更容易,因为只需要处理对象/实例和方法/函数对象。类/继承 JavaScript 中的一部分被构造函数和每个构造函数的prototype 或(蓝图)对象覆盖 作为原型传递给Object.create。工厂将有助于提供 API 并隐藏一个 API 的首选实现 上面提到的方法。

      尽管如此,它们的核心原则仍然保持不变......处理a)objectsfunction-objects b) 继承 和 c) 组合

      因此,以下提供的示例仅选择了 ECMAScript-3 特性和构造函数。从那里可以 可以轻松转换(/转译)为 class 语法或 Object.create

      OP 的例子选得很好,因为骡子既不是马也不是驴。它仍然属于 Equus 属,但具有 它自己的染色体对与马或驴不同。然而,它具有行为和可见标记 他们俩的。因此,如果有的话,继承是通过 Equus 实现的。其他特定的行为和外观 或对这两个物种中的每一个都通用的只是将混合成一头骡子。

      JavaScript 中基于函数的 mixins/traits/天才总是被应用于对象/实例,因此即使是被继承的行为 通过原型链,可以收集到这样的功能中,如果需要/适当的话,可以再次应用于原型对象。

      以下示例利用了这种技术并对其进行了评论,以展示 DRY-ness 和代码重用 在这 2 个 JavaScript 委托级别。

      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");

      旁注 - 基于函数的 JavaScript 中基于 Mixins / Traits / Talents 的推荐资源

      此外,我确实建议阅读我在 SO 上给出的一些列出的答案,这些答案也与该主题相关。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-09-20
        • 1970-01-01
        • 1970-01-01
        • 2012-04-12
        • 1970-01-01
        • 2011-09-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多