Zoeken…


eenling

Ruby Standard Library heeft een Singleton-module die het Singleton-patroon implementeert. De eerste stap bij het maken van een Singleton-klasse is het vereisen en opnemen van de Singleton module in een klasse:

require 'singleton'

class Logger
  include Singleton
end

Als u deze klasse probeert te instantiëren zoals u normaal een normale klasse zou doen, wordt een uitzondering NoMethodError opgeworpen. De constructor wordt privé gemaakt om te voorkomen dat andere instanties per ongeluk worden gemaakt:

Logger.new

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

Om toegang te krijgen tot de instantie van deze klasse, moeten we de instance() :

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

#=> true

Logger-voorbeeld

require 'singleton'


class Logger
  include Singleton

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

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

Om het Logger object te gebruiken:

Logger.instance.log('message 2')

Zonder Singleton bevatten

De bovenstaande singleton-implementaties kunnen ook worden gedaan zonder de Singleton-module op te nemen. Dit kan worden bereikt met het volgende:

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

wat een korte notatie is voor het volgende:

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

Houd er echter rekening mee dat de Singleton-module is getest en geoptimaliseerd, en daarom de betere optie is om uw singleton mee te implementeren.

Waarnemer

Het waarnemerspatroon is een patroon van softwareontwerp waarin een object ( subject ) een lijst bijhoudt van zijn afhankelijken ( observers ) en hen automatisch op de hoogte brengt van eventuele statuswijzigingen, meestal door een van hun methoden aan te roepen.

Ruby biedt een eenvoudig mechanisme om het ontwerppatroon van de waarnemer te implementeren. De module Observable biedt de logica om de abonnee op de hoogte te stellen van eventuele wijzigingen in het Observable-object.

Om dit te laten werken, moet de waarnemer beweren dat het is veranderd en de waarnemers op de hoogte brengen.

Observerende objecten moeten een update() -methode implementeren, die voor de waarnemer wordt teruggebeld.

Laten we een kleine chat implementeren, waar gebruikers zich op gebruikers kunnen abonneren en wanneer een van hen iets schrijft, krijgen de abonnees een melding.

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

Het produceren van de volgende output:

# Computer says: No
# Computer says: No

We hebben de methode write in de Moderator-klasse twee keer geactiveerd en de abonnees hiervan op de hoogte gebracht, in dit geval slechts één.

Hoe meer abonnees we toevoegen, hoe meer de wijzigingen zich zullen verspreiden.

Decorateur Patroon

Decorateurpatroon voegt gedrag toe aan objecten zonder andere objecten van dezelfde klasse te beïnvloeden. Het decorateurpatroon is een nuttig alternatief voor het maken van subklassen.

Maak een module voor elke decorateur. Deze aanpak is flexibeler dan overerving omdat u verantwoordelijkheden in meer combinaties kunt combineren en matchen. Omdat decorators door de transparantie recursief kunnen worden genest, biedt het bovendien een onbeperkt aantal verantwoordelijkheden.

Stel dat de Pizza-klasse een kostenmethode heeft die 300 oplevert:

class Pizza
  def cost
    300
  end
end

Vertegenwoordig pizza met een toegevoegde laag barsten van kaas en de kosten stijgen met 50. De eenvoudigste aanpak is om een PizzaWithCheese subklasse te maken die in de PizzaWithCheese 350 PizzaWithCheese .

class PizzaWithCheese < Pizza
  def cost
    350
  end
end

Vervolgens moeten we een grote pizza vertegenwoordigen die 100 toevoegt aan de kosten van een normale pizza. We kunnen dit vertegenwoordigen met behulp van een LargePizza-subklasse van Pizza.

class LargePizza < Pizza
  def cost
    400
  end
end

We kunnen ook een ExtraLargePizza hebben die nog eens 15 extra kost voor onze LargePizza. Als we zouden overwegen dat deze pizzasoorten met kaas kunnen worden geserveerd, moeten we de subklassen LargePizzaWithChese en ExtraLargePizzaWithCheese toevoegen. We eindigen met in totaal 6 klassen.

Om de aanpak te vereenvoudigen, gebruikt u modules om dynamisch gedrag toe te voegen aan de Pizza-klasse:

Module + uitbreiden + super decorateur: ->

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

volmacht

Proxy-object wordt vaak gebruikt om beveiligde toegang tot een ander object te garanderen, welke interne bedrijfslogica we niet willen vervuilen met veiligheidseisen.

Stel dat we willen garanderen dat alleen gebruikers met specifieke machtigingen toegang hebben tot bronnen.

Proxy-definitie: (het zorgt ervoor dat alleen gebruikers die daadwerkelijk reserveringen kunnen zien, consumer_service kunnen gebruiken)

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

Modellen en reserveringsservice:

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

Klantenservice:

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

Test:

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)

VOORDELEN
  • we vermijden wijzigingen in ReservationService wanneer toegangsbeperkingen worden gewijzigd.
  • we date_from geen bedrijfsgerelateerde gegevens ( date_from , date_to , reservations_count ) met domeingerelateerde concepten (gebruikersrechten) in de service.
  • Consumer ( StatsService ) is ook vrij van logica met betrekking tot machtigingen

WAARSCHUWINGEN
  • De proxy-interface is altijd exact hetzelfde als het object dat het verbergt, zodat de gebruiker die service verpakt door proxy gebruikt niet eens op de hoogte was van de aanwezigheid van proxy.


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow