サーチ…


シングルトン

Ruby標準ライブラリには、シングルトンパターンを実装するSingletonモジュールがあります。 Singletonクラスを作成するための最初のステップは、クラスにSingletonモジュールを必要とすることです。

require 'singleton'

class Logger
  include Singleton
end

通常のクラスと同じようにこのクラスをインスタンス化しようとすると、 NoMethodError例外が発生します。他のインスタンスが誤って作成されないように、コンストラクタはプライベートにします。

Logger.new

#=> NoMethodError: private method `new' called for AppConfig:Class    

このクラスのインスタンスにアクセスするには、 instance()を使用する必要がありinstance()

first, second = Logger.instance, Logger.instance
first == second

#=> true

ロガーの例

require 'singleton'


class Logger
  include Singleton

  def initialize
    @log = File.open("log.txt", "a")
  end

  def log(msg)
    @log.puts(msg)
  end
end

Loggerオブジェクトを使用するには:

Logger.instance.log('message 2')

シングルトンを含まない場合

上記のシングルトン実装は、シングルトンモジュールを含めることなく行うこともできます。これは次のようにして実現できます。

class Logger
  def self.instance
    @instance ||= new
  end
end

これは次の表記法です。

class Logger
  def self.instance
    @instance = @instance || Logger.new
  end
end

しかし、Singletonモジュールはテストされ最適化されているため、シングルトンを実装するためのより良いオプションであることに注意してください。

観察者

オブザーバー・パターンは、オブジェクト( subjectと呼ばれる)がその従属物( observersと呼ばれる)のリストを保持し、通常はそれらのメソッドの1つを呼び出すことによって、どのような状態の変化も自動的に通知するソフトウェア設計パターンです。

RubyはObserverデザインパターンを実装するための簡単なメカニズムを提供します。 Observableモジュールは、Observableオブジェクトの変更をサブスクライバに通知するロジックを提供します。

これが機能するためには、観測者はそれが変更されたと主張し、オブザーバーに通知しなければならない。

観察するオブジェクトは、Observerのコールバックになるupdate()メソッドを実装する必要があります。

小さなチャットを実装して、ユーザーがユーザーを登録できるようにしましょう。そのユーザーの1人が何かを書くと、そのユーザーに通知が届きます。

require "observer"

class Moderator
  include Observable

  def initialize(name)
    @name = name
  end

  def write
    message = "Computer says: No"
    changed
    notify_observers(message)
  end
end

class Warner
  def initialize(moderator, limit)
    @limit = limit
    moderator.add_observer(self)
  end
end

class Subscriber < Warner
  def update(message)
    puts "#{message}"
  end
end

moderator = Moderator.new("Rupert")
Subscriber.new(moderator, 1)
moderator.write
moderator.write

次の出力を生成します。

# Computer says: No
# Computer says: No

Moderatorクラスのメソッドwriteを2回トリガして、サブスクライバ(この場合は1つ)に通知します。

追加する加入者が増えるほど、変更が広がります。

デコレータパターン

Decoratorパターンは、同じクラスの他のオブジェクトに影響を与えることなく、オブジェクトに動作を追加します。デコレータパターンは、サブクラスを作成するのに便利です。

デコレータごとにモジュールを作成します。このアプローチは継承よりも柔軟性があります。なぜなら、より多くの組み合わせで責任を混在させることができるからです。さらに、透過性により、デコレータを再帰的にネストすることができるため、無制限の数の責任が可能になります。

ピザクラスに300を返すコストメソッドがあるとします。

class Pizza
  def cost
    300
  end
end

チーズバーストが追加されたピザをPizzaWithCheese 、コストは50 PizzaWithCheeseします。最も簡単な方法は、コストメソッドで350を返すPizzaWithCheeseサブクラスを作成することです。

class PizzaWithCheese < Pizza
  def cost
    350
  end
end

次に、通常のピザのコストに100を加えた大きなピザを表現する必要があります。ピザのLargePizzaサブクラスを使用して表現できます。

class LargePizza < Pizza
  def cost
    400
  end
end

また、LargePizzaにさらに15のコストを追加するExtraLargePizzaを用意することもできます。これらのピザタイプにチーズを添えることができたら、LargePizzaWithCheseとExtraLargePizzaWithCheeseサブクラスを追加する必要があります。合計で6つのクラスがあります。

アプローチを簡素化するために、モジュールを使用してPizzaクラスにビヘイビアを動的に追加します。

モジュール+拡張+スーパーデコレータ: - >

class Pizza
  def cost
    300
  end
end

module CheesePizza
  def cost
    super + 50
  end
end

module LargePizza
  def cost
    super + 100
  end
end

pizza = Pizza.new         #=> cost = 300
pizza.extend(CheesePizza) #=> cost = 350
pizza.extend(LargePizza)  #=> cost = 450
pizza.cost                #=> cost = 450

プロキシ

プロキシオブジェクトは、他のオブジェクトへのアクセスを確実に保護するためによく使用されます。内部的なビジネスロジックは、安全上の必要性によって汚染されたくありません。

特定のアクセス許可のユーザーだけがリソースにアクセスできることを保証したいとします。

プロキシ定義:(実際には予約を見ることができるユーザーだけがconsumer_serviceにアクセスできるようになります)

class Proxy
  def initialize(current_user, reservation_service)
    @current_user = current_user
    @reservation_service = reservation_service
  end

  def highest_total_price_reservations(date_from, date_to, reservations_count)
    if @current_user.can_see_reservations?
      @reservation_service.highest_total_price_reservations(
        date_from, 
        date_to, 
        reservations_count
      )
    else
      []
    end
  end 
end

モデルと予約サービス:

class Reservation
  attr_reader :total_price, :date

  def initialize(date, total_price)
    @date = date
    @total_price = total_price
  end
end

class ReservationService
  def highest_total_price_reservations(date_from, date_to, reservations_count)
    # normally it would be read from database/external service
    reservations = [
      Reservation.new(Date.new(2014, 5, 15), 100),
      Reservation.new(Date.new(2017, 5, 15), 10),          
      Reservation.new(Date.new(2017, 1, 15), 50)
    ]

    filtered_reservations = reservations.select do |reservation|
      reservation.date.between?(date_from, date_to) 
    end

    filtered_reservations.take(reservations_count)
  end
end        

class User
  attr_reader :name

  def initialize(can_see_reservations, name)
    @can_see_reservations = can_see_reservations
    @name = name
  end

  def can_see_reservations?
    @can_see_reservations
  end
end

消費者サービス:

class StatsService
  def initialize(reservation_service)
    @reservation_service = reservation_service
  end

  def year_top_100_reservations_average_total_price(year)
    reservations = @reservation_service.highest_total_price_reservations(
      Date.new(year, 1, 1),
      Date.new(year, 12, 31),
      100
    )

    if reservations.length > 0
      sum = reservations.reduce(0) do |memo, reservation| 
        memo + reservation.total_price
      end

      sum / reservations.length
    else
      0
    end
  end
end

テスト:

def test(user, year)
  reservations_service = Proxy.new(user, ReservationService.new)
  stats_service = StatsService.new(reservations_service)
  average_price = stats_service.year_top_100_reservations_average_total_price(year)
  puts "#{user.name} will see: #{average_price}"
end

test(User.new(true, "John the Admin"), 2017)
test(User.new(false, "Guest"),         2017)

利点
  • アクセス制限が変更されたときのReservationService変更は避けています。
  • 私たちはビジネス関連のデータ( date_fromdate_toreservations_count )をドメインの無関係な概念(ユーザー権限)と組み合わせてサービスしていません。
  • Consumer( StatsService )にはパーミッション関連のロジックもありStatsService

警告
  • プロキシインターフェイスは、常に非表示のオブジェクトとまったく同じです。したがって、プロキシによってラップされたサービスを使用するユーザーは、プロキシの存在を認識していませんでした。


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow