CodeKata Back to the CheckOut

Zurück zum Supermarkt. Wir implementieren den Code für ein Kassensystem, das  die Preisgestaltung übernimmt. Zu implementieren sind solche Regeln wie “Äpfel kosten 50 Cent, drei Äpfel kosten $1,30.”
In einem normalen Supermarkt werden die Dinge über SKUs identifiziert. In unserem können wir Buchstaben des Alphabets verwenden (A, B, C, und so weiter). Unsere Waren werden individuell berechnet. Darüber hinaus werden einige Elemente rabattiert: kaufen Sie n davon, und diese werden Ihnen y Cent kosten. Beispielsweise kann Artikel “A” 50 Cent einzeln kosten, aber in dieser Woche haben wir ein besonderes Angebot: kaufen drei von A und zahlen Sie $1,30. In der Tat sehen in dieser Woche die Preise wie folgt aus:

Item Unit Price Special Price
A 50 3 for 130
B 30 2 for 45
C 20
D 15

 

Unsere Kasse übernimmt Elemente in beliebiger Reihenfolge, so dass, wenn wir ein B scannen dann  ein A und ein anderes B, bekommen wir einen Gesamtpreis von 95  (wir erkennen, das wir einen Mengenrabatt auf 2xB geben müssen).  Da der Preis sich häufig ändert, müssen wir in der Lage sein den Gesamtpreis bei jeder Transaktion neu zu berechnen.

Das Interface für Checkout kann wie folgt aussehen:

   co = CheckOut.new(pricing_rules)
   co.scan(item)
   co.scan(item)
       :    :
   price = co.total

Hier ist ein Unit-Test implementiert in Ruby. Die Hilfsmethode preis bekommt eine Liste von Artikel und ruft scan-Methode der Kasse für jedes Element der Liste auf, bevor sie schließlich wieder den Gesamtpreis ausgibt.

class TestPrice < Test::Unit::TestCase

    def price(goods)
      co = CheckOut.new(RULES)
      goods.split(//).each { |item| co.scan(item) }
      co.total
    end

    def test_totals
      assert_equal(  0, price(""))
      assert_equal( 50, price("A"))
      assert_equal( 80, price("AB"))
      assert_equal(115, price("CDBA"))

      assert_equal(100, price("AA"))
      assert_equal(130, price("AAA"))
      assert_equal(180, price("AAAA"))
      assert_equal(230, price("AAAAA"))
      assert_equal(260, price("AAAAAA"))

      assert_equal(160, price("AAAB"))
      assert_equal(175, price("AAABB"))
      assert_equal(190, price("AAABBD"))
      assert_equal(190, price("DABABA"))
    end

    def test_incremental
      co = CheckOut.new(RULES)
      assert_equal(  0, co.total)
      co.scan("A");  assert_equal( 50, co.total)
      co.scan("B");  assert_equal( 80, co.total)
      co.scan("A");  assert_equal(130, co.total)
      co.scan("A");  assert_equal(160, co.total)
      co.scan("B");  assert_equal(175, co.total)
    end
  end

Es gibt viele Wege zur Umsetzung dieser Art von Algorithmus; wenn Sie Zeit haben, experimentieren Sie mit mehreren.

Ziele der Kata

Zu einem gewissen Grad ist dies nur ein lustiges kleines Problem. Der Format der Preisregeln wird in der Aufgabe nicht beschrieben. Wie können diese aussehen, damit der Checkout ohne das Wissen über die Preisregeln-Strategie immer einen korrekten Gesamtpreis berechnen kann? Wie können wir das Design flexibel genug halten, so dass wir neue Preisregeln in der Zukunft hinzufügen können?

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.