Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Go Northwest 2018: A Rubyist's (Poignant) Guide to Go

Go Northwest 2018: A Rubyist's (Poignant) Guide to Go

3628a16b0829f76f84f94eb83970762c?s=128

Hsing-Hui Hsu

July 30, 2018
Tweet

More Decks by Hsing-Hui Hsu

Other Decks in Programming

Transcript

  1. A RUBYIST’S (POIGNANT) GUIDE TO GO HSING-HUI HSU @SOMANYHS

  2. GO NORTHWEST 2018

  3. GO NORTHWEST 2018 RUBY! GO NORTHWEST 2018

  4. GO NORTHWEST 2018 RUBY!

  5. GO NORTHWEST 2018 GO!

  6. "

  7. GO NORTHWEST 2018 THINGS I LEARNED FROM RUBY ▸Objects are

    your friend ▸Duck typing ▸Test-Driven Development ✅ ▸Developer happiness
  8. GO NORTHWEST 2018 ▸Static types?! ▸No objects?! ▸No meta programming?!

    ▸…No developer happiness?? FROM RUBY TO GO
  9. OBJECT-ORIENTED DESIGN

  10. WHAT ARE OBJECTS?

  11. OBJECTS 
 ENCAPSULATION OF DATA + BEHAVIOR USED TO INTERACT

    WITH THAT DATA BUT THAT’S NOT ALL!
  12. ‣ Conceptual level: 
 A set of responsibilities ‣ Specification

    level: 
 A set of methods or behaviors that can be invoked by other objects or by itself ‣ Implementation level: 
 A set of code and data and computation interactions between them
  13. HOW TO THINK ABOUT OBJECTS IN GO?

  14. GILDED ROSE KATA

  15. GO NORTHWEST 2018 GILDED ROSE REFACTORING KATA: THE REQUIREMENTS ▸

    All items have a Sell-In value which denotes the number of days in which the item must be sold ▸ All Items have a Quality value which denotes how valuable the item is ▸ At the end of each day our system lowers both values for every item
  16. GO NORTHWEST 2018 GILDED ROSE REFACTORING KATA: THE REQUIREMENTS ▸

    Once the sell-in date has passed, Quality degrades twice as fast ▸ The Quality of an item is never negative ▸ Some items actually increase in Quality the older it gets, e.g. “Aged Brie” ▸ The Quality of an item is never more than 50 ▸ "Sulfuras", being a legendary item, never has to be sold or decreases in Quality ▸ “Backstage passes", like Aged Brie, increases in Quality as it's SellIn value approaches; Quality increases by 2 when there are 10 days or less and by 3 when there are 5 days or less but Quality drops to 0 after the concerts have a Quality value which denotes how valuable the item is
  17. GO NORTHWEST 2018 ▸ We have recently signed a supplier

    of conjured items. This requires an update to our system: ▸ “Conjured” items degrade in Quality twice as fast as normal items GILDED ROSE REFACTORING KATA: THE REQUIREMENTS
  18. THE LEGACY CODE

  19. GO NORTHWEST 2018 package main type Item struct { name

    string sellIn, quality int } var items = []Item{ Item{"+5 Dexterity Vest", 10, 20}, Item{"Aged Brie", 2, 0}, Item{“Chunky Bacon", 5, 7}, Item{"Sulfuras, Hand of Ragnaros", 0, 80}, Item{"Backstage passes to a TAFKAL80ETC concert", 15, 20}, Item{"Conjured Mana Cake", 3, 6}, } func main() { GildedRose() } ...
  20. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { if items[i].name != "Aged Brie" && items[i].name != "Backstage passes to a TAFKAL80ETC concert" { if items[i].quality > 0 { if items[i].name != "Sulfuras, Hand of Ragnaros" { items[i].quality = items[i].quality - 1 } } } else { if items[i].quality < 50 { items[i].quality = items[i].quality + 1 if items[i].name == "Backstage passes to a TAFKAL80ETC concert" { if items[i].sellIn < 11 { if items[i].quality < 50 { items[i].quality = items[i].quality + 1 } } if items[i].sellIn < 6 { if items[i].quality < 50 { items[i].quality = items[i].quality + 1 } } } } } // ...
  21. GO NORTHWEST 2018 // ...continued if items[i].name != "Sulfuras, Hand

    of Ragnaros" { items[i].sellIn = items[i].sellIn - 1 } if items[i].sellIn < 0 { if items[i].name != "Aged Brie" { if items[i].name != "Backstage passes to a TAFKAL80ETC concert" { if items[i].quality > 0 { if items[i].name != "Sulfuras, Hand of Ragnaros" { items[i].quality = items[i].quality - 1 } } } else { items[i].quality = items[i].quality - items[i].quality } } else { if items[i].quality < 50 { items[i].quality = items[i].quality + 1 } } } } }
  22. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { if items[i].name != "Aged Brie" && items[i].name != "Backstage passes to a TAFKAL80ETC concert" { if items[i].quality > 0 { if items[i].name != "Sulfuras, Hand of Ragnaros" { items[i].quality = items[i].quality - 1 } } } else { if items[i].quality < 50 { items[i].quality = items[i].quality + 1 if items[i].name == "Backstage passes to a TAFKAL80ETC concert" { if items[i].sellIn < 11 { if items[i].quality < 50 { items[i].quality = items[i].quality + 1 } } if items[i].sellIn < 6 { if items[i].quality < 50 { items[i].quality = items[i].quality + 1 } } } } } if items[i].name != "Sulfuras, Hand of Ragnaros" { items[i].sellIn = items[i].sellIn - 1 } if items[i].sellIn < 0 { if items[i].name != "Aged Brie" { if items[i].name != "Backstage passes to a TAFKAL80ETC concert" { if items[i].quality > 0 { if items[i].name != "Sulfuras, Hand of Ragnaros" { items[i].quality = items[i].quality - 1 } } } else { items[i].quality = items[i].quality - items[i].quality } } else { if items[i].quality < 50 { items[i].quality = items[i].quality + 1 } } } } }
  23. HOW DID WE GET IN THIS MESS?

  24. “IF YOU KNOW THE ENEMY AND KNOW YOURSELF, YOU NEED

    NOT FEAR THE RESULT OF A HUNDRED BATTLES.” -Sun Tzu, The Art of War
  25. I WANTED TO MAKE MY OWN MESS

  26. GO NORTHWEST 2018 ▸ All items have a SellIn value

    which denotes the number of days in which the item must be sold ▸ All Items have a Quality value which denotes how valuable the item is ▸ At the end of each day our system lowers both values for every item GILDED ROSE REFACTORING KATA: THE REQUIREMENTS
  27. GO NORTHWEST 2018 func TestNormal_BeforeSellDate(t *testing.T) { normal := Item{"Normal",

    2, 10} items = []Item{normal} GildedRose() got := items[0] expectedSellIn := normal.sellIn - 1 if got.sellIn != expectedSellIn { t.Fatalf("Sell In = %d, want %d", got.sellIn, expectedSellIn) }
 expectedQuality := normal.quality - 1 if got.quality != expectedQuality { t.Fatalf("Quality = %d, want %d", got.quality, expectedQuality) } } TDD! "
  28. GO NORTHWEST 2018 === RUN TestNormal_BeforeSellDate --- PASS: TestNormal_BeforeSellDate (0.00s)

    === RUN TestNormal_BeforeSellDate_WithMinQuality --- PASS: TestNormal_BeforeSellDate_WithMinQuality (0.00s) === RUN TestNormal_OnSellDate --- PASS: TestNormal_OnSellDate (0.00s) === RUN TestNormal_OnSellDate_WithMinQuality --- PASS: TestNormal_OnSellDate_WithMinQuality (0.00s) === RUN TestNormal_AfterSellDate --- PASS: TestNormal_AfterSellDate (0.00s) === RUN TestNormal_AfterSellDate_WithMinQuality --- PASS: TestNormal_AfterSellDate_WithMinQuality (0.00s) === RUN TestBrie_BeforeSellDate --- PASS: TestBrie_BeforeSellDate (0.00s) === RUN TestBrie_BeforeSellDate_WithMaxQuality --- PASS: TestBrie_BeforeSellDate_WithMaxQuality (0.00s) === RUN TestBrieOnSellDate --- PASS: TestBrieOnSellDate (0.00s) === RUN TestBrieOnSellDateWithMaxQuality --- PASS: TestBrieOnSellDateWithMaxQuality (0.00s) === RUN TestBrie_AfterSellDate --- PASS: TestBrie_AfterSellDate (0.00s) === RUN TestBrieAfterSellDate_WithMaxQuality --- PASS: TestBrieAfterSellDate_WithMaxQuality (0.00s) === RUN TestSulfuras_BeforeSellDate --- PASS: TestSulfuras_BeforeSellDate (0.00s) === RUN TestSulfuras_OnSellDate --- PASS: TestSulfuras_OnSellDate (0.00s) === RUN TestSulfuras_AfterSellDate --- PASS: TestSulfuras_AfterSellDate (0.00s) === RUN TestBackstagePass_LongBeforeSellDate --- PASS: TestBackstagePass_LongBeforeSellDate (0.00s) === RUN TestBackstagePass_LongBeforeSellDate_WithMaxQuality --- PASS: TestBackstagePass_LongBeforeSellDate_WithMaxQuality (0.00s) === RUN TestBackstagePass_MediumBeforeSellDate --- PASS: TestBackstagePass_MediumBeforeSellDate (0.00s) === RUN TestBackstagePass_MediumBeforeSellDate_WithMaxQuality --- PASS: TestBackstagePass_MediumBeforeSellDate_WithMaxQuality (0.00s) === RUN TestBackstagePass_JustBeforeSellDate --- PASS: TestBackstagePass_JustBeforeSellDate (0.00s) === RUN TestBackstagePass_JustBeforeSellDate_WithMaxQuality --- PASS: TestBackstagePass_JustBeforeSellDate_WithMaxQuality (0.00s) === RUN TestBackstagePassOnSellDate --- PASS: TestBackstagePassOnSellDate (0.00s) === RUN TestBackstagePass_AfterSellDate --- PASS: TestBackstagePass_AfterSellDate (0.00s) === RUN TestConjuredMana_BeforeSellDate --- PASS: TestConjuredMana_BeforeSellDate (0.00s) === RUN TestConjuredMana_BeforeSellDate_WithMinQuality --- PASS: TestConjuredMana_BeforeSellDate_WithMinQuality (0.00s) === RUN TestConjuredMana_OnSellDate --- PASS: TestConjuredMana_OnSellDate (0.00s) === RUN TestConjuredMana_OnSellDate_WithMinQuality --- PASS: TestConjuredMana_OnSellDate_WithMinQuality (0.00s) === RUN TestConjuredMana_AfterSellDate --- PASS: TestConjuredMana_AfterSellDate (0.00s) === RUN TestConjuredMana_AfterSellDate_WithMinQuality --- PASS: TestConjuredMana_AfterSellDate_WithMinQuality (0.00s) PASS ok gilded_rose 0.006s
  29. GO NORTHWEST 2018 // At the end of each day

    our system lowers both values for every item func GildedRose() { for i := 0; i < len(items); i++ { } items[i].SellIn -= 1 items[i].Quality -= 1
  30. GO NORTHWEST 2018 GILDED ROSE REFACTORING KATA: THE REQUIREMENTS ▸

    Once the sell by date has passed, Quality degrades twice as fast ▸ The Quality of an item is never negative ▸ Some items actually increase in Quality the older it gets, e.g. “Aged Brie” ▸ The Quality of an item is never more than 50 ▸ "Sulfuras", being a legendary item, never has to be sold or decreases in Quality ▸ “Backstage passes", like Aged Brie, increases in Quality as it's SellIn value approaches; Quality increases by 2 when there are 10 days or less and by 3 when there are 5 days or less but Quality drops to 0 after the concerts have a Quality value which denotes how valuable the item is
  31. GO NORTHWEST 2018 // Once the sell by date has

    passed, Quality degrades twice as fast func GildedRose() { for i := 0; i < len(items); i++ { items[i].sellIn -= 1 items[i].quality -= 1 } }
  32. GO NORTHWEST 2018 // Once the sell by date has

    passed, Quality degrades // twice as fast func GildedRose() { for i := 0; i < len(items); i++ { items[i].sellIn -= 1 if items[i].sellIn > 0 { items[i].quality -= 1 } if items[i].sellIn <= 0 { items[i].quality -= 2 } } }
  33. GO NORTHWEST 2018 GILDED ROSE REFACTORING KATA: THE REQUIREMENTS ▸

    Once the sell by date has passed, Quality degrades twice as fast ▸ The Quality of an item is never negative ▸ Some items actually increase in Quality the older it gets, e.g. “Aged Brie” ▸ The Quality of an item is never more than 50 ▸ "Sulfuras", being a legendary item, never has to be sold or decreases in Quality ▸ “Backstage passes", like Aged Brie, increases in Quality as it's SellIn value approaches; Quality increases by 2 when there are 10 days or less and by 3 when there are 5 days or less but Quality drops to 0 after the concerts have a Quality value which denotes how valuable the item is
  34. GO NORTHWEST 2018 // The Quality of an item is

    never negative func GildedRose() { for i := 0; i < len(items); i++ { items[i].sellIn -= 1 if items[i].sellIn > 0 { items[i].quality -= 1 } if items[i].sellIn <= 0 { items[i].quality -= 2 } } }
  35. GO NORTHWEST 2018 // The Quality of an item is

    never negative func GildedRose() { for i := 0; i < len(items); i++ { items[i].sellIn -= 1 
 if items[i].sellIn > 0 { items[i].quality -= 1 } if items[i].sellIn <= 0 { items[i].quality -= 2 } } }
  36. GO NORTHWEST 2018 // The Quality of an item is

    never negative func GildedRose() { for i := 0; i < len(items); i++ { items[i].sellIn -= 1 if items[i].quality > 0 {
 if items[i].sellIn > 0 { items[i].quality -= 1 } if items[i].sellIn <= 0 { items[i].quality -= 2 } } } }
  37. GO NORTHWEST 2018 ~/go/src/gilded_rose$ go test -v === RUN TestNormal_BeforeSellDate

    --- PASS: TestNormal_BeforeSellDate (0.00s) === RUN TestNormal_BeforeSellDate_WithMinQuality --- PASS: TestNormal_BeforeSellDate_WithMinQuality (0.00s) === RUN TestNormal_OnSellDate --- PASS: TestNormal_OnSellDate (0.00s) === RUN TestNormal_OnSellDate_WithMinQuality --- PASS: TestNormal_OnSellDate_WithMinQuality (0.00s) === RUN TestNormal_AfterSellDate --- PASS: TestNormal_AfterSellDate (0.00s) === RUN TestNormal_AfterSellDate_WithMinQuality --- PASS: TestNormal_AfterSellDate_WithMinQuality (0.00s) PASS ok gilded_rose 0.005s "
  38. GO NORTHWEST 2018 GILDED ROSE REFACTORING KATA: THE REQUIREMENTS ▸

    Once the sell by date has passed, Quality degrades twice as fast ▸ The Quality of an item is never negative ▸ Some items actually increase in Quality the older it gets, e.g. “Aged Brie” ▸ The Quality of an item is never more than 50 ▸ "Sulfuras", being a legendary item, never has to be sold or decreases in Quality ▸ “Backstage passes", like Aged Brie, increases in Quality as it's SellIn value approaches; Quality increases by 2 when there are 10 days or less and by 3 when there are 5 days or less but Quality drops to 0 after the concerts have a Quality value which denotes how valuable the item is
  39. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { items[i].sellIn -= 1 if items[i].quality > 0 {
 if items[i].sellIn > 0 { items[i].quality -= 1 } if items[i].sellIn <= 0 { items[i].quality -= 2 } } } }
  40. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { items[i].sellIn -= 1 if items[i].quality > 0 {
 if items[i].sellIn > 0 { items[i].quality -= 1 } if items[i].sellIn <= 0 { items[i].quality -= 2 } } } }
  41. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { items[i].sellIn -= 1 if items[i].name != "Aged Brie" { if items[i].quality > 0 {
 if items[i].sellIn > 0 { items[i].quality -= 1 } if items[i].sellIn <= 0 { items[i].quality -= 2 } } } } }
  42. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { items[i].sellIn -= 1 if items[i].name != "Aged Brie" { // stuff that’s not about Aged Brie } else { if items[i].quality < 50 {
 if items[i].sellIn > 0 { items[i].quality += 1 } if items[i].sellIn <= 0 { items[i].quality += 2 } } } } }
  43. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { items[i].sellIn -= 1 if items[i].name != "Aged Brie" { // stuff that’s not about Aged Brie } else { // Aged Brie if items[i].quality < 50 {
 if items[i].sellIn > 0 { items[i].quality += 1 } if items[i].sellIn <= 0 { items[i].quality += 2 } } } } }
  44. GO NORTHWEST 2018 ~/go/src/gilded_rose$ go test -v --- PASS: TestBrie_BeforeSellDate

    (0.00s) === RUN TestBrie_BeforeSellDate_WithMaxQuality --- PASS: TestBrie_BeforeSellDate_WithMaxQuality (0.00s) === RUN TestBrieOnSellDate --- PASS: TestBrieOnSellDate (0.00s) === RUN TestBrieOnSellDateWithMaxQuality --- PASS: TestBrieOnSellDateWithMaxQuality (0.00s) === RUN TestBrie_AfterSellDate --- PASS: TestBrie_AfterSellDate (0.00s) === RUN TestBrieAfterSellDate_WithMaxQuality --- PASS: TestBrieAfterSellDate_WithMaxQuality (0.00s) PASS ok gilded_rose 0.005s "
  45. GO NORTHWEST 2018 // "Sulfuras", being a legendary item, never

    has to be sold or decreases in Quality func GildedRose() { for i := 0; i < len(items); i++ { items[i].sellIn = items[i].sellIn - 1 if items[i].name != "Aged Brie" { if items[i].quality > 0 {
 if items[i].sellIn > 0 { items[i].quality -= 1 } if items[i].sellIn <= 0 { items[i].quality -= 2 } } } else { // Aged Brie if items[i].quality < 50 {
 if items[i].sellIn > 0 { items[i].quality += 1 } if items[i].sellIn <= 0 { items[i].quality += 2 } } } }
  46. GO NORTHWEST 2018 // "Sulfuras", being a legendary item, never

    has to be sold or decreases in Quality func GildedRose() { for i := 0; i < len(items); i++ { if items[i].name != "Sulfuras, Hand of Ragnaros" { items[i].sellIn = items[i].sellIn - 1 if items[i].name != "Aged Brie" { if items[i].quality > 0 {
 if items[i].sellIn > 0 { items[i].quality -= 1 } if items[i].sellIn <= 0 { items[i].quality -= 2 } } } else { // Aged Brie if items[i].quality < 50 {
 if items[i].sellIn > 0 { items[i].quality += 1 } if items[i].sellIn <= 0 { items[i].quality += 2 } } } } }
  47. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { if items[i].name != "Sulfuras, Hand of Ragnaros" { items[i].sellIn = items[i].sellIn - 1 // Things whose quality decreases over time if items[i].name != "Aged Brie" && items[i].name != "Backstage passes to a TAFKAL80ETC concert" { if items[i].quality > 0 { if items[i].sellIn > 0 { items[i].quality -= 1 } if items[i].sellIn <= 0 { items[i].quality -= 2 } } // Things whose quality increases over time } else { // Aged Brie and Backstage passes // Quality can never exceed 50 if items[i].quality >= 50 { return } if items[i].name == "Aged Brie" { if items[i].sellIn > 0 { items[i].quality += 1 } if items[i].sellIn <= 0 { items[i].quality += 2 } } else if items[i].name == "Backstage passes to a TAFKAL80ETC concert" { if items[i].sellIn >= 10 { items[i].quality += 1 } if items[i].sellIn < 10 && items[i].sellIn > 5 { items[i].quality += 2 } if items[i].sellIn <= 5 && items[i].sellIn >= 0 { items[i].quality += 3 } if items[i].sellIn < 0 { items[i].quality = 0 } } } } } }
  48. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { if items[i].name != "Sulfuras, Hand of Ragnaros" { items[i].sellIn = items[i].sellIn - 1 // Things whose quality decreases over time if items[i].name != "Aged Brie" && items[i].name != "Backstage passes to a TAFKAL80ETC concert" { if items[i].quality > 0 { if items[i].sellIn > 0 { items[i].quality -= 1 } if items[i].sellIn <= 0 { items[i].quality -= 2 } } // Things whose quality increases over time } else { // Aged Brie and Backstage passes // Quality can never exceed 50 if items[i].quality >= 50 { return } if items[i].name == "Aged Brie" { if items[i].sellIn > 0 { items[i].quality += 1 } if items[i].sellIn <= 0 { items[i].quality += 2 } } else if items[i].name == "Backstage passes to a TAFKAL80ETC concert" { if items[i].sellIn >= 10 { items[i].quality += 1 } if items[i].sellIn < 10 && items[i].sellIn > 5 { items[i].quality += 2 } if items[i].sellIn <= 5 && items[i].sellIn >= 0 { items[i].quality += 3 } if items[i].sellIn < 0 { items[i].quality = 0 }
  49. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { if items[i].name != "Sulfuras, Hand of Ragnaros" { items[i].sellIn = items[i].sellIn - 1 // Things whose quality decreases over time if items[i].name != "Aged Brie" && items[i].name != "Backstage passes to a TAFKAL80ETC concert" { if items[i].quality == 0 { return } items[i].quality -= 1 if items[i].sellIn <= 0 { items[i].quality -= 1 } // Things whose quality increases over time } else { // Aged Brie and Backstage passes // Quality can never exceed 50 if items[i].quality >= 50 { return } items[i].quality += 1 if items[i].name == "Aged Brie" { if items[i].sellIn <= 0 { items[i].quality += 1 } } else if items[i].name == "Backstage passes to a TAFKAL80ETC concert" { if items[i].sellIn < 10 { items[i].quality += 1 } if items[i].sellIn < 5 { items[i].quality += 1 } if items[i].sellIn < 0 { items[i].quality = 0 } }
  50. GO NORTHWEST 2018 HOW DID WE MAKE SUCH A MESS?

    ▸Relatively Easy ▸Cheap ▸Abstraction was not yet clear
  51. BUT AT SOME POINT WE HAVE TO BALANCE DELIVERY WITH

    MAINTAINABILITY
  52. HAVE EMPATHY FOR THE PROCESS AND THE DEVELOPERS WHO CAME

    BEFORE YOU (BECAUSE SOMETIMES THAT DEVELOPER WAS YOU)
  53. HOW DO WE FIX IT?

  54. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { if items[i].name != "Aged Brie" && items[i].name != "Backstage passes to a TAFKAL80ETC concert" { if items[i].quality > 0 { if items[i].name != "Sulfuras, Hand of Ragnaros" { items[i].quality = items[i].quality - 1 } } } else { if items[i].quality < 50 { items[i].quality = items[i].quality + 1 if items[i].name == "Backstage passes to a TAFKAL80ETC concert" { if items[i].sellIn < 11 { if items[i].quality < 50 { items[i].quality = items[i].quality + 1 } } if items[i].sellIn < 6 { if items[i].quality < 50 { items[i].quality = items[i].quality + 1 } } } } } if items[i].name != "Sulfuras, Hand of Ragnaros" { items[i].sellIn = items[i].sellIn - 1 } if items[i].sellIn < 0 { if items[i].name != "Aged Brie" { if items[i].name != "Backstage passes to a TAFKAL80ETC concert" { if items[i].quality > 0 { if items[i].name != "Sulfuras, Hand of Ragnaros" { items[i].quality = items[i].quality - 1 } } } else { items[i].quality = items[i].quality - items[i].quality } } else { if items[i].quality < 50 { items[i].quality = items[i].quality + 1 } } } } }
  55. OBJECTS 
 ENCAPSULATION OF DATA + BEHAVIOR USED TO INTERACT

    WITH THAT DATA
  56. OBJECTS 
 ENCAPSULATION OF DATA + BEHAVIOR USED TO INTERACT

    WITH THAT DATA
  57. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { if items[i].name == "Normal" { update_normal(&items[i]) return } // Deal with quality // Things whose quality decreases over time if items[i].name != "Aged Brie" && items[i].name != "Backstage passes to a TAFKAL80ETC concert" { // Catch lower limit if items[i].quality > 0 { if items[i].name != "Sulfuras, Hand of Ragnaros" { items[i].quality = items[i].quality - 1 } }
  58. GO NORTHWEST 2018 func update_normal(item *Item) { return }

  59. GO NORTHWEST 2018 --- FAIL: TestNormal_BeforeSellDate (0.00s) gilded_rose_test.go:19: Sell In

    = 2, want 1 --- FAIL: TestNormal_BeforeSellDate_WithMinQuality (0.00s) gilded_rose_test.go:36: Sell In = 2, want 1 --- FAIL: TestNormal_OnSellDate (0.00s) gilded_rose_test.go:53: Sell In = 0, want -1 --- FAIL: TestNormal_OnSellDate_WithMinQuality (0.00s) gilded_rose_test.go:71: Sell In = 0, want -1 --- FAIL: TestNormal_AfterSellDate (0.00s) gilded_rose_test.go:89: Sell In = -2, want -3 --- FAIL: TestNormal_AfterSellDate_WithMinQuality (0.00s) gilded_rose_test.go:106: Sell In = -2, want -3 FAIL exit status 1 FAIL gilded_rose 0.005s
  60. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { if items[i].name != "Sulfuras, Hand of Ragnaros" { items[i].sellIn = items[i].sellIn - 1 if items[i].name != "Aged Brie" && items[i].name != "Backstage passes to a TAFKAL80ETC concert" { if items[i].quality == 0 { return } items[i].quality -= 1 if items[i].sellIn <= 0 { items[i].quality -= 1 } } else { if items[i].quality >= 50 { return } items[i].quality += 1 if items[i].name == "Aged Brie" { if items[i].sellIn <= 0 { items[i].quality += 1 } } else if items[i].name == "Backstage passes to a TAFKAL80ETC concert" { if items[i].sellIn < 10 { items[i].quality += 1 } if items[i].sellIn < 5 { items[i].quality += 1 } if items[i].sellIn < 0 { items[i].quality = 0 } } } } } }
  61. GO NORTHWEST 2018 func update_normal(item *Item) { item.sellIn -= 1

    if item.quality <= 0 { return } item.quality -= 1 if item.sellIn <= 0 { item.quality -= 1 } return }
  62. GO NORTHWEST 2018 --- PASS: TestNormal_BeforeSellDate (0.00s) === RUN TestNormal_BeforeSellDate_WithMinQuality

    --- PASS: TestNormal_BeforeSellDate_WithMinQuality (0.00s) === RUN TestNormal_OnSellDate --- PASS: TestNormal_OnSellDate (0.00s) === RUN TestNormal_OnSellDate_WithMinQuality --- PASS: TestNormal_OnSellDate_WithMinQuality (0.00s) === RUN TestNormal_AfterSellDate --- PASS: TestNormal_AfterSellDate (0.00s) === RUN TestNormal_AfterSellDate_WithMinQuality --- PASS: TestNormal_AfterSellDate_WithMinQuality (0.00s) PASS ok gilded_rose 0.006s "
  63. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { if items[i].name == "Normal" { update_normal(&items[i]) return } else if items[i].name == “Aged Brie" { update_brie(&items[i]) return } // ... existing mess
  64. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { switch items[i].name { case "Normal": update_normal(&items[i]) return case "Aged Brie": update_brie(&items[i]) return } // ... existing mess
  65. GO NORTHWEST 2018 func update_brie(item *Item) { item.sellIn -= 1

    if item.quality >= 50 { return } item.quality += 1 if item.sellIn <= 0 { item.quality += 1 } return }
  66. GO NORTHWEST 2018 --- PASS: TestBrie_BeforeSellDate (0.00s) === RUN TestBrie_BeforeSellDate_WithMaxQuality

    --- PASS: TestBrie_BeforeSellDate_WithMaxQuality (0.00s) === RUN TestBrieOnSellDate --- PASS: TestBrieOnSellDate (0.00s) === RUN TestBrieOnSellDateWithMaxQuality --- PASS: TestBrieOnSellDateWithMaxQuality (0.00s) === RUN TestBrie_AfterSellDate --- PASS: TestBrie_AfterSellDate (0.00s) === RUN TestBrieAfterSellDate_WithMaxQuality --- PASS: TestBrieAfterSellDate_WithMaxQuality (0.00s) PASS ok gilded_rose 0.006s "
  67. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { switch items[i].name { case "Normal": update_normal(&items[i]) return case "Aged Brie": update_brie(&items[i]) return } // ... existing mess
  68. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { switch items[i].name { case "Normal": update_normal(&items[i]) return case "Aged Brie": update_brie(&items[i]) return case "Sulfuras, Hand of Ragnaros": update_sulfuras(&items[i]) return } // ... existing mess
  69. GO NORTHWEST 2018 GILDED ROSE REFACTORING KATA: THE REQUIREMENTS ▸

    Once the sell by date has passed, Quality degrades twice as fast ▸ The Quality of an item is never negative ▸ Some items actually increase in Quality the older it gets, e.g. “Aged Brie” ▸ The Quality of an item is never more than 50 ▸ "Sulfuras", being a legendary item, never has to be sold or decreases in Quality ▸ “Backstage passes", like Aged Brie, increases in Quality as it's SellIn value approaches; Quality increases by 2 when there are 10 days or less and by 3 when there are 5 days or less but Quality drops to 0 after the concerts have a Quality value which denotes how valuable the item is
  70. GO NORTHWEST 2018 func update_sulfuras(item *Item) { return }

  71. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { switch items[i].name { case "Normal": update_normal(&items[i]) return case "Aged Brie": update_brie(&items[i]) return case "Sulfuras, Hand of Ragnaros": update_sulfuras(&items[i]) return case "Backstage passes to a TAFKAL80ETC concert": update_backstage(&items[i]) return } // ... existing mess
  72. GO NORTHWEST 2018 func update_backstage(item *Item) { item.sellIn -= 1

    if item.quality >= 50 { return } item.quality += 1 if item.sellIn < 10 { item.quality += 1 } if item.sellIn < 5 { item.quality += 1 } if item.sellIn < 0 { item.quality = 0 } return }
  73. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { switch items[i].name { case "Normal": update_normal(&items[i]) return case "Aged Brie": update_brie(&items[i]) return case "Sulfuras, Hand of Ragnaros": update_sulfuras(&items[i]) return case "Backstage passes to a TAFKAL80ETC concert": update_backstage(&items[i]) return } // ... existing mess Default?
  74. GO NORTHWEST 2018 package main type Item struct { name

    string sellIn, quality int } var items = []Item{ Item{"+5 Dexterity Vest", 10, 20}, Item{"Aged Brie", 2, 0}, Item{“Chunky Bacon", 5, 7}, Item{"Sulfuras, Hand of Ragnaros", 0, 80}, Item{"Backstage passes to a TAFKAL80ETC concert", 15, 20}, Item{"Conjured Mana Cake", 3, 6}, } func main() { GildedRose() } ...
  75. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { switch items[i].name { case "Normal": update_normal(&items[i]) return case "Aged Brie": update_brie(&items[i]) return case "Sulfuras, Hand of Ragnaros": update_sulfuras(&items[i]) return case "Backstage passes to a TAFKAL80ETC concert": update_backstage(&items[i]) return default: update_default(&items[i]) return } // ... existing mess
  76. GO NORTHWEST 2018 func update_default(item *Item) { return }

  77. GO NORTHWEST 2018 func update_sulfuras(item *Item) { return }

  78. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { switch items[i].name { case "Normal": update_normal(&items[i]) return case "Aged Brie": update_brie(&items[i]) return case "Sulfuras, Hand of Ragnaros": update_sulfuras(&items[i]) return case "Backstage passes to a TAFKAL80ETC concert": update_backstage(&items[i]) return default: update_default(&items[i]) return } // ... existing mess
  79. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { switch items[i].name { case "Normal": update_normal(&items[i]) return case "Aged Brie": update_brie(&items[i]) return case "Backstage passes to a TAFKAL80ETC concert": update_backstage(&items[i]) return default: update_default(&items[i]) return } // ... existing mess
  80. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { switch items[i].name { case "Normal": update_normal(&items[i]) return case "Aged Brie": update_brie(&items[i]) return case "Backstage passes to a TAFKAL80ETC concert": update_backstage(&items[i]) return default: update_default(&items[i]) } // ... existing mess
  81. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { switch items[i].name { case "Normal": update_normal(&items[i]) return case "Aged Brie": update_brie(&items[i]) return case "Backstage passes to a TAFKAL80ETC concert": update_backstage(&items[i]) return default: update_default(&items[i]) } } }
  82. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { switch items[i].name { case "Normal": update_normal(&items[i]) case "Aged Brie": update_brie(&items[i]) case "Backstage passes to a TAFKAL80ETC concert": update_backstage(&items[i]) default: update_default(&items[i]) } return } }
  83. GO NORTHWEST 2018 $ go test PASS ok gilded_rose 0.006s

    "
  84. ADDING THE NEW FEATURE…

  85. GO NORTHWEST 2018 ▸ We have recently signed a supplier

    of conjured items. This requires an update to our system: ▸ “Conjured” items degrade in Quality twice as fast as normal items GILDED ROSE REFACTORING KATA: THE NEW FEATURE
  86. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { switch items[i].name { case "Normal": update_normal(&items[i]) case "Aged Brie": update_brie(&items[i]) case "Backstage passes to a TAFKAL80ETC concert": update_backstage(&items[i]) default: update_default(&items[i]) } return } }
  87. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { switch items[i].name { case "Normal": update_normal(&items[i]) case "Aged Brie": update_brie(&items[i]) case "Backstage passes to a TAFKAL80ETC concert": update_backstage(&items[i]) case “Conjured Mana Cake": update_conjured(&items[i]) default: update_default(&items[i]) } return } }
  88. SOMETHING FEELS WRONG

  89. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { switch items[i].name { case "Normal": update_normal(&items[i]) case "Aged Brie": update_brie(&items[i]) case "Backstage passes to a TAFKAL80ETC concert": update_backstage(&items[i]) case “Conjured Mana Cake": update_conjured(&items[i]) default: update_default(&items[i]) } return } }
  90. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { switch items[i].name { case "Normal": update_normal(&items[i]) case "Aged Brie": update_brie(&items[i]) case "Backstage passes to a TAFKAL80ETC concert": update_backstage(&items[i]) case “Conjured Mana Cake": update_conjured(&items[i]) case “Chunky Bacon": update_bacon(&items[i]) default: update_default(&items[i]) } return } }
  91. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { switch items[i].name { case "Normal": update_normal(&items[i]) case "Aged Brie": update_brie(&items[i]) case "Backstage passes to a TAFKAL80ETC concert": update_backstage(&items[i]) case “Conjured Mana Cake": update_conjured(&items[i]) case “Chunky Bacon": update_bacon(&items[i]) 
 case “+5 Dexterity Vest": update_vest(&items[i]) default: update_default(&items[i]) } return } }
  92. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { switch items[i].name { case "Normal": update_normal(&items[i]) case "Aged Brie": update_brie(&items[i]) case "Backstage passes to a TAFKAL80ETC concert": update_backstage(&items[i]) case “Conjured Mana Cake": update_conjured(&items[i]) case “Chunky Bacon": update_bacon(&items[i]) case “+5 Dexterity Vest": update_vest(&items[i]) case “Gorbypuff Thunderhorse": update_cat(&items[i]) 
 default: update_default(&items[i]) } return } } ❌
  93. ENCAPSULATION WITHOUT OBJECT-ORIENTATION

  94. SOLID DESIGN

  95. SOLID DESIGN

  96. OPEN/CLOSED: OPEN FOR EXTENSION CLOSED FOR MODIFICATION

  97. GO NORTHWEST 2018 #! /usr/local/bin/ruby class Default < Item class

    Normal < Item class Brie < Item class BackstagePass < Item
  98. GO NORTHWEST 2018 #! /usr/local/bin/ruby class Default < Item class

    Normal < Item class Brie < Item class BackstagePass < Item class ConjuredMana < Item
  99. INHERITANCE (…IN GO?!)

  100. "

  101. GO NORTHWEST 2018 type Item struct { name string sellIn,

    quality int }
  102. GO NORTHWEST 2018 type Normal struct { Item } type

    Brie struct { Item } type Sulfuras struct { Item } type BackstagePass struct { Item }
  103. EMBEDDING

  104. COMPOSITION -

  105. GO NORTHWEST 2018 type Normal struct { Item } type

    Brie struct { Item } type Sulfuras struct { Item } type BackstagePass struct { Item } ❌
  106. ‣ Conceptual level: 
 A set of responsibilities ‣ Specification

    level: 
 A set of methods or behaviors that can be invoked by other objects or by itself ‣ Implementation level: 
 A set of code and data and computation interactions between them
  107. USING INTERFACES AS SPECIFICATION FOR OBJECTS

  108. GO NORTHWEST 2018 type Item struct { name string sellIn

    int quality int }
  109. GO NORTHWEST 2018 type Item interface { name string sellIn

    int quality int }
  110. GO NORTHWEST 2018 type Item interface { Name() string SellIn()

    int Quality() int }
  111. GO NORTHWEST 2018 type Item interface { Name() string SellIn()

    int Quality() int Update() }
  112. CONCRETE IMPLEMENTATION OF OBJECT INTERFACE

  113. GO NORTHWEST 2018 type Normal struct { name string sellIn,

    quality int } type Item interface { Name() string SellIn() int Quality() int Update() }
  114. GO NORTHWEST 2018 type Normal struct { name string sellIn,

    quality int } func (item *Normal) Name() string { return item.name } func (item *Normal) SellIn() int { return item.sellIn } func (item *Normal) Quality() int { return item.quality }
  115. GO NORTHWEST 2018 func (item *Normal) Update() { }

  116. GO NORTHWEST 2018 func (item *Normal) Update() { item.sellIn -=

    1 if item.quality <= 0 { return } item.quality -= 1 if item.sellIn <= 0 { item.quality -= 1 } return }
  117. GO NORTHWEST 2018 type Normal struct { name string sellIn,

    quality int } func (item *Normal) Name() string { return item.name } func (item *Normal) SellIn() int { return item.sellIn } func (item *Normal) Quality() int { return item.quality } func (item *Normal) Update() { item.sellIn -= 1 if item.quality <= 0 { return } item.quality -= 1 if item.sellIn <= 0 { item.quality -= 1 } return }
  118. GO NORTHWEST 2018 var items = []Item{ Item{"+5 Dexterity Vest",

    10, 20}, Item{"Aged Brie", 2, 0}, Item{“Chunky Bacon”, 5, 7}, Item{"Sulfuras, Hand of Ragnaros", 0, 80}, Item{"Backstage passes to a TAFKAL80ETC concert", 15, 20}, Item{"Conjured Mana Cake", 3, 6}, }
  119. GO NORTHWEST 2018 type Item struct { name string sellIn,

    quality int } type Item interface { Name() string SellIn() int Quality() int Update() }
  120. GO NORTHWEST 2018 type Params struct { name string sellIn,

    quality int } type Item interface { Name() string SellIn() int Quality() int Update() } -
  121. GO NORTHWEST 2018 var items = []Item{ Item{"+5 Dexterity Vest",

    10, 20}, Item{"Aged Brie", 2, 0}, Item{“Chunky Bacon”, 5, 7}, Item{"Sulfuras, Hand of Ragnaros", 0, 80}, Item{"Backstage passes to a TAFKAL80ETC concert", 15, 20}, Item{"Conjured Mana Cake", 3, 6}, }
  122. GO NORTHWEST 2018 var items = []Params{ Params{"+5 Dexterity Vest",

    10, 20}, Params{"Aged Brie", 2, 0}, Params{“Chunky Bacon”, 5, 7}, Params{"Sulfuras, Hand of Ragnaros", 0, 80}, Params{"Backstage passes to a TAFKAL80ETC concert", 15, 20}, Params{"Conjured Mana Cake", 3, 6}, }
  123. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { switch items[i].name { case "Normal": update_normal(&items[i]) case "Aged Brie": update_brie(&items[i]) case "Backstage passes to a TAFKAL80ETC concert": update_backstage(&items[i]) default: update_default(&items[i]) } return } }
  124. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { switch items[i].name { case "Normal": update_normal(&items[i]) case "Aged Brie": update_brie(&items[i]) case "Backstage passes to a TAFKAL80ETC concert": update_backstage(&items[i]) default: update_default(&items[i]) } return } }
  125. GO NORTHWEST 2018 func update_normal(p *Params) { item := Normal{p.name,

    p.sellIn, p.quality} item.Update() }
  126. SOMETHING FEELS WRONG

  127. GO NORTHWEST 2018 func update_normal(p *Params) { item := Normal{p.name,

    p.sellIn, p.quality} item.Update() }
  128. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { switch items[i].name { p := &items[i] case "Normal": update_normal(p) case "Aged Brie": update_brie(p) case "Backstage passes to a TAFKAL80ETC concert": update_backstage(p) default: update_default(p) } return } } Objects are created here
  129. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { switch items[i].name { p := &items[i] case "Normal": update_normal(p) case "Aged Brie": update_brie(p) case "Backstage passes to a TAFKAL80ETC concert": update_backstage(p) default: update_default(p) } return } }
  130. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { switch items[i].name { p := &items[i] case "Normal": item := Normal{p.name, p.sellIn, p.quality} item.Update() case "Aged Brie": item := Brie{p.name, p.sellIn, p.quality} item.Update() case "Backstage passes to a TAFKAL80ETC concert": item := BackstagePass{p.name, p.sellIn, p.quality} item.Update() default: item := Default{p.name, p.sellIn, p.quality} item.Update() } return } }
  131. GO NORTHWEST 2018 var items = []Params{ Params{"+5 Dexterity Vest",

    10, 20}, Params{"Aged Brie", 2, 0}, Params{"Chunky Bacon", 5, 7}, Params{"Sulfuras, Hand of Ragnaros", 0, 80}, Params{"Backstage passes to a TAFKAL80ETC concert", 15, 20}, Params{"Conjured Mana Cake", 3, 6}, }
  132. GO NORTHWEST 2018 var items = []Item{ Params{"+5 Dexterity Vest",

    10, 20}, Params{"Aged Brie", 2, 0}, Params{“Chunky Bacon”, 5, 7}, Params{"Sulfuras, Hand of Ragnaros", 0, 80}, Params{"Backstage passes to a TAFKAL80ETC concert", 15, 20}, Params{"Conjured Mana Cake", 3, 6}, }
  133. FACTORY PATTERN

  134. GO NORTHWEST 2018 var items = []Params{ Params{"+5 Dexterity Vest",

    10, 20}, Params{"Aged Brie", 2, 0}, Params{"Chunky Bacon", 5, 7}, Params{"Sulfuras, Hand of Ragnaros", 0, 80}, Params{"Backstage passes to a TAFKAL80ETC concert", 15, 20}, Params{"Conjured Mana Cake", 3, 6}, }
  135. GO NORTHWEST 2018 var items = []Item{ ItemFactory("+5 Dexterity Vest",

    10, 20), ItemFactory("Aged Brie", 2, 0), ItemFactory("Chunky Bacon", 5, 7), ItemFactory("Sulfuras, Hand of Ragnaros", 0, 80), ItemFactory("Backstage passes to a TAFKAL80ETC concert", 15, 20), ItemFactory("Conjured Mana Cake", 3, 6), }
  136. “Define an interface for creating an object, but let subclasses

    decide which class to instantiate. The Factory Method lets a class defer instantiation to subclasses.” — .. ..
  137. GO NORTHWEST 2018 type Factory func(string, int, int) Item

  138. GO NORTHWEST 2018 type Factory func(string, int, int) Item //

    Normal{p.name, p.sellIn, p.quality}
  139. GO NORTHWEST 2018 type Factory func(string, int, int) Item func

    NewNormal(name string, sellIn, quality int) Item { return &Normal{name, sellIn, quality} }
  140. GO NORTHWEST 2018 type Normal struct { name string sellIn,

    quality int } type Params struct { name string sellIn, quality int }
  141. GO NORTHWEST 2018 type Normal struct { Params } type

    Params struct { name string sellIn, quality int } -
  142. GO NORTHWEST 2018 type Factory func(Params) Item func NewNormal(params Params)

    Item { return &Normal{params} }
  143. GO NORTHWEST 2018 type Factory func(Params) Item func NewNormal(params Params)

    Item { return &Normal{params} } func NewBrie(params Params) Item { return &Brie{params} } func NewBackstagePass(params Params) Item { return &BackstagePass{params} } func NewDefault(params Params) Item { return &Default{params} }
  144. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { switch items[i].name { p := &items[i] case "Normal": item := Normal{p.name, p.sellIn, p.quality} item.Update() case "Aged Brie": item := Brie{p.name, p.sellIn, p.quality} item.Update() case "Backstage passes to a TAFKAL80ETC concert": item := BackstagePass{p.name, p.sellIn, p.quality} item.Update() default: item := Default{p.name, p.sellIn, p.quality} item.Update() } return } }
  145. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { switch items[i].name { p := &items[i] case "Normal": item := NewNormal(p) item.Update() case "Aged Brie": item := NewBrie(p) item.Update() case "Backstage passes to a TAFKAL80ETC concert": item := NewBackstagePass(p) item.Update() default: item := NewDefault(p) item.Update() } return } }
  146. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { switch items[i].name { p := &items[i] case "Normal": item := NewNormal(p) item.Update() case "Aged Brie": item := NewBrie(p) item.Update() case "Backstage passes to a TAFKAL80ETC concert": item := NewBackstagePass(p) item.Update() default: item := NewDefault(p) item.Update() } return } }
  147. GO NORTHWEST 2018 func ItemFactory(name string, sellIn, quality int) Item

    { params := Params{name, sellIn, quality} var item Item switch name { case "Normal": item = NewNormal(params) case "Aged Brie": item = NewBrie(params) case "Backstage passes to a TAFKAL80ETC concert": item = NewBackstagePass(params) default: item = NewDefault(params) } return item }
  148. GO NORTHWEST 2018 func ItemFactory(name string, sellIn, quality int) Item

    { params := Params{name, sellIn, quality} var item Item switch name { case "Normal": item = NewNormal(params) case "Aged Brie": item = NewBrie(params) case "Backstage passes to a TAFKAL80ETC concert": item = NewBackstagePass(params) default: item = NewDefault(params) } return item }
  149. REGISTER FACTORIES

  150. GO NORTHWEST 2018 type Factory func(Params) Item var Factories =

    map[string]Factory{ "Normal": NewNormal, "Aged Brie": NewBrie, "Backstage passes to a TAFKAL80ETC concert": NewBackstagePass, } func ItemFactory(name string, sellIn, quality int) Item { params := Params{name, sellIn, quality} var item Item switch name { case "Normal": item = NewNormal(params) case "Aged Brie": item = NewBrie(params) case "Backstage passes to a TAFKAL80ETC concert": item = NewBackstagePass(params) default: item = NewDefault(params) } return item }
  151. GO NORTHWEST 2018 type Factory func(Params) Item var Factories =

    map[string]Factory{ "Normal": NewNormal, "Aged Brie": NewBrie, "Backstage passes to a TAFKAL80ETC concert": NewBackstagePass, } func ItemFactory(name string, sellIn, quality int) Item { params := Params{name, sellIn, quality} var item Item switch name { case "Normal": item = NewNormal(params) case "Aged Brie": item = NewBrie(params) case "Backstage passes to a TAFKAL80ETC concert": item = NewBackstagePass(params) default: item = NewDefault(params) } return item }
  152. GO NORTHWEST 2018 type Factory func(Params) Item var Factories =

    map[string]Factory{ "Normal": NewNormal, "Aged Brie": NewBrie, "Backstage passes to a TAFKAL80ETC concert": NewBackstagePass, } func ItemFactory(name string, sellIn, quality int) Item { params := Params{name, sellIn, quality} itemFactory, ok := Factories[name] if !ok { itemFactory = NewDefault } item := itemFactory(params) return item }
  153. GO NORTHWEST 2018 var items = []Item{ ItemFactory("+5 Dexterity Vest",

    10, 20), ItemFactory("Aged Brie", 2, 0), ItemFactory(“Chunky Bacon”, 5, 7), ItemFactory("Sulfuras, Hand of Ragnaros", 0, 80), ItemFactory("Backstage passes to a TAFKAL80ETC concert", 15, 20), ItemFactory("Conjured Mana Cake", 3, 6), } func GildedRose() { for i := 0; i < len(items); i++ { //... } } ACTUAL COLLECTION OF ITEMS
  154. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { item := items[i] item.Update() } }
  155. WHERE WE ARE SO FAR

  156. GO NORTHWEST 2018 type Item struct { name string sellIn,

    quality int } var items = []Item{ Item{"+5 Dexterity Vest", 10, 20}, Item{"Aged Brie", 2, 0}, Item{“Chunky Bacon”, 5, 7}, Item{"Sulfuras, Hand of Ragnaros", 0, 80}, Item{"Backstage passes to a TAFKAL80ETC concert", 15, 20}, Item{"Conjured Mana Cake", 3, 6}, }
  157. GO NORTHWEST 2018 type Item interface { Name() string SellIn()

    int Quality() int Update() } type Params struct { name string sellIn, quality int } var items = []Item{ ItemFactory("+5 Dexterity Vest", 10, 20), ItemFactory("Aged Brie", 2, 0), ItemFactory(“Chunky Bacon”, 5, 7), ItemFactory("Sulfuras, Hand of Ragnaros", 0, 80), ItemFactory("Backstage passes to a TAFKAL80ETC concert", 15, 20), ItemFactory("Conjured Mana Cake", 3, 6), }
  158. GO NORTHWEST 2018 type Factory func(Params) Item var Factories =

    map[string]Factory{ "Normal": NewNormal, "Aged Brie": NewBrie, "Backstage passes to a TAFKAL80ETC concert": NewBackstagePass, } func ItemFactory(name string, sellIn, quality int) Item { params := Params{name, sellIn, quality} itemFactory, ok := Factories[name] if !ok { itemFactory = NewDefault } item := itemFactory(params) return item }
  159. GO NORTHWEST 2018 type Default struct { Params } func

    NewDefault(params Params) Item { return &Default{params} } func (item *Default) Name() string { return item.name } func (item *Default) SellIn() int { return item.sellIn } func (item *Default) Quality() int { return item.quality } func (item *Default) Update() { return }
  160. GO NORTHWEST 2018 type Normal struct { Params } func

    NewNormal(params Params) Item { return &Normal{params} } func (item *Normal) Name() string { return item.name } func (item *Normal) SellIn() int { return item.sellIn } func (item *Normal) Quality() int { return item.quality } func (item *Normal) Update() { item.sellIn -= 1 if item.quality <= 0 { return } item.quality -= 1 if item.sellIn <= 0 { item.quality -= 1 } return } type BackstagePass struct { Params } func NewBackstagePass(params Params) Item { return &BackstagePass{params} } func (item *BackstagePass) Name() string { return item.name } func (item *BackstagePass) SellIn() int { return item.sellIn } func (item *BackstagePass) Quality() int { return item.quality } func (item *BackstagePass) Update() { item.sellIn -= 1 if item.quality >= 50 { return } item.quality += 1 if item.sellIn < 10 { item.quality += 1 } if item.sellIn < 5 { item.quality += 1 } if item.sellIn < 0 { item.quality = 0 } return } type Brie struct { Params } func NewBrie(params Params) Item { return &Brie{params} } func (item *Brie) Name() string { return item.name } func (item *Brie) SellIn() int { return item.sellIn } func (item *Brie) Quality() int { return item.quality } func (item *Brie) Update() { item.sellIn -= 1 if item.quality >= 50 { return } item.quality += 1 if item.sellIn <= 0 { item.quality += 1 } return }
  161. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { if items[i].name != "Aged Brie" && items[i].name != "Backstage passes to a TAFKAL80ETC concert" { if items[i].quality > 0 { if items[i].name != "Sulfuras, Hand of Ragnaros" { items[i].quality = items[i].quality - 1 } } } else { if items[i].quality < 50 { items[i].quality = items[i].quality + 1 if items[i].name == "Backstage passes to a TAFKAL80ETC concert" { if items[i].sellIn < 11 { if items[i].quality < 50 { items[i].quality = items[i].quality + 1 } } if items[i].sellIn < 6 { if items[i].quality < 50 { items[i].quality = items[i].quality + 1 } } } } } if items[i].name != "Sulfuras, Hand of Ragnaros" { items[i].sellIn = items[i].sellIn - 1 } if items[i].sellIn < 0 { if items[i].name != "Aged Brie" { if items[i].name != "Backstage passes to a TAFKAL80ETC concert" { if items[i].quality > 0 { if items[i].name != "Sulfuras, Hand of Ragnaros" { items[i].quality = items[i].quality - 1 } } } else { items[i].quality = items[i].quality - items[i].quality } } else { if items[i].quality < 50 { items[i].quality = items[i].quality + 1 } // …
  162. GO NORTHWEST 2018 func GildedRose() { for i := 0;

    i < len(items); i++ { item := items[i] item.Update() } }
  163. A NOTE ON CODE ORGANIZATION

  164. GO NORTHWEST 2018 package main var inventory = []items.Item{ ItemFactory("+5

    Dexterity Vest", 10, 20), ItemFactory("Aged Brie", 2, 0), ItemFactory("Chunky Bacon", 5, 7), ItemFactory("Sulfuras, Hand of Ragnaros", 0, 80), ItemFactory("Backstage passes to a TAFKAL80ETC concert", 15, 20), ItemFactory("Conjured Mana Cake", 3, 6), } func main() { GildedRose() } func GildedRose() { for i := 0; i < len(inventory); i++ { item := inventory[i] item.Update() } }
  165. GO NORTHWEST 2018 package main type Item interface { Name()

    string SellIn() int Quality() int Update() } type Params struct { name string sellIn, quality int } type Factory func(Params) Item var Factories = map[string]Factory{ "Normal": NewNormal, "Aged Brie": NewBrie, "Backstage passes to a TAFKAL80ETC concert": NewBackstagePass, } func ItemFactory(name string, sellIn, quality int) Item { params := Params{name, sellIn, quality} itemFactory, ok := Factories[name] if !ok { itemFactory = NewDefault } item := itemFactory(params) return item }
  166. GO NORTHWEST 2018 module Item class ItemFactory ... def create

    ... end end class Default ...
 end def update ...
 end end item = Item::ItemFactory.create(name, sellIn, quality)
  167. PACKAGES AS OBJECT NAMESPACES

  168. GO NORTHWEST 2018 package main type Item interface { Name()

    string SellIn() int Quality() int Update() } type Params struct { name string sellIn, quality int } type Factory func(Params) Item var Factories = map[string]Factory{ "Normal": NewNormal, "Aged Brie": NewBrie, "Backstage passes to a TAFKAL80ETC concert": NewBackstagePass, } func ItemFactory(name string, sellIn, quality int) Item { params := Params{name, sellIn, quality} itemFactory, ok := Factories[name] if !ok { itemFactory = NewDefault } item := itemFactory(params) return item }
  169. GO NORTHWEST 2018 package items type Item interface { Name()

    string SellIn() int Quality() int Update() } type Params struct { name string sellIn, quality int } type Factory func(Params) Item var Factories = map[string]Factory{ "Normal": NewNormal, "Aged Brie": NewBrie, "Backstage passes to a TAFKAL80ETC concert": NewBackstagePass, } func ItemFactory(name string, sellIn, quality int) Item { params := Params{name, sellIn, quality} itemFactory, ok := Factories[name] if !ok { itemFactory = NewDefault } item := itemFactory(params) return item }
  170. GO NORTHWEST 2018 package main type Normal struct { Params

    } func NewNormal(params Params) Item { return &Normal{params} } func (item *Normal) Name() string { return "Normal" } func (item *Normal) SellIn() int { return item.sellIn } func (item *Normal) Quality() int { return item.quality } func (item *Normal) Update() { item.sellIn -= 1 if item.quality <= 0 { return } item.quality -= 1 if item.sellIn <= 0 { item.quality -= 1 } return }
  171. GO NORTHWEST 2018 package items type Normal struct { Params

    } func NewNormal(params Params) Item { return &Normal{params} } func (item *Normal) Name() string { return "Normal" } func (item *Normal) SellIn() int { return item.sellIn } func (item *Normal) Quality() int { return item.quality } func (item *Normal) Update() { item.sellIn -= 1 if item.quality <= 0 { return } item.quality -= 1 if item.sellIn <= 0 { item.quality -= 1 } return }
  172. GO NORTHWEST 2018 package main import ( "gilded_rose/items" ) var

    inventory = []items.Item{ items.ItemFactory("+5 Dexterity Vest", 10, 20), items.ItemFactory("Aged Brie", 2, 0), items.ItemFactory("Chunky Bacon", 5, 7), items.ItemFactory("Sulfuras, Hand of Ragnaros", 0, 80), items.ItemFactory("Backstage passes to a TAFKAL80ETC concert", 15, 20), items.ItemFactory("Conjured Mana Cake", 3, 6), } func main() { GildedRose() } func GildedRose() { for i := 0; i < len(inventory); i++ { item := inventory[i] item.Update() } }
  173. Gilded Rose Item Default Normal Brie Backstage Pass main.go items/item.go

    ItemFactory items/default.go items/normal.go items/brie.go items/backstage.go
  174. BUT WAIT, THERE’S MORE!

  175. GO NORTHWEST 2018 package items type Default struct { name

    string sellIn, quality int } func NewDefault(name string, sellIn, quality int) Item { return &Default{name, sellIn, quality} } func (item *Default) Name() string { return item.name } func (item *Default) SellIn() int { return item.sellIn } func (item *Default) Quality() int { return item.quality } func (item *Default) Update() { return }
  176. GO NORTHWEST 2018 package items type Default struct { name

    string sellIn, quality int } func NewDefault(name string, sellIn, quality int) Item { return &Default{name, sellIn, quality} } func (item *Default) Name() string { return item.name } func (item *Default) SellIn() int { return item.sellIn } func (item *Default) Quality() int { return item.quality } func (item *Default) Update() { return }
  177. GO NORTHWEST 2018 package items type Normal struct { Params

    } func NewNormal(params Params) Item { return &Normal{params} } func (item *Normal) Name() string { return "Normal" } func (item *Normal) SellIn() int { return item.sellIn } func (item *Normal) Quality() int { return item.quality } func (item *Normal) Update() { item.sellIn -= 1 if item.quality <= 0 { return } item.quality -= 1 if item.sellIn <= 0 { item.quality -= 1 } return } package items type Brie struct { Params } func NewBrie(params Params) Item { return &Brie{params} } func (item *Brie) Name() string { return item.name } func (item *Brie) SellIn() int { return item.sellIn } func (item *Brie) Quality() int { return item.quality } func (item *Brie) Update() { item.sellIn -= 1 if item.quality >= 50 { return } item.quality += 1 if item.sellIn <= 0 { item.quality += 1 } return package items type BackstagePass struct { Params } func NewBackstagePass(params Params) Item { return &BackstagePass{params} } func (item *BackstagePass) Name() string { return item.name } func (item *BackstagePass) SellIn() int { return item.sellIn } func (item *BackstagePass) Quality() int { return item.quality } func (item *BackstagePass) Update() { item.sellIn -= 1 if item.quality >= 50 { return } item.quality += 1 if item.sellIn < 10 { item.quality += 1 } if item.sellIn < 5 { item.quality += 1 } if item.sellIn < 0 {
  178. GO NORTHWEST 2018 #! /usr/local/bin/ruby class Default attr_reader :name, sellIn,

    :quality def initialize name, sellIn, quality @name, @sellIn, @quality = name, sellIn, quality end def update end end class Brie < Default def update ... end end class Normal < Default def update ... end end class BackstagePass < Default def update ... end end
  179. INHERITANCE USING EMBEDDING

  180. GO NORTHWEST 2018 package items type Item interface { Name()

    string SellIn() int Quality() int Update() } type Params struct { name string sellIn, quality int } ...
  181. GO NORTHWEST 2018 package items type Default struct { name

    string sellIn, quality int } func NewDefault(name string, sellIn, quality int) Item { return &Default{name, sellIn, quality} } ...
  182. GO NORTHWEST 2018 package items type Normal struct { Params

    } func NewNormal(params Params) Item { return &Normal{params} } func (item *Normal) Name() string { return "Normal" } func (item *Normal) SellIn() int { return item.sellIn } func (item *Normal) Quality() int { return item.quality } func (item *Normal) Update() { item.sellIn -= 1 if item.quality <= 0 { return } item.quality -= 1 if item.sellIn <= 0 { item.quality -= 1 } return }
  183. GO NORTHWEST 2018 package items type Normal struct { Default

    } func NewNormal(params Params) Item { return &Normal{params} } func (item *Normal) Name() string { return "Normal" } func (item *Normal) SellIn() int { return item.sellIn } func (item *Normal) Quality() int { return item.quality } func (item *Normal) Update() { item.sellIn -= 1 if item.quality <= 0 { return } item.quality -= 1 if item.sellIn <= 0 { item.quality -= 1 } return }
  184. GO NORTHWEST 2018 package items type Normal struct { Default

    } func NewNormal(params Params) Item { return &Normal{params} } func (item *Normal) Name() string { return "Normal" } func (item *Normal) SellIn() int { return item.sellIn } func (item *Normal) Quality() int { return item.quality } func (item *Normal) Update() { item.sellIn -= 1 if item.quality <= 0 { return } item.quality -= 1 if item.sellIn <= 0 { item.quality -= 1 } return }
  185. GO NORTHWEST 2018 package items type Normal struct { Default

    } func NewNormal(params Params) Item { return &Normal{params} } func (item *Normal) Update() { item.sellIn -= 1 if item.quality <= 0 { return } item.quality -= 1 if item.sellIn <= 0 { item.quality -= 1 } return }
  186. GO NORTHWEST 2018 package items type Normal struct { Default

    } func NewNormal(params Params) Item { return &Normal{params} } func (item *Normal) Update() { item.sellIn -= 1 if item.quality <= 0 { return } item.quality -= 1 if item.sellIn <= 0 { item.quality -= 1 } return }
  187. GO NORTHWEST 2018 package items type Normal struct { Default

    } func NewNormal(params Params) Item { return &Normal{params} } func (item *Normal) Update() { item.sellIn -= 1 if item.quality <= 0 { return } item.quality -= 1 if item.sellIn <= 0 { item.quality -= 1 } return }
  188. GO NORTHWEST 2018 package items type Normal struct { Default

    } func NewNormal(name string, sellIn, quality int) Item { return &Normal{Default{name, sellIn, quality}} } func (item *Normal) Update() { item.sellIn -= 1 if item.quality <= 0 { return } item.quality -= 1 if item.sellIn <= 0 { item.quality -= 1 } return }
  189. GO NORTHWEST 2018 package items type Item interface { Name()

    string SellIn() int Quality() int Update() } type Params struct { name string sellIn, quality int } type Factory func(Params) Item var Factories = map[string]Factory{ "Normal": NewNormal, "Aged Brie": NewBrie, "Backstage passes to a TAFKAL80ETC concert": NewBackstagePass, } ...
  190. GO NORTHWEST 2018 package items type Item interface { Name()

    string SellIn() int Quality() int Update() } type Factory func(Params) Item var Factories = map[string]Factory{ "Normal": NewNormal, "Aged Brie": NewBrie, "Backstage passes to a TAFKAL80ETC concert": NewBackstagePass, } ...
  191. GO NORTHWEST 2018 package items type Item interface { Name()

    string SellIn() int Quality() int Update() } type Factory func(Params) Item var Factories = map[string]Factory{ "Normal": NewNormal, "Aged Brie": NewBrie, "Backstage passes to a TAFKAL80ETC concert": NewBackstagePass, } func ItemFactory(name string, sellIn, quality int) Item { params := Params{name, sellIn, quality} itemFactory, ok := Factories[name] if !ok { itemFactory = NewDefault } item := itemFactory(params) return item }
  192. GO NORTHWEST 2018 package items type Item interface { Name()

    string SellIn() int Quality() int Update() } type Factory func(Params) Item var Factories = map[string]Factory{ "Normal": NewNormal, "Aged Brie": NewBrie, "Backstage passes to a TAFKAL80ETC concert": NewBackstagePass, } func ItemFactory(name string, sellIn, quality int) Item { params := Params{name, sellIn, quality} itemFactory, ok := Factories[name] if !ok { itemFactory = NewDefault } item := itemFactory(params) return item }
  193. GO NORTHWEST 2018 package items type Item interface { Name()

    string SellIn() int Quality() int Update() } type Factory func(Params) Item var Factories = map[string]Factory{ "Normal": NewNormal, "Aged Brie": NewBrie, "Backstage passes to a TAFKAL80ETC concert": NewBackstagePass, } func ItemFactory(name string, sellIn, quality int) Item { itemFactory, ok := Factories[name] if !ok { itemFactory = NewDefault } item := itemFactory(name, sellIn, quality) return item }
  194. FINALLY…

  195. …ADDING THE NEW FEATURE…

  196. GO NORTHWEST 2018 package main var inventory = []items.Item{ ItemFactory("+5

    Dexterity Vest", 10, 20), ItemFactory("Aged Brie", 2, 0), ItemFactory("Chunky Bacon", 5, 7), ItemFactory("Sulfuras, Hand of Ragnaros", 0, 80), ItemFactory("Backstage passes to a TAFKAL80ETC concert", 15, 20), ItemFactory("Conjured Mana Cake", 3, 6), } func main() { GildedRose() } func GildedRose() { for i := 0; i < len(inventory); i++ { item := inventory[i] item.Update() } } DON’T HAVE TO CHANGE ANYTHING!
  197. GO NORTHWEST 2018 package items type ConjuredMana struct { }

  198. GO NORTHWEST 2018 package items type ConjuredMana struct { Default

    }
  199. GO NORTHWEST 2018 package items type ConjuredMana struct { Default

    } // Add factory func NewConjuredMana(name string, sellIn, quality int) Item { return &ConjuredMana{Default{name, sellIn, quality}} }
  200. GO NORTHWEST 2018 package items type ConjuredMana struct { Default

    } func NewConjuredMana(name string, sellIn, quality int) Item { return &ConjuredMana{Default{name, sellIn, quality}} } func (item *ConjuredMana) Update() { item.sellIn -= 1 if item.quality <= 0 { return } item.quality -= 2 if item.sellIn <= 0 { item.quality -= 2 } return }
  201. GO NORTHWEST 2018 package items type Item interface { Name()

    string SellIn() int Quality() int Update() } type Factory func(Params) Item var Factories = map[string]Factory{ "Normal": NewNormal, "Aged Brie": NewBrie, "Backstage passes to a TAFKAL80ETC concert": NewBackstagePass, } func ItemFactory(name string, sellIn, quality int) Item { itemFactory, ok := Factories[name] if !ok { itemFactory = NewDefault } item := itemFactory(name, sellIn, quality) return item }
  202. GO NORTHWEST 2018 package items type Item interface { Name()

    string SellIn() int Quality() int Update() } type Factory func(Params) Item var Factories = map[string]Factory{ "Normal": NewNormal, "Aged Brie": NewBrie, "Backstage passes to a TAFKAL80ETC concert": NewBackstagePass, "Conjured Mana Cake": NewConjuredMana, } func ItemFactory(name string, sellIn, quality int) Item { itemFactory, ok := Factories[name] if !ok { itemFactory = NewDefault } item := itemFactory(name, sellIn, quality) return item }
  203. GO NORTHWEST 2018 === RUN TestConjuredMana_BeforeSellDate --- PASS: TestConjuredMana_BeforeSellDate (0.00s)

    === RUN TestConjuredMana_BeforeSellDate_WithMinQuality --- PASS: TestConjuredMana_BeforeSellDate_WithMinQuality (0.00s) === RUN TestConjuredMana_OnSellDate --- PASS: TestConjuredMana_OnSellDate (0.00s) === RUN TestConjuredMana_OnSellDate_WithMinQuality --- PASS: TestConjuredMana_OnSellDate_WithMinQuality (0.00s) === RUN TestConjuredMana_AfterSellDate --- PASS: TestConjuredMana_AfterSellDate (0.00s) === RUN TestConjuredMana_AfterSellDate_WithMinQuality --- PASS: TestConjuredMana_AfterSellDate_WithMinQuality (0.00s) PASS ok gilded_rose 0.006s "
  204. WHAT HAVE WE LEARNED?

  205. OBJECT-ORIENTATION IS POSSIBLE IN GO

  206. GO NORTHWEST 2018 OBJECT-ORIENTATION IN GO ▸ Go structs can

    be conceptualized as a set of responsibilities ▸ Inheritance is possible through the use of interfaces and embedding ▸ Interfaces represent the specification of the object ▸ Embedding represents a portable encapsulation of a set of data/ behavior ▸ Namespacing can be achieved via packages
  207. " "

  208. OBJECT ORIENTATION IS ONE TOOL THAT CAN BE USED TO

    MAKE CODE MORE MAINTAINABLE
  209. “GIVE ME SIX HOURS TO CHOP DOWN A TREE, AND

    I WILL SPEND THE FIRST FOUR SHARPENING THE AXE.” — Abraham Lincoln
  210. “GIVE A DEVELOPER SIX HOURS TO CHOP DOWN A TREE,

    AND THEY WILL SPEND THE FIRST TWELVE SHARPENING THE AXE.” — also Abraham Lincoln
  211. “MOVE FAST AND BREAK THINGS” — Capitalism

  212. DEVELOPERS WANT TO MOVE FAST

  213. BUT WE DON’T WANT TO BREAK THINGS

  214. FALLACY: PAYING DOWN TECH DEBT IS THE OPPOSITE OF MOVING

    FAST
  215. GOOD ENGINEERS INVEST IN MAINTAINABLE CODE IN ORDER TO MOVE

    FAST
  216. DEVELOPER HAPPINESS "

  217. GO NORTHWEST 2018 THANK YOU! Eric Hodel (@drbrain) Sam Karp

    (@samuelkarp) Allison Stanko (@allisaurus) Wesley Pettit (@pettitwesley) Elliot Isaacson Kate Fulton Lito Nicolai
  218. GO NORTHWEST 2018 RESOURCES ▸ Design Patterns: Elements of Object-Oriented

    Software, by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. ▸ Design Patterns Explained: A New Perspective on Object-Oriented Design, by Alan Shalloway and James R. Trott ▸ All the Little Things, Sandi Metz: https://confreaks.tv/videos/ railsconf2014-all-the-little-things ▸ https://xkcd.com/1428/
  219. HSING-HUI HSU @SOMANYHS