paint-brush
継承と合成: JavaScript でロールプレイング ゲームを例として使用する@kliukovkin
13,325 測定値
13,325 測定値

継承と合成: JavaScript でロールプレイング ゲームを例として使用する

Georgii Kliukovkin4m2022/12/21
Read on Terminal Reader

長すぎる; 読むには

コンポジションを使用すると、継承の問題を回避できます。JavaScript は、継承の問題を回避するのに最適な言語です。
featured image - 継承と合成: JavaScript でロールプレイング ゲームを例として使用する
Georgii Kliukovkin HackerNoon profile picture

継承の問題

  • 子供のコードの重複
  • 継承階層が過度に複雑
  • 親の行動を変えると、子供にエラーが発生する可能性があります


この記事では、これらの問題とは何か、そしてコンポジションを使用してそれらを解決する方法を見ていきます。


オブジェクト指向言語の問題は、暗黙の環境を持ち歩いていることです。あなたはバナナが欲しかったのですが、手に入れたのはバナナとジャングル全体を持ったゴリラでした. - Erlang の作成者、Joe Armstrong 氏

ロールプレイング ゲームの継承

ロールプレイング ゲームのキャラクターの階層を作成するプロセスを考えてみましょう。最初は、戦士と魔法使いの 2 種類のキャラクターが必要で、それぞれに一定量の体力と名前があります。これらのプロパティは公開されており、親の Character クラスに移動できます。

 class Character { constructor(name) { this.name = name; this.health = 100; } }


戦士はスタミナを消費して攻撃することができます:

 class Warrior extends Character { constructor(name) { super(name); this.stamina = 100; } fight() { console.log(`${this.name} takes a mighty swing!`); this.stamina--; } }


また、メイジは一定量のマナを消費する呪文を唱えることができます。

 class Mage extends Character { constructor(name) { super(name); this.mana = 100; } cast() { console.log(`${this.name} casts a fireball!`); this.mana--; } }

パラディンクラスの問題

では、新しいクラス、パラディンを紹介しましょう。パラディンは、戦うことも呪文を唱えることもできます。どうすればこれを解決できますか?以下に、エレガンスの欠如を共有するいくつかのソリューションを示します。


  • PaladinCharacterの子孫にして、 fight() ) メソッドとcast()メソッドの両方をゼロから実装できます。この場合、作成時に各メソッドが複製され、変更を追跡するために Mage および Fighter クラスのメソッドと常に同期する必要があるため、 DRY原則に違反しています。


  • fight() ) メソッドとcast()メソッドはCharacterクラス レベルで実装できるため、3 つのすべてのキャラクター タイプにそれらが含まれます。これは少し良い解決策ですが、この場合、開発者はメイジのfight()メソッドとウォリアーのcast()メソッドをオーバーライドして、それらを空のメソッドに置き換えるか、エラーを慰める必要があります。

構成

これらの問題は、構成を使用した機能的なアプローチで解決できます。型からではなく、機能から始めれば十分です。基本的に、キャラクターの能力を決定する 2 つの重要な機能があります。それは、戦う能力と呪文を唱える能力です。


これらの機能は、文字を定義する状態を拡張するファクトリ関数を使用して設定できます。

 const canCast = (state) => ({ cast: (spell) => { console.log(`${state.name} casts ${spell}!`); state.mana--; } }) const canFight = (state) => ({ fight: () => { console.log(`${state.name} slashes at the foe!`); state.stamina--; } })


したがって、キャラクターは、これらの能力と初期プロパティのセットによって定義されます。一般的なもの (名前と健康) とプライベートなもの (スタミナとマナ) の両方です。

 const fighter = (name) => { let state = { name, health: 100, stamina: 100 } return Object.assign(state, canFight(state)); } const mage = (name) => { let state = { name, health: 100, mana: 100 } return Object.assign(state, canCast(state)); } const paladin = (name) => { let state = { name, health: 100, mana: 100, stamina: 100 } return Object.assign(state, canCast(state), canFight(state)); }

結論

コンポジションを使用すると、コンポジションを使用して継承の問題を回避できます。javascript はこれを行うのに最適な言語です。