継承:派生的作詞



ステージの設定
継承は、クラス再利用を許可するクラス間の関係や分類を設定します.それは両方の一般的に達しており、一般的にその場所を持ってツールを模索されているが、慎重に使用する必要があります.私たちは私のコンサートSetlistのために新しい歌を書くために遺産を使いますRubyConf 2020 ルビーの話Coverage モジュールです.

歌構造
あなたが歌を作成するとき、それは名前(または、少なくとも、働くタイトル)と一連のメモを必要とします.メモは時間とともに変化するかもしれません、そして、タイトルは洗練されるかもしれません、しかし、我々の目的のために、我々が空のページより少しあるまで、我々は歌をそれと呼んでいません.
class Song
  def initialize(notes: [], name:)
    @notes = notes
    @name = name
  end
end
あなたがバンドや自分の曲を書いているときは、曲を再生できるようにする必要があります.この例では、我々の歌は、既知の数の楽器を持っているバンドのために書かれます.
class Song
  def play
    @notes.map do |note|
      composition = []

      composition << Thread.new { @guitar.play(note) }
      composition << Thread.new { @vocal.sing(note) }
      composition << Thread.new { @drum.hit(note) }
      composition << Thread.new { @keyboardist.program(note) }

      composition.map(&:value)
    end
  end
end
すべてのノート(曲のビートやメジャーを表す)のためにバンドの各メンバーが同時にその部分を再生する必要があります.これらの楽器のすべて一緒にノートに注意して曲を構成します.

繰り返し
夜の夜、何度も何度も同じ曲をツーリングバンドが演奏する.ツアーの各コンサートのために、バンドは、彼らがその夜を再生するすべての曲のsetlistを構築する必要があり、どのような順序で.
class Setlist
  def add_song(song)
    @songs << song
  end
end
すべてのコンサートのために何度も繰り返し、各曲のすべてのメモを転写する退屈と不要です.すべてのその仕事を保存するために、バンドのsetlistに現れることができた各々の歌は、別々のクラスとして目録化されます.
class TheLineBeginsToBlur
  def initialize
    @name = "The Line Begins To Blur"
    @notes = verse_1 + chorus + verse_2 + chorus + solo + outro
  end
end
それはすでに完全に形成された曲ですので、我々は曲やメモの名前の引数を受け入れる必要はありません.ツアーの途中で取り決めを変えるつもりはない.しかし、我々は曲を再生できるようにする必要があります.このように、コピーして貼り付けましょうplay 我々がここで我々の特定の歌のためにすることができる何かとしての方法.
class TheLineBeginsToBlur
  def play
    @notes.map do |note|
      composition = []

      composition << Thread.new { @guitar.play(note) }
      composition << Thread.new { @vocal.sing(note) }
      composition << Thread.new { @drum.hit(note) }
      composition << Thread.new { @keyboardist.program(note) }

      composition.map(&:value)
    end
  end
end
これは素晴らしいので、我々は今我々のsetlistを作成するときに毎晩から引くことができる曲の安定している;しかし、play 各曲の方法は素晴らしいです.の実装play 変更する必要がある、我々はすべての曲を横切ってその変化を伝播する必要があります.我々がAを加えるのを忘れるならばplay 我々の歌のうちの1つに、バンドが互いでぼんやりと見つめているとき、誰でも馬鹿に見えます.

歌の作曲
我々の以前のポストからのメモを取る、我々は単独で曲を再生する責任のあるクラスを構築することができます.
class SongPerformer
  def initialize(notes)
    @notes = notes
  end

  def play
    @notes.map do |note|
      composition = []

      composition << Thread.new { @guitar.play(note) }
      composition << Thread.new { @vocal.sing(note) }
      composition << Thread.new { @drum.hit(note) }
      composition << Thread.new { @keyboardist.program(note) }

      composition.map(&:value)
    end
  end
end
すべての曲は、その演奏者を使用することができますし、それに再生する責任を委任する.
class TheLineBeginsToBlur
  def play
    SongPerformer.new(@notes).play
  end
end
我々は、現在1つの場所に歌を演奏する責任を分離しました.私たちが歌が全体的に演奏される方法を変える必要があるならば、我々はそうすることができますSongPerformer そして、その変化は、我々の歌の全てに反映されます.私たちはパフォーマークラスでも歌に合わせて、同じ曲のアレンジを設定できます.それらの利点でさえ、我々はまだplay コールメソッドSongPerformer .
もう一つのオプションがあります.

ヒットを打つ
我々は、既存の、一般的な活用することができます.Song クラスは、特定の曲についての我々のクラスのすべてを持っているSong クラス.
これをすることによって、我々の異なる歌は、実行する必要はありませんplay メソッド.彼らはこの行動をSong .
class TheLineBeginsToBlur < Song
  def initialize
    super(
      name: "The Line Begins To Blur",
      notes: verse_1 + chorus + verse_2 + chorus + solo + outro,
    )
  end
end
我々は、我々がから継承していることを示しますSong クラスウィズ< Song . Song は“基底クラス”です.私たちのコンストラクタではSong 'sコンストラクタsuper , 歌と曲で演奏すべきノートのタイトルを渡す.TheLineBeginsToBlur がないplay クラス定義.それはまだそれに反応しますSong そして、我々はすべてを継承していますSong という.
我々が組成を議論するとき、我々はSandiメッツのものに言及しましたPractical Object-Oriented Design In Ruby Aをモデル化するとき、彼女の推薦が組成を使用するために、関係があります.その同じセクションでは、彼女はあなたが関係のあるときに継承を使用することをお勧めします.我々のケースでは、特定の曲は私たちの専門のバージョンですSong クラス.
継承はオブジェクト指向言語で共通の設計選択である.特にRubyでは、Railsと共に働いているならば、あなたは場所の上ですべての継承を使用したでしょう.あなたのモデルのすべてはApplicationRecord (最終的にはActiveRecord::Base ) すべてのコントローラがApplicationController (最終的にはActionController::Base ).

測定されたアプローチ
継承には欠点がある.それは一般的に避けることをお勧めします.前に、“継承以上の組成を好む”というフレーズに遭遇した可能性があります.その理由を議論しましょう.

透明性
継承は、特定のクラスがどのような振る舞いをしているかを知ることをより困難にします.私たちの歌のクラスはSong を持っているplay クラス定義のメソッド.しかし、彼ら全員がSong , 彼らは皆反応するplay . それはクラスの迅速な読書に基づいて明らかではない決定.

基底クラスの制限
どんな継承クラスでも、基本クラスがどのようにするかということとは必ずしも違うことをするべきではありません.もちろん、あなたはこれを行うことができますが、それは非常に慎重に使用する必要があります.私たちはplay 特定のクラスのメソッド
残りの動作と再定義play 我々の1つの特別な例外のために.問題は、これらの例外が積み上げ始めると、我々は一般的に離れて欠けて終了すると、基本的なクラスから継承することを意味するものの共有理解は、小さなように見える各変更で浸食される
それ自身で.
我々の歌のために、我々が突然弦楽四重奏のために歌を書く必要があるならばSong クラスは役に立ちません.ギター、ボーカリスト、ドラマー、キーボード奏者を想定している.特にRubyでは、任意のメソッド定義を再定義することができますが、デザインの観点からは、継承の制限を受け入れる必要があります.それらの制限が尊重されないならば、構成のようなもう一つの組織構造を考えてください.

将来の柔軟性
システムが時間とともに進化する方法を知ることは、しばしば不可能です.継承はあなたのシステムがどのようにモデル化されるべきかの非常に具体的な表現にあなたを閉じ込めることができます、そして、機能が開発されたと仮定する仮定は機能が追加されるのを必要として、そして、アプリケーションが成長しなければならないニーズとして真実を保持しないかもしれません.
この剛性は時間をかけて押し戻され、遺伝的構造が維持されにくくなる.私の意見では、この長所の展望は、相続が開業医によってまばらに推薦される主な理由になるということです.それはあなたのシステムの現在および将来の状態についての完全な知識を持っている限り、それは素晴らしい作品をすることができます.現実は、そのような状況では非常にまれです.
この例では、我々のアプリケーションは、ツアーのコース全体を変更すべきではないバンド、メンバーとメイクのコンサートツアーをモデル化しています.我々は、我々がツアーを始めるギタリストが取り替えられるとしても、まだギタリストがいるという賭けをしました、そして、我々は歌の2つをする方法に沿ってフランスのホーン奏者を拾っていません.実用的な観点から、このアプリケーションのコンテキストで、これらの曲の各ステージを再生する方法のこの堅牢な構造に関連付けられて合理的です.しかし、始まりから、我々はすでにこの構造が戻って来るかもしれない1つの方法を特定しました.

ロックオン
継承は、コード再利用を達成する迅速かつ容易な方法としてしばしば到達する.それだけですしかし、それはあなたのシステムに制限と制約を課します.それらの制限は意図的で、必要なガードレールであるかもしれません、しかし、しばしば、彼らは痛み、涙、複数の「コードスパイク」と「技術的な負債sprints」を引き起こす要因であるために終わります.相続はこれに基づいて卸売を避けてはいけません、しかし、それは慎重に、そして、司法的にあなたのシステムに適用されなければなりません.
私たちの次の投稿は、理論から少し離れて移動し、どのようにSonic Piにインターフェイスを構築する方法を探るので、これらの原則は、実際にあなたのコンピュータ上で音を作るために一緒に働くことができます.

This post originally published on The Gnar Company blog.