インターフェース分離の原則 とは?
インターフェース分離の原則(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クラスが必要なクレジット払いのみに依存するようになり、現金払いという不要なメソッドを削除できました。結果として、不要な実装を削除し、明確で安全なコードを実現しています。