Problems to be solved

Consider a coffee shop charging problem: how to achieve flexible coffee pricing. Coffee shop sells coffee, but later in order to meet the different tastes of different customers, just pure coffee, seems too drab, will consider increasing the different collocation, the collocation of different ingredients to another drink, in this way, the variety, but the problem came also, then how to different varieties of new calculate new price. There are two options:

First: you can inherit pure coffee as a base class, and then create a subclass that can rewrite the pricing method as a single variety and add other functions to that variety. But: inheritance has a big problem is that such a scheme implementation is first of all, you know what are varieties, only send produce various subclasses, but, if the subsequent to an existing varieties or add some contents, removing some even directly delete this breed, it will be very trouble always change the corresponding class, There is also the disadvantage of producing many seed classes. If there are many varieties, and the differences between each variety are small, it can be troublesome to separate them as one class.

The second is to use the theme decorator model of the article we are going to talk about today. Let’s think of an example in our daily life: a paper photo. If we want to keep it longer, we can mould the photo first. Once you’ve moulded it, if that’s not enough, you might even frame the photo. If a frame doesn’t protect the photo, add a glass cover. In this example, we can understand that the photograph itself is the object to be decorated, with plastic sealant, picture frame and glass cover all acting as decorators. Decorators on each level do not modify the most decorated object inside. Here we can consider the specific coffee drink as the decorator, the food or drink to be added as the decorator, and each coffee drink can be decorated by a different decorator.

The problem of calculating the price of different coffee drinks mentioned above is how to transparently add functions to an object and realize dynamic combination of functions in programming terms. That’s what decorator mode does.

The schema definition

The decorator pattern allows you to dynamically add functionality to objects, adding functionality transparently from outside an object. Adding functionality to an object transparently means adding functionality to an object without letting the object know about it, that is, without modifying it

Each decorator can be decorated by multiple decorators. For example, black coffee (decorator) can be decorated with milk (decorator), fruit (decorator), and there is no order between the different decorators.

The specific implementation

Decorator and decorator need to inherit from the same class or implement the same interface as the decorator, and then, in the specific decorator implementation, transfer the decorator object.

Below is a UML diagram of the implementation and a hierarchy of calls between classes.

  • CoffeeComponent: Coffee base class (can also be interface/protocol)
  • BlackCoffee: The concrete coffee is the object to be decorated.
  • CondimentDecorator: Base class of the ingredient (decorator’s base class) and needs to inherit from the decorator’s base classCoffeeComponentAnd hold oneCoffeeComponentType property.
  • MilkDecorator: milk decorator, concrete decorator object.

As can be seen from the hierarchy diagram above, layers of decorators are wrapped layer by layer outside the decorated object, and function methods are called layer by layer recursively calling the decorated object. As can be seen from the figure, when the black coffee is decorated with milk, the milk decorator becomes the new decorator and can be decorated by subsequent decorators, and there is no order requirement between each decorator. The order can be done in any way you want.

/ / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * coffee components (base class) * * * * * * * * * * * * * * * * * * * * * @ interface CoffeeComponent: NSObject - (double)getPrice; @end@implementation CoffeeComponent - (double)getPrice {return0.f; } @ the end / / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * black coffee (coffee) * * * * * * * * * * * * * * * * * * * * * @ interface BlackCoffee: CoffeeComponent// Concrete component inherited from abstract component - (double)getPrice; @end @implementation BlackCoffee - (double)getPrice {return5; } @ the end / / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * decorator base class * * * * * * * * * * * * * * * * * * * * * @ interface CondimentDecorator: CoffeeComponent// decorator abstract class inherited from component - (instancetype)initWithComponent:(CoffeeComponent *)component; @property (nonatomic,strong)CoffeeComponent *component; @end @implementation CondimentDecorator - (instancetype)initWithComponent:(CoffeeComponent *)component {if (self = [super init]) {
        _component = component;
    }
    returnself; } @ the end / / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * milk decorator (specific decorator) * * * * * * * * * * * * * * * * * * * * * @ interface MilkDecorator: CondimentDecorator// Concrete decorator inherited from abstract CondimentDecorator @end@implementation MilkDecorator - (double)getPrice {NSLog(@"Two dollars more milk.");
    return2 + [self.component getPrice]; } @end // Omit the other decorators' code here, just like the code for the milk decorator. The code can be viewed in the demo. / / = = = = = = = = = = = = = = = = = = = = = = = = = external call = = = = = = = = = = = = = = = = = = = = = / / pure coffee BlackCoffee * BlackCoffee = [[BlackCoffee alloc] init]; / / white MilkDecorator * MilkDecorator = [[MilkDecorator alloc] initWithComponent: blackCoffee]; / / add soymilk SoyDecorator * SoyDecorator = [[SoyDecorator alloc] initWithComponent: milkDecorator]; / / add fruit FruitDecorator * FruitDecorator = [[FruitDecorator alloc] initWithComponent: soyDecorator]; NSLog(@"How much is it altogether % F?",[fruitDecorator getPrice]);

Copy the code

conclusion

  • The decorator pattern is more flexible than inheritance: inheritance is static and inheriting subclasses have the same functionality as the base class, but the decorator pattern can split functionality into different decorators and dynamically choose what functionality you want.
  • The nature of the decorator pattern is dynamic composition: dynamic combinations of decorators can add functionality transparently to decorated objects.
  • Decorator mode not only adds functionality, but also fully enables access to new functionality and control functionality. You can control when the decorator calls the decorator object function.
  • Disadvantages of decorator pattern: Fine grained objects are produced. If a series of complex functions are subdivided into different decorators, many fine grained objects are produced.

The above is the author’s own reading notes, if there is any misunderstanding, please point out. Thank you very much!

The Demo address


Reference acknowledgments:

Grinding Design Patterns

Head First Design Patterns