Schlagwort-Archive: code retreat

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?

CodeKata Bowling-Game

CodeKata Bowling-Game

bowling-kata

Das Spiel besteht aus 10 Frames. In jedem Frame hat der Spieler zwei Versuche (Rolls) und kann bis zu 10 Pins umzuwerfen. Die Punktzahl pro Frame setzt sich aus der Anzahl der umgeworfenen Pins und weiteren Boni zusammen. Diese Boni ergeben sich aus Spares und Strikes.

Ein Spare liegt vor, wenn der Spieler alle 10 Pins in einem Frame umgeworfen hat. Der Bonus für dieses Frame ist die Anzahl der Pins, die im direkt darauf folgenden Roll umgeworfen werden. Ein Beispiel ist Frame 3 im obigen Bild. Der Score ist 10 (Anzahl der umgeworfenen Pins) plus einem Bonus von 5 (Die Anzahl der Pins im ersten Roll der 4. Frame).

Ein Strike liegt vor, wenn der Spieler alle 10 Pins im ersten Versuch umwirft. Der Bonus hierfür ist die Anzahl der umgeworfenen Pins der nächsten zwei Rolls. Im zehnten Frame darf ein Spieler, der einen Spare oder einen Strike erreicht einen zusätzlichen Roll spielen, um die Frame zu beenden. Es können aber nicht mehr als 3 Rolls im letzten Frame gespielt werden.