├── .gitignore ├── README.md ├── abstract_factory_pattern.py ├── adapter.py ├── chaining.py ├── chaining_authentication.py ├── command.py ├── decorators.py ├── facade.py ├── factory_method.py ├── ffmpeg ├── log.txt ├── memento.py ├── observer.py ├── proxy.py ├── simple_factory.py ├── singleton_1.py ├── singleton_2.py ├── singleton_3.py ├── singleton_4.py ├── state.py ├── state_example.py └── template_method.py /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | 4 | venv -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Programming Design patterns in python3 2 | 3 | ## Pre-requisite 4 | - Python3.6+ 5 | 6 | ## Design Patterns 7 | 8 | ### Introduction 9 | - What is design patterns? To understand watch this video - https://youtu.be/3GoiinBUwCU 10 | 11 | ### Creational Design Patterns 12 | - SignleTon - https://youtu.be/wElVjMlYVAw 13 | - Factory - https://youtu.be/4McdC4EtXKw 14 | - Simple Factory 15 | - Factory method 16 | - Abstract factory 17 | - Facade - https://youtu.be/tGK0pIbJYYg 18 | 19 | ### Behavioural Design Patterns 20 | - Command - https://youtu.be/Z7vHUey-fb8 21 | - Observer - https://youtu.be/H5kxgh8z0oo 22 | - Template Method - https://youtu.be/uX84cx_5tUQ 23 | - State - https://youtu.be/FvDyKect0tw 24 | - Flyweight - https://youtu.be/Atm2ZEIHNKU 25 | - Strategy - https://youtu.be/6446WicNoz4 26 | - Chain of Responsibility - https://youtu.be/HaNROiPxY2M 27 | 28 | ### Structural Design Patterns 29 | - Proxy - https://youtu.be/3A-G35mQqZo 30 | - Adapter - https://youtu.be/HbcAV-fYTFA 31 | - Decorator - https://youtu.be/ByDSp7mco-s 32 | -------------------------------------------------------------------------------- /abstract_factory_pattern.py: -------------------------------------------------------------------------------- 1 | """ 2 | Provide an interface for creating families of related or dependent 3 | objects without specifying their concrete classes. 4 | """ 5 | 6 | from abc import ABCMeta, abstractmethod 7 | 8 | 9 | class CarFactory(metaclass=ABCMeta): 10 | 11 | @abstractmethod 12 | def build_parts(self): 13 | pass 14 | 15 | @abstractmethod 16 | def build_car(self): 17 | pass 18 | 19 | 20 | class SedanCarFactory(CarFactory): 21 | 22 | def build_parts(self): 23 | return SedanCarPartsFactory() 24 | 25 | def build_car(self): 26 | return SedanCarAssembleFactory() 27 | 28 | 29 | class SUVCarFactory(CarFactory): 30 | 31 | def build_parts(self): 32 | return SUVCarPartsFactory() 33 | 34 | def build_car(self): 35 | return SUVCarAssembleFactory() 36 | 37 | 38 | class CarPartsFactory(metaclass=ABCMeta): 39 | """ 40 | Declare an interface for a type of car parts. 41 | """ 42 | 43 | @abstractmethod 44 | def build(self): 45 | pass 46 | 47 | 48 | class SedanCarPartsFactory(CarPartsFactory): 49 | 50 | def build(self): 51 | print("Sedan car parts are built") 52 | 53 | def __str__(self): 54 | return '' 55 | 56 | 57 | class SUVCarPartsFactory(CarPartsFactory): 58 | 59 | def build(self): 60 | print("SUV Car parts are built") 61 | 62 | def __str__(self): 63 | return '' 64 | 65 | 66 | class CarAssembleFactory(metaclass=ABCMeta): 67 | """ 68 | Declare an interface for a type of cars. 69 | """ 70 | 71 | @abstractmethod 72 | def assemble(self): 73 | pass 74 | 75 | 76 | class SedanCarAssembleFactory(CarAssembleFactory): 77 | 78 | def assemble(self, parts): 79 | print(f"Sedan car is assembled here using {parts}") 80 | 81 | 82 | class SUVCarAssembleFactory(CarAssembleFactory): 83 | 84 | def assemble(self, parts): 85 | print(f"SUV car is assembled here using {parts}") 86 | 87 | 88 | if __name__ == "__main__": 89 | for factory in (SedanCarFactory(), SUVCarFactory()): 90 | car_parts = factory.build_parts() 91 | car_parts.build() 92 | car_builder = factory.build_car() 93 | car_builder.assemble(car_parts) -------------------------------------------------------------------------------- /adapter.py: -------------------------------------------------------------------------------- 1 | """ 2 | Adapter design pattern 3 | Description - 4 | Name itself suggest that this pattern has a nature of 5 | adapting to each others. Adapter method provides multiple 6 | interfaces for different classes so they can understand or 7 | adapt each other even they are actually incompatible 8 | 9 | We have many real life examples for this pattern. 10 | One of them is data lake where you might have multiple data source stream 11 | but you have one pipe where you have support of all 12 | """ 13 | 14 | def adapter_method(): 15 | # This is adapter method 16 | You ca -------------------------------------------------------------------------------- /chaining.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | from queue import Queue 3 | 4 | 5 | class Handler(metaclass=ABCMeta): 6 | def __init__(self) -> None: 7 | self.next_handler = None 8 | 9 | def set_next(self, handler): 10 | self.next_handler = handler 11 | 12 | def run(self, request): 13 | self.handle(request) 14 | if self.next_handler: 15 | self.next_handler.run(request) 16 | 17 | @abstractmethod 18 | def handle(self, request): 19 | pass 20 | 21 | class FileHandler(Handler): 22 | def handle(self, request): 23 | with open("log.txt", "a") as f: 24 | f.write(f"{request}\n") 25 | 26 | 27 | class DatabaseHandler(Handler): 28 | def handle(self, request): 29 | print("Getting database connection") 30 | print(f"INSERT INTO requests (request) VALUES ('{request}')") 31 | print("closing connection") 32 | 33 | class EmailHandler(Handler): 34 | def handle(self, request): 35 | print(f"Sending Email for request: {request}") 36 | 37 | class DefaultHandler(Handler): 38 | def handle(self, request): 39 | print("There is no handler for this request") 40 | 41 | 42 | file_handler = FileHandler() 43 | db_handler = DatabaseHandler() 44 | email_handler = EmailHandler() 45 | file_handler.set_next(db_handler) 46 | db_handler.set_next(email_handler) 47 | 48 | request = "This is a request" 49 | file_handler.run(request) 50 | -------------------------------------------------------------------------------- /chaining_authentication.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | from queue import Queue 3 | 4 | 5 | class AbstractAuthentication(metaclass=ABCMeta): 6 | def __init__(self) -> None: 7 | self.next_handler = None 8 | 9 | def set_next(self, handler): 10 | self.next_handler = handler 11 | 12 | def run(self, request): 13 | self.authenticate(request) 14 | if not request.get("is_authenticated"): 15 | if self.next_handler: 16 | self.next_handler.run(request) 17 | else: 18 | print("Request is not authenticated") 19 | 20 | @abstractmethod 21 | def authenticate(self, request): 22 | pass 23 | 24 | class BaseAuthentication(AbstractAuthentication): 25 | def authenticate(self, request): 26 | if "username" in request and "password" in request: 27 | print("User is authenticated using basic authentication") 28 | request["is_authenticated"] = True 29 | 30 | 31 | class SessionAuthentication(AbstractAuthentication): 32 | def authenticate(self, request): 33 | if "session" in request: 34 | print("User is authenticated using session authentication") 35 | request["is_authenticated"] = True 36 | 37 | class TokenAuthentication(AbstractAuthentication): 38 | def authenticate(self, request): 39 | if "token" in request: 40 | print("User is authenticated using token authentication") 41 | request["is_authenticated"] = True 42 | 43 | authentication_chain = BaseAuthentication() 44 | session_authentication = SessionAuthentication() 45 | token_authentication = TokenAuthentication() 46 | authentication_chain.set_next(session_authentication) 47 | session_authentication.set_next(token_authentication) 48 | 49 | request1 = { 50 | "username": "test", 51 | "password": "test" 52 | } 53 | 54 | authentication_chain.run(request1) 55 | 56 | request2 = { 57 | "session": "test" 58 | } 59 | 60 | authentication_chain.run(request2) 61 | 62 | request3 = { 63 | "token": "test" 64 | } 65 | 66 | authentication_chain.run(request3) 67 | 68 | authentication_chain.run({}) 69 | -------------------------------------------------------------------------------- /command.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Command Design Pattern - This is behavioral design pattern. 3 | ''' 4 | from abc import ABC, abstractmethod 5 | 6 | 7 | class BaseCommannd(ABC): 8 | ''' 9 | Base command class 10 | ''' 11 | 12 | @abstractmethod 13 | def execute(self): 14 | raise NotImplementedError("Please implement in subclass") 15 | 16 | class EMailCommand(BaseCommannd): 17 | ''' 18 | Email Command class 19 | ''' 20 | def __init__(self, receiver, data): 21 | self.receiver = receiver 22 | self.data = data 23 | 24 | def execute(self): 25 | self.receiver.send_email(self.data) 26 | 27 | 28 | class SMSCommand(object): 29 | ''' 30 | Command class 31 | ''' 32 | def __init__(self, receiver, data): 33 | self.receiver = receiver 34 | self.data = data 35 | 36 | def execute(self): 37 | self.receiver.send_sms(self.data) 38 | 39 | 40 | 41 | class NotificationService(object): 42 | ''' 43 | Receiver class 44 | ''' 45 | def send_email(self, data): 46 | print("Sending email", data) 47 | 48 | def send_sms(self, data): 49 | print("Sending short message", data) 50 | 51 | 52 | class NotificationInvoker(object): 53 | ''' 54 | Invoker class 55 | ''' 56 | def __init__(self): 57 | self.notification_history = [] 58 | 59 | def invoke(self, command): 60 | self.notification_history.append(command) 61 | command.execute() 62 | 63 | if __name__ == "__main__": 64 | invoker = NotificationInvoker() 65 | receiver = NotificationService() 66 | invoker.invoke(EMailCommand(receiver, {"subject": "Test Email"})) 67 | invoker.invoke(SMSCommand(receiver, {"subject": "Test SMS"})) 68 | print(invoker.notification_history) -------------------------------------------------------------------------------- /decorators.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | def log_execution_time(func): 4 | """ 5 | Implements and return wrapper function 6 | """ 7 | 8 | def wrapper(*args, **kwargs): 9 | """ 10 | This function implements additional functionalities 11 | wrapping to original function 12 | """ 13 | 14 | start = time.time() 15 | result = func(*args, **kwargs) 16 | end = time.time() 17 | print(f"The execution time of {func.__name__} was {end - start}") 18 | return result 19 | 20 | return wrapper 21 | 22 | @log_execution_time 23 | def prepare_dish(name): 24 | print(f"Cooking {name}...") 25 | time.sleep(2) # preparing dish 26 | print(f"Dish {name} is ready") 27 | 28 | prepare_dish("Maxican rice") -------------------------------------------------------------------------------- /facade.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Facade design pattern 3 | ''' 4 | class Cook(object): 5 | ''' 6 | Facade class 7 | Desc: Provides easy interface to prepare dish instead of calling three 8 | different classes and making difficult for client to use. 9 | ''' 10 | def prepareDish(self): 11 | self.cutter = Cutter() 12 | self.cutter.cutVegetables() 13 | 14 | self.boiler = Boiler() 15 | self.boiler.boilVegetables() 16 | 17 | self.frier = Frier() 18 | self.frier.fry() 19 | 20 | 21 | class Cutter(object): 22 | ''' 23 | System class 24 | Desc: Cutter class provide feature of cutting vegetables 25 | ''' 26 | def cutVegetables(self): 27 | print("All vegetables are cut") 28 | 29 | 30 | class Boiler(object): 31 | ''' 32 | System class 33 | Desc: Cutter class provide feature of boiling vegetables 34 | ''' 35 | def boilVegetables(self): 36 | print("All vegetables are boiled") 37 | 38 | 39 | class Frier(object): 40 | ''' 41 | System class 42 | Desc: Cutter class provide feature of frying vegetables 43 | ''' 44 | def fry(self): 45 | print("All vegetables is mixed and fried.") 46 | 47 | 48 | if __name__ == "__main__": 49 | # Using facade class to prepare dish 50 | cook = Cook() 51 | cook.prepareDish() -------------------------------------------------------------------------------- /factory_method.py: -------------------------------------------------------------------------------- 1 | """ 2 | Learn how to create simple factory which helps to hide 3 | logic of creating objects. 4 | """ 5 | 6 | from abc import ABCMeta, abstractmethod 7 | 8 | class AbstractDegree(metaclass=ABCMeta): 9 | @abstractmethod 10 | def info(self): 11 | pass 12 | 13 | 14 | class BE(AbstractDegree): 15 | def info(self): 16 | print("Bachelor of engineering") 17 | 18 | def __str__(self): 19 | return "Bachelor of engineering" 20 | 21 | class ME(AbstractDegree): 22 | def info(self): 23 | print("Master of engineering") 24 | 25 | def __str__(self): 26 | return "Master of engineering" 27 | 28 | 29 | class MBA(AbstractDegree): 30 | def info(self): 31 | print("Master of business administration") 32 | 33 | def __str__(self): 34 | return "Master of business administration" 35 | 36 | 37 | class ProfileAbstractFactory(object): 38 | def __init__(self): 39 | self._degrees = [] 40 | self.createProfile() 41 | 42 | @abstractmethod 43 | def createProfile(self): 44 | pass 45 | 46 | def addDegree(self, degree): 47 | self._degrees.append(degree) 48 | 49 | def getDegrees(self): 50 | return self._degrees 51 | 52 | 53 | class ManagerFactory(ProfileAbstractFactory): 54 | def createProfile(self): 55 | self.addDegree(BE()) 56 | self.addDegree(MBA()) 57 | 58 | class EngineerFactory(ProfileAbstractFactory): 59 | def createProfile(self): 60 | self.addDegree(BE()) 61 | self.addDegree(ME()) 62 | 63 | class ProfileCreatorFactory(object): 64 | 65 | @classmethod 66 | def create_profile(self, name): 67 | return eval(profile_type + 'Factory')() 68 | 69 | 70 | if __name__ == '__main__': 71 | profile_type = input("Which Profile would you like to create? Manager/Engineer - ") 72 | profile = ProfileCreatorFactory.create_profile(profile_type) 73 | print("Creating Profile of ", profile_type) 74 | print("Profile has following degrees -") 75 | for deg in profile.getDegrees(): 76 | print('- ', deg) 77 | -------------------------------------------------------------------------------- /ffmpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hnmpatel/design-patterns-python/6a64d0f233a55911574ede38d60f5aa5725a4975/ffmpeg -------------------------------------------------------------------------------- /log.txt: -------------------------------------------------------------------------------- 1 | This is a request 2 | This is a request 3 | This is a request 4 | This is a request 5 | This is a request 6 | This is a request 7 | This is a request 8 | This is a request 9 | -------------------------------------------------------------------------------- /memento.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Memento Design pattern 3 | Required Clasess 4 | - Originator - 5 | - Memento 6 | - CareTaker 7 | ''' 8 | import copy 9 | 10 | class User: 11 | def __init__(self, name: str, age: int) -> None: 12 | self.name = name 13 | self.age = age 14 | 15 | def __str__(self) -> str: 16 | return f'{self.name} - {self.age}' 17 | 18 | 19 | class Memento: 20 | def __init__(self, state): 21 | self.state = state 22 | 23 | class Originator: 24 | def __init__(self): 25 | self.state = None 26 | 27 | def set_state(self, state): 28 | self.state = state 29 | 30 | def get_memento(self): 31 | return Memento(self.state) 32 | 33 | def restore_memento(self, memento): 34 | self.state = memento.state 35 | 36 | class Caretaker: 37 | def __init__(self): 38 | self.mementos = [] 39 | 40 | def add_memento(self, memento): 41 | self.mementos.append(memento) 42 | 43 | def get_memento(self, index): 44 | return self.mementos[index] 45 | 46 | def main(): 47 | originator = Originator() 48 | caretaker = Caretaker() 49 | 50 | user = User('hardik', 20) 51 | 52 | originator.set_state(copy.deepcopy(user)) 53 | memento = originator.get_memento() 54 | caretaker.add_memento(memento) 55 | 56 | user.age = 33 57 | originator.set_state(copy.deepcopy(user)) 58 | print("Current state:", originator.state) 59 | 60 | originator.restore_memento(caretaker.get_memento(0)) 61 | print("Current state:", originator.state) 62 | 63 | main() -------------------------------------------------------------------------------- /observer.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Obeserver Design pattern 3 | ''' 4 | 5 | class User: # Observer class 6 | ''' 7 | User class will act role of observer to subject 8 | ''' 9 | def __init__(self, name): 10 | self.name = name 11 | 12 | def update(self, article, blog_writer): 13 | print(f'For {self.name}, new article {article} by {blog_writer.name} is added') 14 | 15 | class BlogWriter: 16 | ''' 17 | BlogWriter class is useful to blog writer to add new article 18 | and manage subscribers as well 19 | ''' 20 | def __init__(self, name): 21 | self.name = name 22 | self.__subscribers = [] 23 | self.__articles = [] # Article is the subject 24 | 25 | def add_article(self, article): 26 | ''' 27 | Add new article and notify subscribers 28 | ''' 29 | self.__articles.append(article) 30 | self.notify_subscribers(article) 31 | 32 | def get_articles(self): 33 | ''' 34 | Get articles written by {self} 35 | ''' 36 | return self.__articles 37 | 38 | def subscribe(self, subscriber): 39 | ''' 40 | Add new subscriber to notify on adding article 41 | ''' 42 | self.__subscribers.append(subscriber) 43 | 44 | def unsubscribe(self, subscriber): 45 | ''' 46 | User can unsubscribe from further notifications 47 | ''' 48 | return self.__subscribers.remove(subscriber) 49 | 50 | def subscribers(self): 51 | ''' 52 | Get subsribers 53 | ''' 54 | return self.__subscribers 55 | 56 | def notify_subscribers(self, article): 57 | ''' 58 | Notifying all the subsribers about new addition of an article 59 | ''' 60 | for sub in self.__subscribers: 61 | sub.update(article, self) 62 | 63 | if __name__ == '__main__': 64 | blog_writer = BlogWriter('Hardik\'s blog') 65 | shailaja = User('Shailaja') 66 | aarav = User('Aarav') 67 | blog_writer.subscribe(shailaja) 68 | blog_writer.subscribe(aarav) 69 | blog_writer.add_article('Article 1') 70 | blog_writer.unsubscribe(aarav) 71 | blog_writer.add_article('Article 2') -------------------------------------------------------------------------------- /proxy.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Proxy Design Pattern - This is structural design pattern. 3 | ''' 4 | import abc 5 | 6 | class AbstractCmd(metaclass=abc.ABCMeta): 7 | 8 | @abc.abstractmethod 9 | def execute(self, command): 10 | pass 11 | 12 | class RealCmd(AbstractCmd): 13 | 14 | def execute(self, command): 15 | print(f"{command} command executed.") 16 | 17 | 18 | class ProxyCmd(AbstractCmd): 19 | 20 | def __init__(self, user): 21 | self.is_authorized = False 22 | if user == "admin": 23 | self.is_authorized = True 24 | self.executor = RealCmd() 25 | self.restricted_commands = ['rm', 'mv'] 26 | 27 | def execute(self, command): 28 | if self.is_authorized: 29 | self.executor.execute(command) 30 | else: 31 | if any([command.strip().startswith(cmd) 32 | for cmd in self.restricted_commands]): 33 | raise Exception(f"{command} command is not allowed for non-admin users.") 34 | else: 35 | self.executor.execute(command) 36 | 37 | 38 | if __name__ == '__main__': 39 | admin_executor = ProxyCmd("admin") 40 | other_executor = ProxyCmd("other") 41 | try: 42 | admin_executor.execute("ls -la"); 43 | admin_executor.execute("rm -rf /"); 44 | print("\n") 45 | other_executor.execute("ls -la"); 46 | other_executor.execute("rm -rf"); 47 | except Exception as e: 48 | print(e) -------------------------------------------------------------------------------- /simple_factory.py: -------------------------------------------------------------------------------- 1 | """ 2 | Learn how to create simple factory which helps to hide 3 | logic of creating objects. 4 | """ 5 | 6 | from abc import ABCMeta, abstractmethod 7 | 8 | class Person(metaclass=ABCMeta): 9 | @abstractmethod 10 | def create(self): 11 | pass 12 | 13 | class HR(Person): 14 | def create(self, name): 15 | print(f"HR {name} is created") 16 | 17 | class Engineer(Person): 18 | def create(self, name): 19 | print(f"Engineer {name} is created") 20 | 21 | class PersonFactory(object): 22 | @classmethod 23 | def createPerson(cls, designation, name): 24 | eval(designation)().create(name) 25 | 26 | 27 | if __name__ == "__main__": 28 | designation = input("Please enter the designation - ") 29 | name = input("Please enter the person's name - ") 30 | PersonFactory.createPerson(designation, name) 31 | -------------------------------------------------------------------------------- /singleton_1.py: -------------------------------------------------------------------------------- 1 | """ 2 | Pattern name - SingleTon 3 | Pattern type - Creational Design Pattern 4 | """ 5 | 6 | # Solution - 1 7 | class SingleTon(object): 8 | def __new__(cls, *args, **kwargs): 9 | if not hasattr(cls, '_instance'): 10 | cls._instance = super().__new__(cls, *args, **kwargs) 11 | return cls._instance 12 | 13 | o1 = SingleTon() 14 | print("Object - 1 ==>", o1) 15 | o1.data = 10 16 | 17 | o2 = SingleTon() 18 | print("Object - 2 ==>", o2) 19 | print("Object - 2 data ==>", o2.data) 20 | o2.data = 5 21 | 22 | print("Object - 1 data ==>", o1.data) -------------------------------------------------------------------------------- /singleton_2.py: -------------------------------------------------------------------------------- 1 | """ 2 | Pattern name - SingleTon (Mono state pattern) 3 | Pattern type - Creational Design Pattern 4 | """ 5 | 6 | # Solution - 2 7 | class Borg(object): 8 | _shared = {} 9 | 10 | def __init__(self): 11 | self.__dict__ = self._shared 12 | 13 | 14 | class SingleTon(Borg): 15 | def __init__(self, arg): 16 | Borg.__init__(self) 17 | self.val = arg 18 | 19 | # def __str__(self): 20 | # return "<{} - Object>".format(self.val) 21 | 22 | 23 | o1 = SingleTon("Hardik") 24 | print("Object - 1 ==>", o1) 25 | print("Object - 1 val ==>", o1.val) 26 | 27 | o2 = SingleTon("Aarav") 28 | print("Object - 2 ==>", o2) 29 | print("Object - 2 val ==>", o2.val) 30 | print("Object - 1 val ==>", o1.val) 31 | 32 | print(o1.__dict__) 33 | print(o2.__dict__) -------------------------------------------------------------------------------- /singleton_3.py: -------------------------------------------------------------------------------- 1 | """ 2 | Pattern name - SingleTon 3 | Pattern type - Creational Design Pattern 4 | """ 5 | 6 | 7 | # Solution - 3 8 | class SingletonDecorator(object): 9 | def __init__(self, klass): 10 | self.klass = klass 11 | self.instance = None 12 | 13 | def __call__(self, *args, **kwargs): 14 | if self.instance == None: 15 | self.instance = self.klass(*args,**kwargs) 16 | return self.instance 17 | 18 | 19 | @SingletonDecorator 20 | class Logger(object): 21 | def __init__(self): 22 | self.start = None 23 | 24 | def write(self, message): 25 | if self.start: 26 | print(self.start, message) 27 | else: 28 | print(message) 29 | 30 | logger1 = Logger() 31 | logger1.start = "# >" 32 | print("Logger 1", logger1) 33 | logger1.write("Logger1 object is created.") 34 | 35 | logger2 = Logger() 36 | logger2.start = "$ >" 37 | print("Logger 2", logger2) 38 | logger1.write("Logger2 object is created.") -------------------------------------------------------------------------------- /singleton_4.py: -------------------------------------------------------------------------------- 1 | """ 2 | Pattern name - SingleTon 3 | Pattern type - Creational Design Pattern 4 | """ 5 | 6 | 7 | # Solution - 4 8 | class SingletonMeta(type): 9 | __instances = {} 10 | 11 | def __call__(cls, *args, **kwargs): 12 | if cls not in cls.__instances: 13 | cls.__instances[cls] = super().__call__(*args, **kwargs) 14 | print(cls.__instances) 15 | return cls.__instances[cls] 16 | 17 | 18 | class DBConnector(metaclass=SingletonMeta): 19 | def __init__(self): 20 | self.status = "Not Connected" 21 | 22 | def disconnect(self): 23 | self.status = "Disconnected" 24 | 25 | def connect(self): 26 | self.status = "Connected" 27 | 28 | 29 | client1 = DBConnector() 30 | print("Client 1 ", client1) 31 | print(client1.status) 32 | 33 | client2 = DBConnector() 34 | print("Client 2 ", client2) 35 | client2.connect() 36 | print(client1.status) 37 | 38 | client1.disconnect() 39 | print(client2.status) -------------------------------------------------------------------------------- /state.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | 4 | class State(ABC): 5 | 6 | @abstractmethod 7 | def push_down_btn(self) -> None: 8 | pass 9 | 10 | @abstractmethod 11 | def push_up_btn(self) -> None: 12 | pass 13 | 14 | 15 | class Elevator: 16 | 17 | _state = None 18 | 19 | def __init__(self, state: State) -> None: 20 | self.set_elevator(state) 21 | 22 | def set_elevator(self, state: State): 23 | self._state = state 24 | self._state.elevator = self 25 | 26 | def present_state(self): 27 | print(f"Elevator is on {type(self._state).__name__}") 28 | 29 | def push_down_btn(self): 30 | self._state.push_down_btn() 31 | 32 | def push_up_btn(self): 33 | self._state.push_up_btn() 34 | 35 | 36 | class GroundFloor(State): 37 | 38 | def push_down_btn(self) -> None: 39 | print("Already on the Ground floor") 40 | 41 | def push_up_btn(self) -> None: 42 | print("Elevator moving upward one floor.") 43 | self.elevator.set_elevator(FirstFloor()) 44 | 45 | 46 | class FirstFloor(State): 47 | 48 | def push_down_btn(self) -> None: 49 | print("Elevator moving down one floor.") 50 | self.elevator.set_elevator(GroundFloor()) 51 | 52 | def push_up_btn(self) -> None: 53 | print("Elevator moving upward one floor.") 54 | self.elevator.set_elevator(SecondFloor()) 55 | 56 | 57 | class SecondFloor(State): 58 | 59 | def push_down_btn(self) -> None: 60 | print("Elevator moving down a floor...") 61 | self.elevator.set_elevator(FirstFloor()) 62 | 63 | def push_up_btn(self) -> None: 64 | print("Already on the top floor") 65 | 66 | 67 | if __name__ == "__main__": 68 | 69 | myElevator = Elevator(GroundFloor()) 70 | myElevator.present_state() 71 | myElevator.push_up_btn() 72 | myElevator.push_up_btn() 73 | myElevator.present_state() 74 | myElevator.push_down_btn() 75 | myElevator.present_state() 76 | -------------------------------------------------------------------------------- /state_example.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hnmpatel/design-patterns-python/6a64d0f233a55911574ede38d60f5aa5725a4975/state_example.py -------------------------------------------------------------------------------- /template_method.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Template method Pattern - This is behavioral design pattern. 3 | ''' 4 | import abc 5 | 6 | 7 | class ThreeDaysTrip(metaclass=abc.ABCMeta): 8 | @abc.abstractmethod 9 | def transport(self): 10 | pass 11 | 12 | @abc.abstractmethod 13 | def day1(self): 14 | pass 15 | 16 | @abc.abstractmethod 17 | def day2(self): 18 | pass 19 | 20 | @abc.abstractmethod 21 | def day3(self): 22 | pass 23 | 24 | @abc.abstractmethod 25 | def back_to_home(self): 26 | pass 27 | 28 | def iternary(self): 29 | print("Trip is started") 30 | self.transport() 31 | self.day1() 32 | self.day2() 33 | self.day3() 34 | self.back_to_home() 35 | print("Trip is over") 36 | 37 | 38 | class SouthTrip(ThreeDaysTrip): 39 | def transport(self): 40 | print("Go by train! check in to hotel") 41 | 42 | def day1(self): 43 | print("Day-1: Enjoy the hotel beach whole day") 44 | 45 | def day2(self): 46 | print("Day-2: Visit historical places and Enjoy cruise life at night") 47 | 48 | def day3(self): 49 | print("Day-3: Enjoy shopping day with family and go anywhere you wish") 50 | 51 | def back_to_home(self): 52 | print("Check out and go Home by air!") 53 | 54 | 55 | class NorthTrip(ThreeDaysTrip): 56 | def transport(self): 57 | print("Go by air! check in to hotel") 58 | 59 | def day1(self): 60 | print("Day-1: Go to very highted place and enjoy snow activities") 61 | 62 | def day2(self): 63 | print("Day-2: Enjoy river rafting and lavish dinner at night") 64 | 65 | def day3(self): 66 | print("Day-3: Enjoy shopping day with family and go anywhere you wish") 67 | 68 | def back_to_home(self): 69 | print("Check out and go Home by air!") 70 | 71 | 72 | if __name__ == "__main__": 73 | place = input("Where do you want to go? ") 74 | if place == 'north': 75 | trip = NorthTrip() 76 | trip.iternary() 77 | elif place == 'south': 78 | trip = SouthTrip() 79 | trip.iternary() 80 | else: 81 | print("Sorry, We do not have any trip towards that place!") --------------------------------------------------------------------------------