はっさんブログ

フォローするとお得なブログ

オブジェクト指向のSOLID原則について学んでみよう【Ruby】

参考になったらシェアいただけると幸いです!

背景

コードレビューにてSOLID原則を教えていただきました。

この記事ではRubyでの事例を交えて内容を書こうと思います。

都度、SOLID原則に当てはまる事例に直面した際に、更新していきます。

Open/Closed Principle (OCP)

ソフトウェアの構成要素(クラス、モジュール、関数など)は拡張に対して開いて(オープン:Open)いて、修正に対して閉じて(クローズド:Closed)いなければならない。
オープン・クローズドの原則(OCP:The Open-Closed Principle

「拡張をしやすく、かつ修正は許さない」といった感じでしょうか。

before

例ではリソースごとにキャッシュをJSONで取得、なければ作成するコードです。
データはブロックで渡された関数が返します。

def find_or_create_cache(key, resource:)
  return yield.to_json unless enabled?(resource)

  redis = Redis::Namespace.new("api:#{resource}", redis: Redis.current)

  cached = redis.get(key)
  return JSON.parse(cached, { symbolize_names: true }) if cached

  data = yield

  data.to_json.tap { |d| redis.setex(key, ttl(resource), d) }
end

def enabled?(resource)
  case resource
  when 'category'
    Settings.api.categories.cache.enabled
  when 'partner'
    Settings.api.partners.cache.enabled
  when 'shop'
    Settings.api.shops.cache.enabled
  end
end

def ttl(resource)
  case resource
  when 'category'
    Settings.api.categories.cache.ttl
  when 'partner'
    Settings.api.partners.cache.ttl
  when 'shop'
    Settings.api.shops.cache.ttl
  end
end

「キャッシュするかしないか」と「保存時間(TTL)」をリソースごとに定数管理して呼び出しています。

しかし、これではリソースが増えるたびにコード例: when image ~を追加する必要が出てきてしまいます。

after

enablettlをキーワード引数として定義し、呼び出し先に判別を移譲します。

これにより、呼び出し元のコードを変えることなく、リソースが増えてもfind_or_create_cacheメソッドに手を加える必要はなくなりました!

def find_or_create_cache(key, resource:, enabled:, ttl:)
  return yield.to_json unless enabled

  redis = Redis::Namespace.new("api:#{resource}", redis: Redis.current)

  cached = redis.get(key)
  return JSON.parse(cached, { symbolize_names: true }) if cached

  data = yield

  data.to_json.tap { |d| redis.setex(key, ttl, d) }
end
categories = find_or_create_cache(
    cache_key,
    resource: 'category',
    enabled: Settings.api.categories.cache.enabled,
    ttl: Settings.api.categories.cache.ttl
    )
partners = find_or_create_cache(
    cache_key,
    resource: 'partner',
    enabled: Settings.api.partners.cache.enabled,
    ttl: Settings.api.partners.cache.ttl
    )

Open/Close「拡張をしやすく、かつ修正は許さない」が実現できたかと思います。

Special Thanks !

今回紹介したコードは実コードで出てきた例になります。

さらにシンプルな例がみたい場合は以下の記事を参考にしてください。

subvisual.co

開放/閉鎖原則 - Wikipedia