Gothic Online Forums

Full Version: Decorator [Design Pattern]
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Decorator


Hi, have you ever heard about the decorator design pattern? If not, just read this topic, and hopefully you'll learn the basic concept of this powerful abstract idea.

The basic concept





In object-oriented programming, the decorator pattern is a design pattern that allows behavior to be added to an individual object, dynamically, without affecting the behavior of other objects from the same class.
The decorator pattern is often useful for adhering to the Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern.


source: wikipedia.org

Practical example



Basically the decorator pattern allows us to compose the object from multiple classes which inherits from the Decorator class. Let's just say, that we want to create a simple class called Pizza. We also want to make more types of pizza, to do that we could use the inheritance.

Squirrel Script
  1. class Pizza
  2. {
  3. function getDescription()
  4. {
  5. return "Thin Dough"
  6. }
  7.  
  8. function getCost()
  9. {
  10. return 4.0
  11. }
  12. }
  13.  
  14. class MozzarellaPizza extends Pizza
  15. {
  16. function getDescription()
  17. {
  18. return base.getDescription() + ", Mozzarella"
  19. }
  20.  
  21. function getCost()
  22. {
  23. return 4.5
  24. }
  25. }
  26.  
  27. class MozzarellaPizzaWithTomatoSouce extends MozzarellaPizza
  28. {
  29. function getDescription()
  30. {
  31. return base.getDescription() + ", Tomato Souce"
  32. }
  33.  
  34. function getCost()
  35. {
  36. return base.getCost() + 0.5
  37. }
  38. }



This silly example demonstrates the basic problem, if we use the inheritance, we will end up with many types of classes, and we can't easilly combine our own pizza object from the ingredients.
So, instead of making multiple class inheritance, we will try to use the Decorator pattern in practice. The pattern requires from us to implement the class called Decorator which will be holding a reference to the pizza object, or any object,
we will make this class to be universal, that means that it could be used in different scenarios, not only pizza example.

Squirrel Script
  1. class Decorator
  2. {
  3. // making a object reference, this is needed by the pattern itself
  4. obj = null
  5.  
  6. constructor(obj)
  7. {
  8. // assigning the passed object reference to the field
  9. this.obj = obj
  10. }
  11.  
  12. // this is a _get metamethod which gets called when we try to access the field/method which doesn't exists
  13. // using it, we can return a field/method from the object reference
  14. // that's why we needed the reference
  15. function _get(idx)
  16. {
  17. // if object doesn't contain the specified index -> throw an error
  18. if (!(idx in obj))
  19. throw null
  20.  
  21. // else -> return the content of the obj[idx]
  22. return obj[idx]
  23. }
  24.  
  25. // this is a _set metamethod, it works almost the same as _get, but it get's called
  26. // when we try to assigning something to a field/method which doesn't exists
  27. // this can be useful when we would like to change the field of the ingredient class directly
  28. function _set(idx, value)
  29. {
  30. // if object doesn't contain the specified index -> throw an error
  31. if (!(idx in obj))
  32. throw null
  33.  
  34. // else -> assign the value to the obj[idx]
  35. obj[idx] = value
  36. }
  37. }
  38.  
  39. class Pizza
  40. {
  41. function getDescription()
  42. {
  43. return "Thin Dough"
  44. }
  45.  
  46. function getCost()
  47. {
  48. return 4.0
  49. }
  50. }
  51.  
  52. class Mozzarella extends Decorator
  53. {
  54. constructor(obj)
  55. {
  56. // calling the Decorator constructor
  57. base.constructor(obj)
  58. }
  59.  
  60. function getDescription()
  61. {
  62. return obj.getDescription() + ", Mozzarella"
  63. }
  64.  
  65. function getCost()
  66. {
  67. return obj.getCost() + 0.4
  68. }
  69. }
  70.  
  71. class TomatoSouce extends Decorator
  72. {
  73. constructor(obj)
  74. {
  75. // calling the Decorator constructor
  76. base.constructor(obj)
  77. }
  78.  
  79. function getDescription()
  80. {
  81. return obj.getDescription() + ", Tomato Souce"
  82. }
  83.  
  84. function getCost()
  85. {
  86. return obj.getCost() + 0.5
  87. }
  88. }
  89.  
  90. addEventHandler("onInit", function()
  91. {
  92. local pizza = TomatoSouce(Mozzarella(Pizza))
  93.  
  94. print(pizza.getDescription()) // outputs: Thin Dough, Mozzarella, Tomato Souce
  95. print(pizza.getCost()) // outputs: 4.9
  96. })



As you can see, the idea behind the pattern is really simple. The Decorator class holds a reference to the object, using this we can make multiple classes which will inherit from the Decorator and then make the object from the specified classes.
This gives you a lot more flexibility, because you can compose the object from many classes that inherits from the Decorator. In case of usage in G2O, think about the NPC AI written with the usage of this pattern, you could compose the AI from small classes which would implement only partial functionallity.

The code is based on this video:



Good Job!