diff --git a/README.md b/README.md index af3242d5..d2fd2860 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,11 @@ written correctly. - [Data Compression](src/standard_libraries/test_zlib.py) (`zlib` library) 12. **User input** - [Terminal input](src/user_input/test_input.py) (`input` statement) +13. **Design Patterns** + - [Creational Patterns](src/design_patterns/creational_patterns/test_creational_pattern.py) (`creational` design pattern) + - [Structural Patterns](src/design_patterns/structural_patterns/test_structural_pattern.py) (`structural` design pattern) + - [Behavioral Patterns](src/design_patterns/behavioral_patterns/test_behavioral_pattern.py) (`behavioral` design pattern) + - [Concurrency Patterns](src/design_patterns/concurrency_patterns/test_concurrency_pattern.py) (`concurrency` design pattern) ## Prerequisites diff --git a/src/design_patterns/behavioral_patterns/test_behavioral_pattern.py b/src/design_patterns/behavioral_patterns/test_behavioral_pattern.py new file mode 100644 index 00000000..fe626f3a --- /dev/null +++ b/src/design_patterns/behavioral_patterns/test_behavioral_pattern.py @@ -0,0 +1,86 @@ +"""Behavioral Patterns + +@see https://legacy.python.org/workshops/1997-10/proceedings/savikko.html#gof +@see https://www.toptal.com/python/python-design-patterns +@see https://github.com/faif/python-patterns + +Being one of software engineering's main fields of work, software design patterns are a general, reusable solution to a commonly occurring software design problem. +A design pattern can be understood as a description or template, guiding an engineer when solving a problem through best practices and well-known standards. +On top, design patterns make code easier to understand and maintainable. + +Behavioral design patterns - a subcategory of design patterns - are design patterns that identify common communication patterns among objects. +By doing so, these patterns increase flexibility in carrying out inter-object-communication. + +Typical Behavioral design patterns are: + +- Blackboard +- Command +- Chain of Responsibility +- Interpretar +- Iterator +- Mediator +- Memento +- Observer +- Protocol Stack +- State +- Strategy +- Visitor +- Scheduled-Task + +Note: Design patterns fit a special role in python because everything in python is an object, even functions are objects. Some design patterns are already implemented in python, +others are not necessary due to python's nature. +""" + + +# Chain of Responsibility +class ContentFilter(object): + """Every code element has one - and only one - specific job""" + + def __init__(self, filters=None): + self._filters = list() + if filters is not None: + self._filters += filters + + def filter(self, content): + for filter in self._filters: + content = filter(content) + return content + +filter = ContentFilter([ + offensive_filter, + ads_filter, + porno_video_filter]) + +filtered_content = filter.filter(content) + + +# Command +class RenameFileCommand(object): + """Command execution when needed""" + + def __init__(self, from_name, to_name): + self._from = from_name + self._to = to_name + + def execute(self): + os.rename(self._from, self._to) + + def undo(self): + os.rename(self._to, self._from) + +class History(object): + def __init__(self): + self._commands = list() + + def execute(self, command): + self._commands.append(command) + command.execute() + + def undo(self): + self._commands.pop().undo() + +history = History() +history.execute(RenameFileCommand('docs/cv.doc', 'docs/cv-en.doc')) +history.execute(RenameFileCommand('docs/cv1.doc', 'docs/cv-bg.doc')) +history.undo() +history.undo() diff --git a/src/design_patterns/creational_patterns/test_creational_pattern.py b/src/design_patterns/creational_patterns/test_creational_pattern.py new file mode 100644 index 00000000..31050073 --- /dev/null +++ b/src/design_patterns/creational_patterns/test_creational_pattern.py @@ -0,0 +1,73 @@ +"""Creational Patterns + +@see https://legacy.python.org/workshops/1997-10/proceedings/savikko.html#gof +@see https://www.toptal.com/python/python-design-patterns +@see https://github.com/faif/python-patterns + +Being one of software engineering's main fields of work, software design patterns are a general, reusable solution to a commonly occurring software design problem. +A design pattern can be understood as a description or template, guiding an engineer when solving a problem through best practices and well-known standards. +On top, design patterns make code easier to understand and maintainable. + +Creational design patterns - a subcategory of design patterns - are patterns dealing with object creation mechanisms (often in OOP environments), +trying to create objects in a manner suitable to the situation. The basic form of object creation could result in design problems or in added complexity to the design. +Creational design patterns aim to solve this problem by somehow controlling this object creation. + +Typical Creational design patterns are: + +- Abstract Factory +- Builder +- Dependency Injection +- Factory Method +- Lazy Initialization +- Multition +- Object Pool +- Prototype +- Singleton + +Note: Design patterns fit a special role in python because everything in python is an object, even functions are objects. Some design patterns are already implemented in python, +others are not necessary due to python's nature. Creational patterns are rather uncommon in python, since it already is a very dynamic language. +""" + + +# Singleton +class Logger(object): + """Making sure that only one instance of a given class exists during runtime""" + def __new__(cls, *args, **kwargs): + if not hasattr(cls, '_logger'): + cls._logger = super(Logger, cls + ).__new__(cls, *args, **kwargs) + return cls._logger + + +# Dependency Injection +class Command: + """Creating object outside of where it is used""" + + def __init__(self, authenticate=None, authorize=None): + self.authenticate = authenticate or self._not_authenticated + self.authorize = authorize or self._not_autorized + + def execute(self, user, action): + self.authenticate(user) + self.authorize(user, action) + return action() + +if in_sudo_mode: + command = Command(always_authenticated, always_authorized) +else: + command = Command(config.authenticate, config.authorize) + +command.execute(current_user, delete_user_action) + + +# Alternative Dependency Injection +command = Command() + +if in_sudo_mode: + command.authenticate = always_authenticated + command.authorize = always_authorized +else: + command.authenticate = config.authenticate + command.authorize = config.authorize + +command.execute(current_user, delete_user_action) diff --git a/src/design_patterns/structural_patterns/test_structural_pattern.py b/src/design_patterns/structural_patterns/test_structural_pattern.py new file mode 100644 index 00000000..5bd0fe4f --- /dev/null +++ b/src/design_patterns/structural_patterns/test_structural_pattern.py @@ -0,0 +1,117 @@ +"""Structural Patterns + +@see https://legacy.python.org/workshops/1997-10/proceedings/savikko.html#gof +@see https://www.toptal.com/python/python-design-patterns +@see https://github.com/faif/python-patterns + +Being one of software engineering's main fields of work, software design patterns are a general, reusable solution to a commonly occurring software design problem. +A design pattern can be understood as a description or template, guiding an engineer when solving a problem through best practices and well-known standards. +On top, design patterns make code easier to understand and maintainable. + +Structural design patterns - a subcategory of design patterns - are patterns dealing with object relations. They ease the design by identifying a simple way to +realize relationships among entities. + +Typical Structural design patterns are: + +- Adapter +- Aggregate +- Facade +- Bridge +- Flyweight +- Decorator +- Marker +- Proxy + +Note: Design patterns fit a special role in python because everything in python is an object, even functions are objects. Some design patterns are already implemented in python, +others are not necessary due to python's nature. +""" + + +# Facade +class Car(object): + """Streamlining an interface by creating a client-facing facade which hides multiple other objects""" + + def __init__(self): + self._tyres = [Tyre('front_left'), + Tyre('front_right'), + Tyre('rear_left'), + Tyre('rear_right'), ] + self._tank = Tank(70) + + def tyres_pressure(self): + return [tyre.pressure for tyre in self._tyres] + + def fuel_level(self): + return self._tank.level + + +# Adapter --> similar to Bridge and Proxy +import socket + +class SocketWriter(object): + """Altering an interface by translating a new interface to a known one""" + + def __init__(self, ip, port): + self._socket = socket.socket(socket.AF_INET, + socket.SOCK_DGRAM) + self._ip = ip + self._port = port + + def write(self, message): + self._socket.send(message, (self._ip, self._port)) + +def log(message, destination): + destination.write('[{}] - {}'.format(datetime.now(), message)) + +upd_logger = SocketWriter('1.2.3.4', '9999') +log('Something happened', udp_destination) + + +# Decorator + """Introducing additional functionality without inheritance""" + +def execute(action, *args, **kwargs): + return action() + +def autheticated_only(method): + def decorated(*args, **kwargs): + if check_authenticated(kwargs['user']): + return method(*args, **kwargs) + else: + raise UnauthenticatedError + return decorated + +def authorized_only(method): + def decorated(*args, **kwargs): + if check_authorized(kwargs['user'], kwargs['action']): + return method(*args, **kwargs) + else: + raise UnauthorizeddError + return decorated + +execute = authenticated_only(execute) +execute = authorized_only(execute) + +# We can also use the core-python integrated syntax +def autheticated_only(method): + def decorated(*args, **kwargs): + if check_authenticated(kwargs['user']): + return method(*args, **kwargs ) + else: + raise UnauthenticatedError + return decorated + + +def authorized_only(method): + def decorated(*args, **kwargs): + if check_authorized(kwargs['user'], kwargs['action']): + return method(*args, **kwargs) + else: + raise UnauthorizedError + return decorated + + +@authorized_only +@authenticated_only +def execute(action, *args, **kwargs): + return action()