Python package implements design patterns in generic way. Its can be used in a wide range of projects.
Project description
Python Generic Design Patterns
Python package implements design patterns in generic way. Its can be used in a wide range of projects. Some of these patterns are slightly improved for efficient use in real-world projects.
Installation
pip install generic-design-patterns
Overview
Implemented Patterns
Other parts of package
Chain Of Responsibility
The purpose of this text is not to explain the principles of chain of responsibility. For example, source describing CoR is refactoring.guru. This package implements node of chain as plugin. For more information about plugin in this package visit plugin chapter.
How it works in few steps
-
User create chain node plugin
-
User set collectors which collect all chain nodes (plugins)
-
User call build function
Chain Node
Chain node have to inherit from gdp.chain.ChainNodePlugin
, which inherit form yapsy.IPlugin.IPlugin
.
Each node of chain have to implement these methods:
-
check()
- It detects that the request is handleable by the node. The method has to return bool value. -
handle()
- It is performing method which processes the request. It returns result. -
description()
- It returns string or any other class which describes the node/plugin.
All nodes/plugins (in one chain) have to implement check()
and handle()
with same arguments.
Examples
Here is a short minimum example. It implements chain nodes for pseudo handling different text formats.
TXT Node Plugin
import generic_design_patterns as gdp
class TxtChainPlugin(gdp.chain.ChainNodePlugin):
answer = "txt successfully handled"
def check(self, input_string):
return "txt" == input_string.strip()
def handle(self, input_string):
return self.answer
def description(self):
return "txt"
JSON Node Plugin
import generic_design_patterns as gdp
class JsonChainPlugin(gdp.chain.ChainNodePlugin):
answer = "json successfully handled"
def check(self, input_string):
return "json" == input_string.strip()
def handle(self, input_string):
return self.answer
def description(self):
return "json"
Build chain
import generic_design_patterns as gdp
collectors = [gdp.plugin.SubclassPluginCollector(gdp.chain.ChainNodePlugin)]
chain = gdp.chain.build(collectors)
This example uses gdp.plugin.SubclassPluginCollector
. This package implements more plugin collectors, which are described in part plugin collectors.
Handle request by chain
for request in ["txt", "json", "yaml"]:
result = chain.handle(request)
print(result)
>>> txt successfully handled
>>> json successfully handled
>>> None
Get description of chain nodes
The chain is dynamically build by collected plugins. Generally we do not know which nodes chain will contain (before build).
However assembled chain should offer information about its nodes. It other words chain should describe which request is able handle.
This feature cover chain method description()
.
descriptions = chain.description()
print(descriptions)
>>> ["txt", "json"]
Input value yaml has not handler in the chain. In that case return value is None
.
Event Provider
This standard implementation of publisher-subscriber design pattern. There are not any improvements. Note that current implementation is only for single thread/process usage.
How it works
-
Main part is event provider, which store subscriptions. On the basis of subscriptions provider directs notifications to right subscribers.
-
Subscribers can register at provider.
-
Publishers can send notification via provider.
Examples
The code shows minimum example. Note:
-
The subscriber has to implement
update()
method. The package containsAdvancedSubscriber
class which add methods for subscribe and unsubscribe itself. -
The publisher is created only for this example. Important is line where
notify()
method is called. -
The example shows how to make subscription. It has to part string
message
andsubscriber
object. -
Use notification class from this package or your custom class which should inherit from it. The most import is that notification has to contain message attribute.
import generic_design_patterns as gdp
dummy_message = "dummy message"
class DummySubscriber(gdp.event.Subscriber):
def __init__(self):
self.notification = None
def update(self, notification):
print(notification.message)
class DummyPublisher:
def __init__(self, provider):
self.provider = provider
def publish(self):
dummy_notification = gdp.event.Notification(dummy_message)
self.provider.notify(dummy_notification)
provider = gdp.event.Provider()
subscriber = DummySubscriber()
provider.subscribe(dummy_message, subscriber)
publisher = DummyPublisher(provider)
publisher.publish()
print(subscriber.notification.message)
>>> dummy message
Specification
The purpose of this text is not to explain the principles of specification pattern. For detail information visit wiki - specification pattern.
However it is useful to describe the most important aspects of this pattern:
-
This pattern encapsulates condition to class.
-
This pattern enables compose condition together and create more complex conditions. All this without losing readability and clarity. In other words, it allows to avoid an endless cascade of if-else cascades or some very long condition.
-
This pattern allows to create the composite conditions dynamically.
Examples
The example is intended to demonstrate the creation of a complex condition.
First of all define list to work with.
alphabet_list = ["Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel"]
Now define rules for selecting items:
-
Select items its index is lower or equal to 2 and not start with char "b" (case insensitive).
-
Select items its index is higher than 2 and it contains "e" or "a" but not both. (case insensitive)
Create single conditions by specification pattern
import generic_design_patterns as gdp
class ContainChar(gdp.specification.Condition):
required_char = ""
def is_satisfied(self, index, item):
return self.required_char in item.lower()
class ContainCharA(ContainChar):
required_char = "a"
class ContainCharE(ContainChar):
required_char = "e"
class IsIndexHigherThanTwo(gdp.specification.Condition):
def is_satisfied(self, index, item):
return index > 2
class FirstCharIsB(gdp.specification.Condition):
def is_satisfied(self, index, item):
return item[0].lower() == "b"
Note that input arguments of method is_satisfied()
, it depends only on the user's requirements. But it is necessary that the arguments of all conditions are the same.
Put single conditions together
condition = (~IndexHigherThanTwo() & ~FirstCharIsB())
condition |= (IndexHigherThanTwo() & (ContainCharA() ^ ContainCharE()))
Apply condition
Iterate over the list and filter items which meet condition.
for index, item in enumerate(alphabet_list):
if condition(index, item):
print(item)
>>> Alpha
>>> Charlie
>>> Echo
>>> Hotel
Plugin
Here is not implement some plugin system. Plugin module only encapsulates existing systems and makes it easier to use. Current version of the package uses plugin only for chain of responsibility.
Collectors
In the context of this package, plugin can be average class or Yapsy plugin. For more information about Yapsy plugin system visit Yapsy documentation pages.
Collectors are intended for find plugin and make it accessible. This package contains three basic plugin collectors:
gdp.plugin.YapsyPluginCollector
gdp.plugin.YapsyRegExPluginCollector
gdp.plugin.SubclassPluginCollector
All examples in this chapter follow the example in chapter chain of responsibility.
YapsyPluginCollector
In the default setting, this collector find standard Yapsy plugins by .yapsy-plugin
info file.
Assume this directory structure:
+- plugins/
+- toml.py
+- toml.yapsy-plugin
+- yaml.py
+- yaml.yapsy-plugin
toml.py
import generic_design_patterns as gdp
class TomlChainPlugin(gdp.chain.ChainNodePlugin):
answer = "toml successfully handled"
def check(self, input_string):
return "toml" == input_string.strip()
def handle(self, input_string):
return self.answer
def description(self):
return "toml"
toml.yapsy-plugin
[Core]
Name = toml
Module = toml
[Documentation]
Author = ShadowCodeCz
Version = 0.1
Description = Test Toml Plugin
Toml and Yaml plugins are similar.
Collector construction
import generic_design_patterns as gdp
collector = gdp.plugin.YapsyPluginCollector(["./plugins"])
If you are experienced with Yapsy, you can use attribute plugin_manager
of gdp.plugin.YapsyPluginCollector
class. It is instance of yapsy.PluginManager.PluginManager
.
YapsyRegExPluginCollector
This collector is child of YapsyPluginCollector
, which bring some improvements:
- plugins are located in destination by regular expression
.yapsy-plugin
are not required
Assume this directory structure which is similar to previous one only without .yapsy-plugin
. Contents of .py
file are same.
+- plugins/
+- t_plugin_toml.py
+- t_plugin_yaml.py
Collector construction
import generic_design_patterns as gdp
collector = gdp.plugin.YapsyRegExPluginCollector(["./plugins"], "t_plugin_.+.py$")
Be careful about regular expression. Especially about ending symbol $
. It will find also .pyc
files without $
at the end of re. It will causes problems.
SubclassPluginCollector
It is collecting all child of selected class. The example of usage the collector SubclassPluginCollector
is in chain of responsibility chapter.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
File details
Details for the file generic_design_patterns-1.1.10.tar.gz
.
File metadata
- Download URL: generic_design_patterns-1.1.10.tar.gz
- Upload date:
- Size: 13.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.2.0 pkginfo/1.6.1 requests/2.24.0 setuptools/50.3.2 requests-toolbelt/0.9.1 tqdm/4.51.0 CPython/3.9.0
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | c83cc8289ffc60d30fa23aac678b612fd8a7217031287d065922c80e2ce31239 |
|
MD5 | 3d5a91833c8123ebb90dfd963447c0dd |
|
BLAKE2b-256 | 270647a96ac46d1f67d6e61fe257b0230410777e361978298be8d3b3ab3a35e7 |