Decorator design provides flexible alternatives for sub-classing when inheritance is unfeasible like causing diamond dependency problem or massive code replications. This is commonly seen in object-oriented programming when a class is big and the algorithm variables/customization is small.
Decorator design pattern is something as such:
The golden rule for decorator design pattern is that the decorator function is always returning the same component type. They only perform enhancement/degradation alteration to the output of the component, before returning the final output to the client.
Inheritance has a limitation when the class is being inherited for multiple degree of orders. These are the common problems:
If a main class has more than 2 degree of order inheritance, inheriting/copying the main class is a massive memory wastage and code replications. Say if want to polymorph a function inside the main class, inheriting main class to create a new class types creates massive duplication over the sub-classes just to modify a function.
If we arrange the inheritance by multiple inheritance technique, we will get into the diamond inheritance problem easily.
Here is an example of an RPG gun effects in Go.
package mainimport ( "fmt")// ACOG is a decorator objectstype ACOG struct { increaseHitRate int}func (a *ACOG) aim() int { return a.increaseHitRate}// LRDS is a decoractor objectstype LRDS struct { increaseHitRate int}func (a *LRDS) aim() int { return a.increaseHitRate}// Enhancement is a decorator abstract interfacetype Enhancement interface { aim() int}// core weapontype M16 struct { hitRate int enhancement Enhancement}func (w *M16) fire() int { if w.enhancement != nil { return w.hitRate + w.enhancement.aim() } return w.hitRate}func main() { acog := &ACOG{increaseHitRate: 40} lrds := &LRDS{increaseHitRate: 20} m16 := &M16{hitRate: 50} m16.enhancement = acog fmt.Printf("M16 with ACOG: %v\n", m16.fire()) m16.enhancement = lrds fmt.Printf("M16 with LRDS: %v\n", m16.fire()) m16.enhancement = nil fmt.Printf("M16 without enhancement: %v\n", m16.fire())}// Output:// M16 with ACOG: 90// M16 with LRDS: 70// M16 without enhancement: 50Unlike other languages, Go does not have "class" concept. Hence, the enhancement is inside the core weapon using the interface type. the m16 object is able to equip any enhancement decorator objects that influences the fire() function.
Another good example would be function modifiers in Go:
package mainimport ( "fmt")// functionstype a struct { statement string}// decorator interfacetype pkg struct {}func (p *pkg) prefix(prefix string, statement string) string { return prefix + statement}func (p *pkg) suffix(suffix string, statement string) string { return statement + suffix}func main() { p := &pkg{} s := &a{statement: "Hello World"} fmt.Printf("pure : %v\n", s.statement) fmt.Printf("prefix: %v\n", p.prefix("James, ", s.statement)) fmt.Printf("suffix: %v\n", p.suffix(", James.", s.statement))}// Output:// pure : Hello World// prefix: James, Hello World// suffix: Hello World, James.Notice that the pkg structure is a set of decorator functions for string object. In this example, the pkg structure prepend and append the original s.statement string without altering s object.
That's all about decorator design pattern.