🎉

インターフェース分離の原則 (Interface Segregation Principle, ISP)

に公開

1. どんなもの?

インターフェース分離の原則(Interface Segregation Principle, ISP)は、クラスが利用しないメソッドを持つインターフェースに依存してはならないという設計原則です。
大きなインターフェースを小さな役割ごとに分割することで、クラスが必要な機能だけを実装するようにします。

例えば、あるクラスが「すべてのメソッドを実装しなければならない大きなインターフェース」に依存すると、余計なコードが増えたり、変更が難しくなります。この原則はそれを防ぎます。

2. 通常の実装方法と比べてどこがすごいの?

通常の方法

大きなインターフェースを持つ場合、必要ないメソッドも実装しなければならず、無駄が生じます。

# NOTE: Workerモジュールでインターフェースを定義する。code, test, deployの3つのメソッドを実装しなければならない
module Worker
  def code
    raise NotImplementedError
  end

  def test
    raise NotImplementedError
  end

  def deploy
    raise NotImplementedError
  end
end

# NOTE: DeveloperクラスはWorkerモジュールをインクルードしているが、deployメソッドを実装していない
class Developer
  include Worker

  def code
    puts "Writing code"
  end

  def test
    puts "Testing code"
  end

  def deploy
    raise "I don't deploy"
  end
end

dev = Developer.new
dev.deploy # => エラー: I don't deploy
  • 課題:
    • Developerクラスはdeployメソッドを実装しないのに、インターフェースの制約上実装しなければならない。
    • 必要のないメソッドを実装することで、コードが煩雑になる。

ISPに基づいた方法

インターフェースを分割することで、クラスが必要な機能だけを実装できるようにします。

module Coder
  def code
    raise NotImplementedError
  end
end

module Tester
  def test
    raise NotImplementedError
  end
end

class Developer
  include Coder
  include Tester

  def code
    puts "Writing code"
  end

  def test
    puts "Testing code"
  end
end

dev = Developer.new
dev.code  # => "Writing code"
dev.test  # => "Testing code"
  • 利点:
    • クラスは必要なメソッドだけを実装すればよい。
    • 余計な依存がなくなり、コードが簡潔になる。

3. 技術や手法の"キモ"はどこにある?

  1. 役割ごとの分離

    • インターフェースを小さな役割に分けることで、必要な機能だけを実装できます。
  2. 柔軟な設計

    • クラスが複数のインターフェースを選択的に実装できるようにすることで、設計の柔軟性が向上します。
  3. 変更の影響を最小化

    • インターフェースが小さくなれば、変更による影響範囲も小さくなります。

4. 実装例

例: Storageサービス

ローカルストレージやクラウドストレージ(S3やGoogle Cloud)を選択的に使用します。それぞれのストレージが異なるインターフェースを実装しています。

# NOTE: インターフェースを定義するStorageServiceモジュール
module StorageService
  def upload(file)
    raise NotImplementedError
  end

  def download(file)
    raise NotImplementedError
  end
end

class LocalStorage
  include StorageService

  def upload(file)
    puts "Uploading #{file} to local storage"
  end

  def download(file)
    puts "Downloading #{file} from local storage"
  end
end

class S3Storage
  include StorageService

  def upload(file)
    puts "Uploading #{file} to S3"
  end

  def download(file)
    puts "Downloading #{file} from S3"
  end
end

storage = LocalStorage.new
storage.upload("file.txt") # => "Uploading file.txt to local storage"
  • 実装ポイント:
    • 各ストレージは独自のロジックを持ちながら、共通のインターフェースを実装。

5. 議論はあるか?

メリット

  • クラスが必要な機能だけを実装すればよいため、設計がシンプルになる。
  • インターフェースが小さくなることで、変更の影響範囲を最小化。
  • 再利用性が高まり、メンテナンスが容易になる。

デメリット

  • インターフェースを分割しすぎると、管理するファイルやコードが増える。
  • 小規模なプロジェクトでは過剰設計になる場合がある。

議論

インターフェース分離の原則は、特に大規模なシステムで有効ですが、小規模プロジェクトではコストが高くなる場合があります。適切な範囲で適用することが重要です。

6. まとめ

インターフェース分離の原則(ISP)は、クラスが利用しないメソッドを持つインターフェースに依存しないよう設計する原則です。

この原則を守ることで、コードの保守性と拡張性が向上し、柔軟でシンプルな設計を実現できます。

GitHubで編集を提案

Discussion