Suche…


Singleton

Ruby Standard Library verfügt über ein Singleton-Modul, das das Singleton-Muster implementiert. Der erste Schritt beim Erstellen einer Singleton-Klasse besteht darin, das Singleton Modul in eine Klasse aufzunehmen und aufzunehmen:

require 'singleton'

class Logger
  include Singleton
end

Wenn Sie versuchen, diese Klasse wie eine normale Klasse zu instanziieren, wird eine NoMethodError Ausnahme NoMethodError . Der Konstruktor wird als privat festgelegt, um zu verhindern, dass andere Instanzen versehentlich erstellt werden:

Logger.new

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

Um auf die Instanz dieser Klasse zuzugreifen, müssen wir die instance() :

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

#=> true

Logger-Beispiel

require 'singleton'


class Logger
  include Singleton

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

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

Um das Logger Objekt zu verwenden:

Logger.instance.log('message 2')

Ohne Singleton gehören dazu

Die obigen Singleton-Implementierungen können auch ohne das Singleton-Modul durchgeführt werden. Dies kann mit folgendem erreicht werden:

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

Dies ist eine Kurzschreibweise für Folgendes:

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

Denken Sie jedoch daran, dass das Singleton-Modul getestet und optimiert wurde. Daher ist es die bessere Option, Ihr Singleton mit zu implementieren.

Beobachter

Das Beobachtermuster ist ein Software-Entwurfsmuster, in dem ein Objekt ( subject ) eine Liste seiner abhängigen Personen (sogenannte observers ) verwaltet und diese automatisch über alle Zustandsänderungen benachrichtigt, normalerweise durch Aufrufen einer ihrer Methoden.

Ruby bietet einen einfachen Mechanismus zum Implementieren des Observer-Entwurfsmusters. Das Modul Observable bietet die Logik, um den Teilnehmer über Änderungen am Observable-Objekt zu informieren.

Damit dies funktioniert, muss das Beobachtbare behaupten, dass es sich geändert hat, und die Beobachter benachrichtigen.

Beobachtende Objekte müssen eine update() -Methode implementieren, die der Rückruf für den Observer ist.

Lassen Sie uns einen kleinen Chat implementieren, bei dem Benutzer Benutzer abonnieren können und wenn einer von ihnen etwas schreibt, werden die Abonnenten benachrichtigt.

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

Ausgabe der folgenden Ausgabe:

# Computer says: No
# Computer says: No

Wir haben die Methode write in der Moderator-Klasse zweimal ausgelöst und ihre Abonnenten benachrichtigt, in diesem Fall nur einen.

Je mehr Abonnenten wir hinzufügen, desto mehr verbreiten sich die Änderungen.

Dekorateur-Muster

Das Dekorationsmuster fügt Objekten ein Verhalten hinzu, ohne dass andere Objekte derselben Klasse betroffen sind. Das Dekorationsmuster ist eine nützliche Alternative zum Erstellen von Unterklassen.

Erstellen Sie für jeden Dekorateur ein Modul. Dieser Ansatz ist flexibler als die Vererbung, da Sie Verantwortlichkeiten in mehr Kombinationen kombinieren können. Da die Transparenz das rekursive Verschachteln von Dekorateuren zulässt, ist außerdem eine unbegrenzte Anzahl von Verantwortlichkeiten möglich.

Angenommen, die Pizzaklasse hat eine Kostenmethode, die 300 zurückgibt:

class Pizza
  def cost
    300
  end
end

Stellen Sie Pizza mit einer zusätzlichen Schicht Käse dar und die Kosten PizzaWithCheese um 50. Die einfachste Methode besteht darin, eine PizzaWithCheese Unterklasse zu erstellen, die in der PizzaWithCheese 350 PizzaWithCheese .

class PizzaWithCheese < Pizza
  def cost
    350
  end
end

Als nächstes müssen wir eine große Pizza darstellen, die die Kosten einer normalen Pizza um 100 erhöht. Wir können dies mit einer LargePizza-Unterklasse Pizza darstellen.

class LargePizza < Pizza
  def cost
    400
  end
end

Wir könnten auch eine ExtraLargePizza haben, was unsere LargePizza um weitere 15 erhöht. Wenn wir bedenken würden, dass diese Pizzasorten mit Käse serviert werden könnten, müssten wir LargePizzaWithChese und ExtraLargePizzaWithCheese-Unterklassen hinzufügen.

Um den Ansatz zu vereinfachen, verwenden Sie Module, um der Pizza-Klasse dynamisch Verhalten hinzuzufügen:

Modul + verlängern + Superdekorateur: ->

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

Proxy

Proxy-Objekte werden häufig verwendet, um den geschützten Zugriff auf ein anderes Objekt sicherzustellen. Diese interne Geschäftslogik möchten wir nicht mit Sicherheitsanforderungen belasten.

Angenommen, wir möchten garantieren, dass nur Benutzer bestimmter Berechtigungen auf die Ressource zugreifen können.

Proxy-Definition: (Es wird sichergestellt, dass nur Benutzer, die tatsächlich Reservierungen sehen können, denesen reservation_service verwenden können.)

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

Modelle und Reservierungsservice:

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

Kundenservice:

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

Prüfung:

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)

LEISTUNGEN
  • wir vermeiden Änderungen in ReservationService wenn Zugriffsbeschränkungen geändert werden
  • Wir mischen keine geschäftsbezogenen Daten ( date_from , date_to , reservations_count ) mit date_to Konzepten (Benutzerberechtigungen) im Service.
  • Consumer ( StatsService ) ist ebenfalls frei von Logik für Berechtigungen

CAVEATS
  • Die Proxy-Schnittstelle entspricht immer genau dem Objekt, das sie verbirgt, so dass der Benutzer, der den von Proxy umschlossenen Dienst in Anspruch nimmt, nicht einmal die Anwesenheit eines Proxy erkannt hat.


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow