インターフェース分離の原則 とは?

インターフェース分離の原則(Interface Segregation Principle, ISP)とは、SOLID原則の一つであり、「クライアントが利用しないメソッドへの依存を強制すべきではない」という考え方です。つまり、大きなインターフェースをより具体的で細かなインターフェースに分割して、クライアントが必要な機能だけを利用できるようにすることを推奨しています。

なぜ インターフェース分離の原則 が重要なのか?

巨大なインターフェースに依存すると、以下のような問題が発生します。

  • 利用しないメソッドの実装を強制される。
  • 変更の影響範囲が広がり、保守性が低下する。
  • クラス間の結合度が上がり、柔軟性が損なわれる。

ISPを適用することで、インターフェースが明確になり、各クラスが本来の責務に集中できるようになります。

解説

以下はISP適用前後のシンプルな例です。

修正前のコード

class MultiFunctionDevice
  def print_document
    puts '印刷する'
  end

  def scan_document
    puts 'スキャンする'
  end

  def fax_document
    puts 'FAXを送信する'
  end
end

class OldPrinter < MultiFunctionDevice
  def scan_document
    raise 'スキャン機能はありません'
  end

  def fax_document
    raise 'FAX機能はありません'
  end
end

問題点

  • OldPrinterクラスでは不要なメソッド(scan_document, fax_document)の実装を強制されている。
  • 実行時にエラーを起こす可能性があり、コードが安全ではない。

修正後のコード

module Printable
  def print_document
    raise NotImplementedError
  end
end

module Scannable
  def scan_document
    raise NotImplementedError
  end
end

module Faxable
  def fax_document
    raise NotImplementedError
  end
end

class MultiFunctionPrinter
  include Printable
  include Scannable
  include Faxable

  def print_document
    puts '印刷する'
  end

  def scan_document
    puts 'スキャンする'
  end

  def fax_document
    puts 'FAXを送信する'
  end
end

class OldPrinter
  include Printable

  def print_document
    puts '印刷する'
  end
end

解決された問題

  • 各クラスが必要最小限のインターフェースだけに依存するようになりました。
  • 不要なメソッドを実装する必要がなくなり、安全性が向上しました。

まとめ

インターフェース分離の原則を守ることによって、クライアントが必要な機能だけに依存できるようになり、柔軟で保守性の高いシステム設計が可能になります。


練習問題 (1)

修正前のコード

class Animal
  def fly; end
  def swim; end
  def run; end
end

class Fish < Animal
  def fly
    raise '魚は飛べません'
  end

  def run
    raise '魚は走れません'
  end
end

修正後のコード

module Flyable
  def fly; end
end

module Swimmable
  def swim; end
end

module Runnable
  def run; end
end

class Fish
  include Swimmable

  def swim
    puts '魚が泳ぐ'
  end
end

解説

インターフェースを細分化したことで、Fishクラスが不要なメソッド(fly, run)を実装する必要がなくなり、無意味な例外処理を回避できました。


練習問題 (2)

修正前のコード

class Worker
  def work; end
  def eat; end
end

class Robot < Worker
  def eat
    raise 'ロボットは食べません'
  end
end

修正後のコード

module Workable
  def work; end
end

module Eatable
  def eat; end
end

class Robot
  include Workable

  def work
    puts 'ロボットが作業をする'
  end
end

解説

RobotクラスはWorkableのみを依存することで、不要なeatメソッドの実装を回避できました。これにより、コードが明確になり、安全性が向上しました。


練習問題 (3)

修正前のコード

class Vehicle
  def drive; end
  def fly; end
end

class Car < Vehicle
  def fly
    raise '車は飛びません'
  end
end

修正後のコード

module Drivable
  def drive; end
end

module Flyable
  def fly; end
end

class Car
  include Drivable

  def drive
    puts '車が走る'
  end
end

解説

インターフェースを分割することで、Carクラスが不要なflyメソッドを実装しなくて済み、安全なコード設計が可能になりました。


練習問題 (4)

修正前のコード

class Player
  def play_audio; end
  def play_video; end
end

class MP3Player < Player
  def play_video
    raise '動画再生はできません'
  end
end

修正後のコード

module AudioPlayable
  def play_audio; end
end

module VideoPlayable
  def play_video; end
end

class MP3Player
  include AudioPlayable

  def play_audio
    puts '音楽を再生します'
  end
end

解説

ISPによりMP3Playerが音楽再生だけをサポートすることを明確に表現でき、不要なメソッドを排除できました。


練習問題 (5)

修正前のコード

class Payment
  def pay_credit; end
  def pay_cash; end
end

class CreditPayment < Payment
  def pay_cash
    raise '現金払いはできません'
  end
end

修正後のコード

module CreditPayable
  def pay_credit; end
end

module CashPayable
  def pay_cash; end
end

class CreditPayment
  include CreditPayable

  def pay_credit
    puts 'クレジットカードで支払い'
  end
end

解説

ISPを適用したことで、CreditPaymentクラスが必要なクレジット払いのみに依存するようになり、現金払いという不要なメソッドを削除できました。結果として、不要な実装を削除し、明確で安全なコードを実現しています。