Wzorzec projektowy – Strategia

Wzorzec projektowy Strategii (ang. Strategy) pozwala na swobodne zmienianie algorytmów używanych przez aplikację, bez konieczności modyfikowania samego kodu. W tym przypadku, przedstawię Ci przykład kodu, w którym stacja benzynowa ma różne sposoby naliczania ceny paliw, a następnie zastosuję wzorzec Strategii do refaktoryzacji tego kodu.

Przykładowy kod przed refaktoryzacją:

class GasStation
  attr_accessor :gas_price

  def initialize(gas_price)
    @gas_price = gas_price
  end

  def calculate_bill(litres)
    litres * @gas_price
  end
end

station = GasStation.new(3.50)
puts station.calculate_bill(10) # 35.0

Ten kod tworzy klasę GasStation, która ma jedną metodę calculate_bill, która mnoży cenę paliwa przez ilość litrów, aby obliczyć rachunek. Warto zastosować wzorzec Strategii, ponieważ cena paliw może się zmieniać w zależności od różnych czynników, takich jak cena surowca, opłaty rządowe itp. i chcemy mieć możliwość łatwego dodawania nowych sposobów liczenia ceny paliwa bez konieczności modyfikowania kodu GasStation.

Refaktoring

Oto przykład kodu po refaktoryzacji z wykorzystaniem wzorca Strategii:

class GasStation
  attr_accessor :gas_price, :billing_strategy

  def initialize(gas_price, billing_strategy)
    @gas_price = gas_price
    @billing_strategy = billing_strategy
  end

  def calculate_bill(litres)
    @billing_strategy.calculate_bill(litres, @gas_price)
  end
end

class BillingStrategy
  def calculate_bill(litres, gas_price)
    raise 'You must implement calculate_bill in a subclass'
  end
end

class SimpleBilling < BillingStrategy
  def calculate_bill(litres, gas_price)
    litres * gas_price
  end
end

class RewardBilling < BillingStrategy
  def calculate_bill(litres, gas_price)
    litres * gas_price * 0.9
  end
end

station = GasStation.new(3.50, SimpleBilling.new)
puts station.calculate_bill(10) # 35.0

# to switch to a different billing strategy:
station.billing_strategy = RewardBilling.new
puts station.calculate_bill(10) # 31.5

Jak widać, klasa GasStation teraz przechowuje referencję do klasy BillingStrategy (w polu billing_strategy), a nie ma już własnej logiki dotyczącej liczenia rachunku. Zamiast tego, deleguje to zadanie do klasy BillingStrategy przez wywołanie metody calculate_bill.

Klasa BillingStrategy jest abstrakcyjną klasą bazową, która zawiera logikę wymaganą dla implementacji klas pochodnych. W klasach pochodnych takich jak SimpleBilling i RewardBilling implementowane są różne strategie liczenia rachunku.

Poprzez tą implementację, kod jest bardziej elastyczny, ponieważ bez konieczności zmieniania kodu klasy GasStation, możemy łatwo dodać nowe strategie liczenia rachunku, jedynie przez dodanie klasy pochodnej od klasy BillingStrategy.

Testy

Na koniec zobaczmy, jak wyglądałyby testy do powyższego kodu:

require 'minitest/autorun'

class TestGasStation < Minitest::Test
  def setup
    @station = GasStation.new(3.50, SimpleBilling.new)
  end

  def test_simple_billing
    assert_equal 35.0, @station.calculate_bill(10)
  end

  def test_reward_billing
    @station.billing_strategy = RewardBilling.new
    assert_equal 31.5, @station.calculate_bill(10)
  end
end

Leave a Reply

Your email address will not be published. Required fields are marked *