<jupyter_start><jupyter_text>Python Design Patterns              Classification:+ [Creational](Creational)+ [Structural](Structural)+ [Behavioral](Behavioral)  CreationalCreational patterns provide various object creation mechanisms, which increase flexibility and reuse of existing code.- All these patterns serves the purpose of object creation, thus classified as creational design pattern.- All these hides the instantiation logic. In other words, encapsulates object creation.- All these have a factory class responsible for creating new objects.Ref. - [Saurav-Satphathy's blog](https://medium.com/bitmountn/factory-vs-factory-method-vs-abstract-factory-c3adaeb5ac9a) Creational Design Patterns (covered in this):1. [Factory](Factory)2. [Abstract Factory](Abstract-Factory)3. [Builder](Builder)4. [Director](Director)5. [Prototype](Prototype)6. [Singleton](Singleton)7. [Borg](Borg)  Factory A Factory is a method for creating other objects.It allows an interface or a class to create an object, which can further be decided based on arguments or already specified configuration. This is used to separate the business logic with a certain group of classes that belong to a pattern and can be scaled or reduced in future. Let's understand this with an example:For applications which cater to the users with understanding of different languages.<jupyter_code>class HindiLanguage:
    """
    This will return the hindi version.
    """
    def __init__(self):
        self.bag_of_words = {"orange": "narangi", "car": "gadi"} 
    
    def convert(self, msg):
        """
        msg: list of words
        return: list of words converted in the language.
        """
        converted_list = []
        for word in msg:
            converted_list.append(self.bag_of_words.get(word, word))
        return converted_list
class EnglishLanguage:
    """
    This will return the same message.
    """
    def convert(self, msg):
        return msg
# Factory Method 
def LanguageModelSelector(language="Hindi"):
    """
    This method to select the language model based on argument.
    To add a new language, create its model class and add in the below dictionary.
    """
    language_models = {
        "English": EnglishLanguage,
        "Hindi": HindiLanguage
    }
    return language_models.get(language)()
hindi = LanguageModelSelector('Hindi')
hindi.convert(["car", "orange", "hindi"])<jupyter_output><empty_output><jupyter_text>Abstract FactoryThis allows to produce the families of related objects without specifying their concrete classes.The idea is to abstract the creation of objects depending on businesslogic, platform choice, etc.<jupyter_code>import random

class LearningMaterialFactory:
    """
    A learning material abstract factory
    """
    
    def __init__(self, learning_material):
        self.factory = learning_material
        
    def time_required(self):
        resource = self.factory()
        print("The time required for completion of ", resource.name,
              "will be: ", resource.time_factor*resource.length)
        
class Video:
    time_factor = 10
    length = 50
    name = "Video"
        
class Book:
    time_factor = 5
    length = 200
    name = "Book"
        
def choose_random():
    return random.choice([Video, Book])
material = LearningMaterialFactory(choose_random())
material.time_required()<jupyter_output>The time required for completion of  Book will be:  1000<jupyter_text>BuilderBuilder helps in constructing complex objects step by step. The pattern allows to produce different types and representations of an object using the same construction code. Let's consider a problem where we have to create a House object. To build a simple house, you need to construct four walls and a floor, install a door, fit a pair of windows, and build a roof. But what if you want a bigger house with a swimming pool, garage, statues, garden, etc.One solution to this problem is to create a base class House with all attributes possible shown below.The problem with this is in most cases the parameters will be unused, making the constructor calls ugly. ![image.png](attachment:image.png)[Ref.](https://refactoring.guru/design-patterns/builder) The builder solution:<jupyter_code>class House():
    def __init__(self, floors, doors, windows):
        self.floors = floors
        self.doors = doors
        self.windows = windows
    def add_floor():
        self.floor +=1
        return True
    
class ComplexHouse(House):
    """ Builder Class """
    
    def __init__(self, floors, doors, windows):
        super().__init__(floors, doors, windows)
        pass
    
    def build_swimming_pool(self):
        pass
    def build_garage(self):
        pass
    
def builder_villa(cls, *args):
    """ Builder Method """
    
    house = cls(*args)
    house.build_swimming_pool()
    house.build_garage()
    return house

comp_house = ComplexHouse
completed_house = builder_villa(comp_house, 1,10,20)<jupyter_output><empty_output><jupyter_text>DirectorThe director class defines the order in which to execute the building steps, while the builder provides the implementation for those steps.<jupyter_code>class Director():
    def __init__(self, builder):
        self.builder = builder
        
    def construct_mansion(self):
        self.builder.build_garage()
        self.builder.build_swimming_pool()
        self.builder.build_garage()
        
comp_house = ComplexHouse(4, 50, 100)
Director(comp_house).construct_mansion()<jupyter_output><empty_output><jupyter_text>PrototypePrototype allows to copy existing objects independent of the concrete implementation of their classes.Prototype design pattern is mainly used in languages that don't allow access to private members from outside.It is basically a class that supports cloning.One of its usecase is - for instances that downloads and stores data, this can be used to reduce repeated downloads.  SingletonBinding to a single instance  BorgAll instances have the same contents.UseCase: Db with multi-connections[Back to Creational](Creational)  Structural Structural Design Patterns (covered in this):1. [Adapter](Adapter)2. [Bridge](Bridge)3. [Composite](Composite)4. [Decorator](Decorator)5. [Facade](Facade)6. [Proxy](Proxy)7. [FlyWeight](Flyweight)  AdapterAdapter is used to work as a bridge between two or many incompatible interfaces. ![adapter](https://refactoring.guru/images/patterns/content/adapter/adapter-en-2x.png)<jupyter_code># ChangeInterface/Adapter.py
# Variations on the Adapter pattern.

class WhatIHave:
    def g(self): pass
    def h(self): pass

class WhatIWant:
    def f(self): pass

class ProxyAdapter(WhatIWant):
    def __init__(self, whatIHave):
        self.whatIHave = whatIHave

    def f(self):
        # Implement behavior using
        # methods in WhatIHave:
        self.whatIHave.g()
        self.whatIHave.h()

class WhatIUse:
    def op(self, whatIWant):
        whatIWant.f()

# Approach 2: build adapter use into op():
class WhatIUse2(WhatIUse):
    def op(self, whatIHave):
        ProxyAdapter(whatIHave).f()<jupyter_output><empty_output><jupyter_text>Bridge ![bridge](https://refactoring.guru/images/patterns/content/bridge/bridge-3-en-2x.png)  Composite<jupyter_code>from abc import ABCMeta, abstractmethod

class Item(metaclass=ABCMeta):
    
    @abstractmethod
    def price(self):
        pass


class Pencil(Item):
    @staticmethod
    def price():
        return 5
    
class CompositeBox:
    """Ex: box of boxes or items like pencil etc."""
    
    def __init__(self):
        self.items = []
        pass
    
    def add(self, item):
        self.items.append(item)
    
    def price(self):
        price = 0
        for item in self.items:
            price += item.price()
        return price
    
p1 = Pencil()
p2 = Pencil()
box = CompositeBox()
box.add(p1)
box.add(p2)
box.price()

box_1 = CompositeBox()
box_1.add(p1)
box_1.add(p2)
box_1.add(box)
box_1.price()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.08882485592583313, 'fraction_numerical': 0.0073916311701327985, 'mean_word_length': 3.652097902097902, 'pattern_counts': {'":': 4, '<': 24, '<?xml version=': 0, '>': 24, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '6', 'dir': 'jupyter-structured-clean-dedup', 'id': '334331', 'license': 'MIT', 'n_tokens_mistral': 2276, 'n_tokens_neox': 2198, 'n_words': 759, 'path': '.ipynb_checkpoints/python_design_patterns-checkpoint.ipynb', 'repo_name': 'shreyansh96/awesome-python-patterns', 'size': '7982'}