수색…


하나씩 일어나는 것

Ruby 표준 라이브러리에는 싱글 톤 패턴을 구현하는 싱글 톤 모듈이 있습니다. Singleton 클래스를 생성하는 첫 번째 단계는 클래스에 Singleton 모듈을 필요로하고 포함시키는 것입니다.

require 'singleton'

class Logger
  include Singleton
end

일반 클래스와 NoMethodError 클래스를 인스턴스화하려고하면 NoMethodError 예외가 발생합니다. 생성자는 다른 인스턴스가 실수로 생성되는 것을 방지하기 위해 비공개로 만들어집니다.

Logger.new

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

이 클래스의 인스턴스에 액세스하려면 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 모듈은 테스트되고 최적화되므로 Singleton을 구현하는 것이 더 좋은 옵션이라는 것을 명심하십시오.

관찰자

관찰자 패턴은 객체 ( subject )가 종속체 ( observers 라고 함)의 목록을 유지하고 일반적으로 해당 메소드 중 하나를 호출하여 상태 변경을 자동으로 통지하는 소프트웨어 설계 패턴입니다.

Ruby는 Observer 디자인 패턴을 구현하는 간단한 메커니즘을 제공합니다. Observable 모듈은 Observable 객체의 변경 사항을 구독자에게 알리는 로직을 제공합니다.

이를 위해서는 관찰자가 관찰자에게 변경 사실을 주장하고 관찰자에게 알리십시오.

관찰하는 객체는 Observer의 콜백이 될 update() 메소드를 구현해야합니다.

사용자가 사용자를 구독 할 수있는 작은 채팅을 구현하고 그 중 하나가 무언가를 쓸 때 구독자에게 알림을 보냅니다.

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 을 두 번 실행하여 구독자에게이 경우 하나만 알립니다.

우리가 더 많은 가입자를 추가할수록 변경 사항이 전파됩니다.

장식 패턴

데코레이터 패턴은 동일한 클래스의 다른 객체에 영향을주지 않고 객체에 비헤이비어를 추가합니다. 데코레이터 패턴은 하위 클래스를 만드는 데 유용한 대안입니다.

각 데코레이터에 대한 모듈을 만듭니다. 이 방법은 더 많은 조합으로 책임을 혼합하고 조화시킬 수 있으므로 상속보다 유연합니다. 또한 투명도를 사용하면 데코레이터를 반복적으로 중첩시킬 수 있으므로 무제한의 책임을 수행 할 수 있습니다.

피자 클래스에 300을 반환하는 비용 메서드가 있다고 가정합니다.

class Pizza
  def cost
    300
  end
end

치즈 버스트가 추가 된 피자를 PizzaWithCheese 비용은 50 씩 증가합니다. 가장 간단한 방법은 비용 방법에서 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 개의 클래스가 있습니다.

접근 방식을 단순화하기 위해 모듈을 사용하여 피자 클래스에 동작을 동적으로 추가합니다.

모듈 + 확장 + 수퍼 데코레이터 : ->

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 reservation_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_from , date_to , reservations_count )를 서비스에서 도메인과 무관 한 개념 (사용자 권한)과 혼합하지 않습니다.
  • Consumer ( StatsService )는 사용 권한 관련 논리가 없습니다.

주의 사항
  • 프록시 인터페이스는 항상 숨겨진 개체와 동일하므로 프록시로 래핑 된 서비스를 사용하는 사용자는 프록시 존재를 인식하지 못합니다.


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow