Skip to content

Instantly share code, notes, and snippets.

@kencoba
Created February 21, 2012 11:27
Show Gist options
  • Select an option

  • Save kencoba/1875983 to your computer and use it in GitHub Desktop.

Select an option

Save kencoba/1875983 to your computer and use it in GitHub Desktop.
Decorator pattern (Design Patterns in Scala)
// http://en.wikipedia.org/wiki/Decorator_pattern
trait Coffee {
def cost:Double
def ingredients: String
}
class SimpleCoffee extends Coffee {
override def cost = 1
override def ingredients = "Coffee"
}
abstract class CoffeeDecorator(decoratedCoffee: Coffee) extends Coffee {
val sep = ", "
override def cost = decoratedCoffee.cost
override def ingredients = decoratedCoffee.ingredients
}
class Milk(decoratedCoffee: Coffee) extends CoffeeDecorator(decoratedCoffee) {
override def cost = super.cost + 0.5
override def ingredients = super.ingredients + sep + "Milk"
}
class Whip(decoratedCoffee: Coffee) extends CoffeeDecorator(decoratedCoffee) {
override def cost = super.cost + 0.7
override def ingredients = super.ingredients + sep + "Whip"
}
class Sprinkles(decoratedCoffee: Coffee) extends CoffeeDecorator(decoratedCoffee) {
override def cost = super.cost + 0.2
override def ingredients = super.ingredients + sep + "Sprinkles"
}
object DecoratorSample {
def main(args: Array[String]) = {
var c:Coffee = new SimpleCoffee
printf("Cost: %f Ingredients %s\n", c.cost, c.ingredients)
c = new Milk(c)
printf("Cost: %f Ingredients %s\n", c.cost, c.ingredients)
c = new Sprinkles(c)
printf("Cost: %f Ingredients %s\n", c.cost, c.ingredients)
c = new Whip(c)
printf("Cost: %f Ingredients %s\n", c.cost, c.ingredients)
c = new Sprinkles(c)
printf("Cost: %f Ingredients %s\n", c.cost, c.ingredients)
}
}
DecoratorSample.main(Array())
@EddieJamsession

Copy link
Copy Markdown

Sometimes you want to implement decorator in this way:

class Coffee {
  val sep = ", "
  def cost:Double  = 1
  def ingredients: String = "Coffee"
}

trait Milk extends Coffee {
  abstract override def cost = super.cost + 0.5
  abstract override def ingredients = super.ingredients + sep + "Milk"
}

trait Whip extends Coffee {
  abstract override def cost = super.cost + 0.7
  abstract override def ingredients = super.ingredients + sep + "Whip"
}

trait Sprinkles extends Coffee {
  abstract override def cost = super.cost + 0.2
  abstract override def ingredients = super.ingredients + sep + "Sprinkles"
}

object DecoratorSample {
  def main(args: Array[String]) {
    var c: Coffee = new Coffee with Sprinkles
    printf("Cost: %f Ingredients %s\n", c.cost, c.ingredients)
    c = new Coffee with Sprinkles with Milk
    printf("Cost: %f Ingredients %s\n", c.cost, c.ingredients)
    c = new Coffee with Sprinkles with Milk with Whip
    printf("Cost: %f Ingredients %s\n", c.cost, c.ingredients)
  }
}

DecoratorSample.main(Array())

@ryantheleach

Copy link
Copy Markdown

@EddieJamsession If the decorated coffee can't be modified at runtime it doesn't really fit the decorator pattern.

@Ehsan1997

Ehsan1997 commented Nov 8, 2017

Copy link
Copy Markdown

@EddieJamsession, You should remove this comment, as it is misleading. You are clearly violating ISP.

@jeffxu71

Copy link
Copy Markdown

Can I buy a coffee with milk and sprinkle in the main()?

@afrat

afrat commented Jul 13, 2018

Copy link
Copy Markdown

@Ehsan1997 Can't see your point about violating ISP. ISP says that no implementation should depend on unecessary functionality this ist clearly not the case by using mixin compositions in this scenario.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment