Rubyで理解する、デザインパターン入門の6ステップ

6ステップで学ぶRubyのデザインパターンRuby
この記事は約12分で読めます。

※本記事のコンテンツは、利用目的を問わずご活用いただけます。実務経験10000時間以上のエンジニアが監修しており、基礎知識があれば初心者にも理解していただけるように、常に解説内容のわかりやすさや記事の品質に注力しております。不具合・分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。(理解できない部分などの個別相談も無償で承っております)
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)


はじめに

この記事を読めば、プログラミング言語Rubyを使って、デザインパターンの基礎を理解し、活用することができるようになります。

デザインパターンとは、一般的な設計問題を解決するための再利用可能なソリューションです。

この記事では、具体的なRubyのコード例とともに、デザインパターンを詳しく解説します。

なお、この記事は初心者向けに分かりやすく説明していますので、Rubyやデザインパターンに詳しくない方でも理解しやすい内容となっています。

●Rubyとは

Rubyは、ユーザーフレンドリーで直感的なプログラミング言語です。

Rubyは、すっきりとしたシンタックスと豊富なライブラリにより、初心者にも扱いやすい言語とされています。

○Rubyの特徴

Rubyは、ダックタイピングやメタプログラミングといった特性を持ち、これらの特性を活用することで高度なプログラミングを可能にします。

また、Rubyはオブジェクト指向プログラミング言語であるため、再利用性の高いコードの記述が可能で、これがデザインパターンの理解に大いに役立ちます。

●デザインパターンとは

デザインパターンは、再利用可能なソリューションで、特定の問題に対する一般的な設計解決策です。

これらは、複雑なシステム設計を容易にするために、過去のプログラマーの経験に基づいています。

○デザインパターンの種類

デザインパターンは大きく分けて3つのカテゴリーに分類されます。

それは、生成パターン(Creational Patterns)、構造パターン(Structural Patterns)、そして行動パターン(Behavioral Patterns)です。

これら各カテゴリーのパターンは、特定の種類の設計問題を解決するのに適しています。

●Rubyでのデザインパターンの使い方

ここからは具体的なコード例を使って、Rubyでのデザインパターンの使い方を説明します。

○サンプルコード1:シングルトンパターン

シングルトンパターンは、特定のクラスのインスタンスが1つしか存在しないことを保証するためのパターンです。

Rubyでシングルトンパターンを実装した例を紹介します。

require 'singleton'

class SingletonObject
  include Singleton
  attr_accessor :counter

  def initialize
    @counter = 0
  end
end

object1 = SingletonObject.instance
object1.counter += 1

object2 = SingletonObject.instance
object2.counter += 1

puts object1.counter
puts object2.counter

このコードでは、Singletonモジュールを使ってシングルトンパターンを実装しています。

この例では、counterという属性を持つSingletonObjectクラスのインスタンスが1つしか存在しないことを保証しています。

object1とobject2は同じSingletonObjectのインスタンスを参照しています。

ですから、object1.counterとobject2.counterの値は同じになります。

このコードを実行すると、次のような出力が得られます。

2
2

○サンプルコード2:ファクトリーパターン

次に紹介するのはファクトリーパターンです。

ファクトリーパターンは、オブジェクトの生成処理を専用のメソッド(ファクトリーメソッド)に分離することで、オブジェクトの生成処理を柔軟に変更できるようにするためのパターンです。

class ProductFactory
  def self.create_product(type)
    if type == :A
      ProductA.new
    elsif type == :B
      ProductB.new
    else
      raise "Invalid product type: #{type}"
    end
  end
end

class ProductA
  def perform
    puts 'I am product A'
  end
end

class ProductB
  def perform
    puts 'I am product B'
  end
end

product = ProductFactory.create_product(:A)
product.perform

このコードでは、ProductFactoryクラスのクラスメソッドcreate_productを通じて、特定のタイプのProductオブジェクトを生成しています。

ProductAとProductBのインスタンス生成を、ProductFactoryクラスが一手に引き受けることで、Productオブジェクトの生成方法を統一し、柔軟性を持たせています。

このコードを実行すると、次のような出力が得られます。

I am product A

○サンプルコード3:ストラテジーパターン

ストラテジーパターンは、アルゴリズム(戦略)をオブジェクトで表現し、それを使うコンテキストから独立させて、アルゴリズムの交換を容易にするためのパターンです。

Rubyでストラテジーパターンを実装した例を紹介します。

class Context
  attr_accessor :strategy

  def initialize(strategy)
    @strategy = strategy
  end

  def execute_strategy
    @strategy.execute
  end
end

class StrategyA
  def execute
    puts 'Strategy A'
  end
end

class StrategyB
  def execute
    puts 'Strategy B'
  end
end

context = Context.new(StrategyA.new)
context.execute_strategy

このコードでは、Contextクラスが特定の戦略を持ち、その戦略の実行を委譲します。

StrategyAとStrategyBはそれぞれ異なるアルゴリズムを提供し、Contextはこれらのアルゴリズムを使い分けることができます。

このコードを実行すると、次のような出力が得られます。

Strategy A

○サンプルコード4:オブザーバーパターン

オブザーバーパターンは、あるオブジェクト(被观察者)の状態が変化したときに、そのオブジェクトに依存している他のオブジェクト(观察者)にその変化を自動的に通知するためのパターンです。

class Observable
  def initialize
    @observers = []
  end

  def add_observer(observer)
    @observers << observer
  end

  def notify_observers
    @observers.each do |observer|
      observer.update(self)
    end
  end
end

class Observer
  def update(observable)
    puts "#{self.class} observed a change in #{observable.class}"
  end
end

observable = Observable.new
observer = Observer.new

observable.add_observer(observer)
observable.notify_observers

このコードでは、ObservableクラスがObserverクラスのインスタンスを登録し、状態が変わると登録した全てのObserverに通知します。

ObserverはObservableから通知を受け取ると、自身のupdateメソッドを実行します。

このコードを実行すると、次のような出力が得られます。

Observer observed a change in Observable

○サンプルコード5:デコレーターパターン

次に取り上げるのはデコレーターパターンです。

デコレーターパターンは、オブジェクトの振る舞いを動的に追加・変更するためのパターンです。

class Component
  def operation
    'Component'
  end
end

class Decorator
  def initialize(component)
    @component = component
  end

  def operation
    @component.operation
  end
end

class ConcreteDecorator < Decorator
  def operation
    "ConcreteDecorator(#{super})"
  end
end

component = Component.new
decorator = ConcreteDecorator.new(component)
puts decorator.operation

このコードでは、Componentクラスのoperationメソッドの振る舞いを、ConcreteDecoratorクラスを用いて動的に変更しています。

ConcreteDecoratorはDecoratorを継承し、Decoratorは具体的なComponentをラップします。

ConcreteDecoratorはラップされたComponentの振る舞いを変更することで、新たな振る舞いを提供します。

このコードを実行すると、次のような出力が得られます。

ConcreteDecorator(Component)

○サンプルコード6:アダプターパターン

アダプターパターンは、既存のクラスのインターフェイスを使用者の要求に合わせて変更するためのパターンです。

既存のクラスを直接修正するのではなく、中間的な役割を果たすクラスを新たに作成して実装をラップし、使用者から見て都合の良いインターフェイスを提供します。

class Target
  def request
    'Target: The default target\'s behavior.'
  end
end

class Adaptee
  def specific_request
    '.eetpadA eht fo roivaheb laicepS'
  end
end

class Adapter
  def initialize(adaptee)
    @adaptee = adaptee
  end

  def request
    @adaptee.specific_request.reverse
  end
end

adaptee = Adaptee.new
puts 'Adaptee: ' + adaptee.specific_request
adapter = Adapter.new(adaptee)
puts 'Adapter: ' + adapter.request

このコードでは、Adapteeクラスが提供するspecific_requestメソッドが使用者にとって都合の悪いインターフェイスを持っていると考え、Adapterクラスを作成しました。

AdapterクラスはAdapteeクラスのインスタンスをラップし、使用者から見て都合の良いrequestメソッドを提供します。

このコードを実行すると、次のような出力が得られます。

Adaptee: .eetpadA eht fo roivaheb laicepS
Adapter: Special behavior of the Adaptee.

●注意点と対処法

デザインパターンはあくまで一つの解決策であり、全ての問題に対する銀の弾丸ではありません。適切な場所で適切に使用することが重要です。

また、過度にデザインパターンを使用すると、逆にコードの複雑性が増すこともあります。

初心者がデザインパターンを学ぶ際には、まず各パターンが解決を試みる問題とその解決策を理解することから始め、それから自分のコードにどのように適用するかを考えるのが良いでしょう。

●デザインパターンの応用例

デザインパターンは設計段階での抽象的な概念ですが、具体的なソフトウェア開発の現場では様々な形で応用されています。

例えば、Ruby on RailsのMVC(Model-View-Controller)パターンは、データの操作(Model)、ユーザーインターフェース(View)、それらを制御するロジック(Controller)を分離するという設計思想を表現しています。

このように、デザインパターンを理解していれば、既存のソフトウェアアーキテクチャの理解や、新たなソフトウェアの設計に役立てることができます。

○サンプルコード7:Webアプリケーションにおけるデザインパターンの適用例

Webアプリケーションの開発においてもデザインパターンは頻繁に用いられます。

Ruby on Railsなどのフレームワークを用いることで、一部のデザインパターンは自然と適用されますが、それだけではなく、自分で意識的にデザインパターンを適用することで、より良い設計を行うことが可能です。

class ApplicationController < ActionController::Base
  before_action :authenticate_user

  private

  def authenticate_user
    redirect_to new_session_path unless current_user
  end
end

上記のコードはRuby on Railsにおけるコントローラの一例で、一種のフィルタリングを行っています。

これは「インターセプターパターン」と呼ばれ、特定の処理を行う前に何らかのチェックや準備を行うためのパターンです。

このコードではauthenticate_userというメソッドが、アクションの実行前に必ず呼び出されることで、未ログインのユーザーが認証が必要なページにアクセスした場合にログイン画面にリダイレクトするという挙動を実現しています。

このようにデザインパターンを理解していれば、既存のフレームワークの機能をより深く理解し、効果的に活用することができます。

まとめ

本記事では、Ruby言語を使用してデザインパターンの基礎を学ぶための6つのステップを紹介しました。

具体的なコード例と共に、アダプターパターンやインターセプターパターンなど、各デザインパターンの概念とその応用例について解説しました。

初心者でも分かりやすく、Rubyによるデザインパターンの理解を深める一助になれば幸いです。

これからもRubyや他のプログラミング言語でデザインパターンを学んでいくことで、ソフトウェア設計の幅を広げ、より良いソフトウェアを開発する能力を身につけていきましょう。