Having ice creams (popsicle, cone) and curious about how good it will be with extra flavor. For the most authentic experience, you should dip your ice creams in each flavor.
Decorator is a structural design pattern:
🖤🖤🖤🖤🤍
Don’t be confused! Decorator and Function Decorator have no relationship at all. However, In Python when people talk about decorator, mostly they are talking about Function Decorator.
What is Closure?
Closure is a technique of combining the callables (usually 2 functions) together prepare_recipe, updated_func that:
The variables layer_3, layer_2, layer_1 need to be stored for later usage in a specific scope. It will be passed to the outer/enclosing/wrapper function.
The outer/enclosing/wrapper function prepare_recipe stores variables. The inner/nested function will use the variables later.
The inner/nested function updated_func can access the variables thanks to the executed outer/enclosing/wrapper function.
Similar to Closure’s technique, we also have Function Decorator
What is Function Decorator?
Function Decorator is a technique of combining the callables (usually 2 functions) together prepare_recipe, updated_func that:
The origin/main function decorate_ice_cream is deprecated and needs to be updated a bit. It will be passed to the outer/enclosing/wrapper function.
The outer/enclosing/wrapper function prepare_recipe stores origin/main function. The inner/nested function will use the origin/main function later (Closure’s usage).
The inner/nested function updated_func will:
update behaviors around the origin/main function:
modify the input before giving the input to the origin/main function:
then return the new version of the origin/main function.
What is Decorator?
Decorator help to add/update the object PopsicleIceCream dynamically by placing them inside the decorators ChocolateDecorator, CondensedMilkDecorator, AlmondDecorator.
It’s like a Function Decorator, but here it applies to Class Instance.
Why use Closure?
Closure creates an environment to remember simple values layer_3, layer_2, layer_1, it’s like the class’s usage!
Do you know functools.partial? functools.partial function is also an example of Closure’s usage.
Question:Class is the alternative to Closure, then when should we use Closure in lieu of Class?
Answer:For simplicity, you should use Class for all cases. Closure is just a technique in theory, it’s probably only mentioned a lot in pointless interviews. I’m still bitter about that interview -_-
Why use Function Decorator?
It helps reduce code duplication by wrapping common code into a bundle def prepare_recipe, then simply adding @prepare_recipe above the concrete functions. This makes the function’s code shorter and easier to update/refactor.
Question:What are the alternatives if I’m too lazy to create the Function Decorator?
Answer:You can make a long code with duplication or a little better solution is to split the big function into small functions, the choice is yours! However, let’s reconsider using Function Decorator! Business before pleasure!
Why use Decorator?
Easily transform the original popsicle object into a almond_condensed_milk_chocolate_popsicle multiple features object dynamically, without affecting anything to the origin.
Question:I can easily add more features to an object without using Decorator, right?
Answer:Of course, but Decorator is lovely in its own way because it keeps the code clean by wrapping an popsicle object in new chocolate_popsicle objects and repeating that condensed_milk_chocolate_popsicle, almond_condensed_milk_chocolate_popsicle process endlessly without touching the old code!
When to use Closure?
Question:When should I use Closure?
Answer:When it’s necessary to create an instance in order to store values layer_3, layer_2, layer_1 and use decorate_ice_cream those values later
Input:
1
2
3
4
5
6
7
8
9
10
11
12
13
popsicle_ice_cream="PopsicleIceCream"cone_ice_cream="IceCreamCone"print("[original ice creams] ")print(popsicle_ice_cream)print(cone_ice_cream)# main functiondefdecorate_ice_cream(layer_3,layer_2,layer_1,ice_cream):magic_number=9layer_2_a,layer_2_b=layer_2[:magic_number],layer_2[magic_number:]decorated_ice_cream=f"{layer_3}({layer_2_a}({layer_1}({ice_cream})){layer_2_b})"returndecorated_ice_cream
Answer:When the environment values, input values, output values surrounding the main/deprecated function needs to be updated before and after execution.
Input:
1
2
3
4
5
6
7
8
9
10
popsicle_ice_cream="PopsicleIceCream"cone_ice_cream="IceCreamCone"print("[original ice creams] ")print(popsicle_ice_cream)print(cone_ice_cream)# main functiondefdecorate_ice_cream(ice_cream):returnice_cream
# decorator with paramsdefprepare_recipe_with_params(layer_3,layer_2,layer_1):defprepare_recipe(func):defupdated_func(ice_cream):magic_number=9layer_2_a,layer_2_b=layer_2[:magic_number],layer_2[magic_number:]front_ice_cream=f"{layer_3}({layer_2_a}({layer_1}("back_ice_cream=f")){layer_2_b})"half_decorated_ice_cream=func(front_ice_cream+ice_cream)decorated_ice_cream=half_decorated_ice_cream+back_ice_creamreturndecorated_ice_creamreturnupdated_funcreturnprepare_recipe@prepare_recipe_with_params("Almond","CondensedMilk","Chocolate")# main functiondefdecorate_ice_cream(ice_cream):returnice_creamif__name__=="__main__":almond_condensed_milk_chocolate_popsicle=decorate_ice_cream(popsicle_ice_cream)almond_condensed_milk_chocolate_cone=decorate_ice_cream(cone_ice_cream)print("\n[decorated ice creams] ")print(almond_condensed_milk_chocolate_popsicle)print(almond_condensed_milk_chocolate_cone)
classDecorator(IceCream):_component=Nonedef__init__(self,component):print(f"{type(self)} is decorating the {type(component)}")self._component=component@propertydefcomponent(self):returnself._componentdeftaste(self):returnself._component.taste()classChocolateDecorator(Decorator):deftaste(self):returnf"Chocolate({self.component.taste()})"classCondensedMilkDecorator(Decorator):deftaste(self):returnf"Condensed({self.component.taste()})Milk"classAlmondDecorator(Decorator):deftaste(self):returnf"Almond({self.component.taste()})"if__name__=="__main__":chocolate_popsicle=ChocolateDecorator(popsicle)chocolate_cone=ChocolateDecorator(cone)condensed_milk_chocolate_popsicle=CondensedMilkDecorator(chocolate_popsicle)condensed_milk_chocolate_cone=CondensedMilkDecorator(chocolate_cone)almond_condensed_milk_chocolate_popsicle=AlmondDecorator(condensed_milk_chocolate_popsicle)almond_condensed_milk_chocolate_cone=AlmondDecorator(condensed_milk_chocolate_cone)print("\n[decorated ice creams] ")print(almond_condensed_milk_chocolate_popsicle.taste())print(almond_condensed_milk_chocolate_cone.taste())