web-dev-qa-db-ja.com

ES6-メンバーを初期化する方が良いのはどこですか-親クラスまたは派生クラス?

ES6で派生クラスのクラスメンバーを初期化するためのより良い方法があるかどうか(子または親)を理解しようとしています。その理由は何ですか。

例えば:

オプション1:

class AbstractAnimal {
   constructor() {
       this.voice = null;
   }

   makeVoice() {
      this.voice.make(); 
   }
}

class Dog extends AbstractAnimal {
   constructor() {
       super();
       this.voice = new Bark();
   }

   onSeeFriend() {
       this.makeVoice();
   }
}

オプション2:

class AbstractAnimal {
   constructor(voice) {
       this.voice = voice;
   }

   makeVoice() {
      this.voice.make(); 
   }
}

class Dog extends AbstractAnimal {
   constructor() {
       super(new Bark());
   }

   onSeeFriend() {
       this.makeVoice();
   }
}

明らかに、両方の方法に長所と短所があります。最初のオプションは、メンバーの初期化を分散させるため、追跡が困難になります。 2番目のオプションでは、すべてが1つの場所にバブルされますが、それよりも、巨大なコンストラクターが多くの引数を取ることになります。

これについてのご意見をお聞かせいただければ幸いです。ありがとう!

1
Yogev

voiceフィールドはAbstractAnimalクラスのメンバーです。 Dogポインタを使用してthisクラスでアクセスする場合、サブクラスに関する限り、voiceは基本クラスのパブリックインターフェイスの一部です。サブクラス。これは、AbstractAnimalDogの両方がスーパークラスに存在するvoiceと、voiceのサポートに依存しているため、2つのクラス間に余分な結合があることを意味します特定の操作。現在、これはJavaScriptがダックタイピングをサポートしているという事実によって軽減されていますが、それでも、これらはプログラマーとして考慮しなければならないことです。

この結合は、AbstractAnimalの内部構造と実装を変更する(たとえば、voiceを削除する、または他の方法で表現する、および/またはそれを使用するいくつかのメソッドを変更する)ことが難しいことを意味します)既存のサブクラスに影響を与えることなく。これはすべて、スーパークラスでvoiceが実際に果たす役割、AbstractAnimal自体に実装されている動作があることを前提としています。そうでない場合は、AbstractAnimalvoiceフィールドを含める必要があるかどうかを検討してください。

コンストラクターを介してスーパークラスメンバーを初期化し、voiceフィールドをスーパークラスのプライベートとして扱うと、AbstractAnimalのインターフェイスの背後に内部の詳細が隠されます。これは一般的には良い考えですが、繰り返しになりますが、デカップリングが面倒な価値があるかどうかを判断する必要があります。そうである場合は、クラスを設計して、それぞれが他の内部に依存することなく、それぞれの仕事を実行し、他のクラスとコラボレーションできるようにします。静的に型付けされた言語では、これをある程度強制することができます。ここでは、開発者の規律と、チーム設定では、チームメンバー間の明確なコミュニケーションが必要です。

もう1つできることは、クライアントコード(継承階層の外側のコード)用の狭いインターフェイスとサブクラス用の広いインターフェイス(いくつかの追加のメソッドと、サブクラスが使用できるフィールドですが、使用できないフィールド)を用意することです。外部から使用する)。これは何らかの形で文書化され、コードの読者に伝達される必要があります。他の言語では、パブリックおよび保護されたアクセス修飾子を使用してこれを行います。ESでは、命名規則(_name)および/または特定のトリック(例: レバレッジクロージャー )。

2番目のオプションは、すべてを1つの場所にバブルしますが、多くの引数を取る巨大なコンストラクターになってしまう可能性があります。

まあ、そのようなコンストラクターで終わらないでください。そのような方法でクラスを設計しないでください。引数が多すぎる場合は、クラスの設計をさらに詳しく調べてください。たぶんあなたのクラスは本当にたくさんのパラメータを必要とします、その場合あなたはそれらをパラメータオブジェクトにバンドルすることができます、しかしより可能性が高いのはあなたのクラスが一度にあまりにも多くのことをしようとしている(彼らはあまりにも多くの責任を処理する)のであなたは壊すべきですそれらをアップします。

最後に、スーパークラスは、サブクラスのbehavioral抽象化である必要があります( [〜#〜] lsp [〜#〜]の意味で) )、データメンバーといくつかの便利な関数を継承できるようにするために、通常は継承を回避する必要がありますが、これは便利です。有名な GoF本 有名な行があります:

クラス継承よりもオブジェクトコンポジションを優先します。

彼らはこれをOOデザインの原則の1つとして述べています。構成はより柔軟な代替手段であり、デザインはそれを使って簡単にすることができます。これは実際には常に達成できるとは限らないことを認めていますが、多くの場合、プログラマーは継承を使いすぎます。

したがって、一般的に言えば、プログラムの対象となる動作の抽象化とサブシステムファサードを作成するために継承を予約し、小さなパーツからオブジェクトを構成する必要がある場合は、従来の構成を使用するか、 mixins を使用します。

1