added builder pattern
This commit is contained in:
75
pypatterns/creational/builder.py
Normal file
75
pypatterns/creational/builder.py
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
from abc import ABCMeta, abstractmethod
|
||||||
|
|
||||||
|
|
||||||
|
class Director(object, metaclass=ABCMeta):
|
||||||
|
"""
|
||||||
|
Abstract director class, responsible for using a builder to fully construct an object.
|
||||||
|
|
||||||
|
Part of the builder pattern.
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Initialize a new Director.
|
||||||
|
"""
|
||||||
|
self.builder = None
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def construct(self):
|
||||||
|
"""
|
||||||
|
Abstract class for fully constructing an object.
|
||||||
|
|
||||||
|
Concrete implementations should override this and use a builder to construct the object.
|
||||||
|
|
||||||
|
@raise NotImplementedError: If this method is not overridden.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_constructed_object(self):
|
||||||
|
"""
|
||||||
|
Get the object this director is responsible for constructing.
|
||||||
|
|
||||||
|
@return: The object that this director is responsible for constructing.
|
||||||
|
"""
|
||||||
|
return self.builder.constructed_object
|
||||||
|
|
||||||
|
|
||||||
|
class Builder(object):
|
||||||
|
"""
|
||||||
|
Abstract builder class, responsible for constructing various pieces of an object.
|
||||||
|
|
||||||
|
Part of the builder pattern.
|
||||||
|
"""
|
||||||
|
def __init__(self, constructed_object):
|
||||||
|
"""
|
||||||
|
Initialize a new Builder.
|
||||||
|
|
||||||
|
Concrete Builders should call this method from within their own __init__ method.
|
||||||
|
The concrete __init__ method should also register all build options to build methods,
|
||||||
|
by using the _register method.
|
||||||
|
|
||||||
|
@param constructed_object: An instance of an object this builder is responsible for.
|
||||||
|
"""
|
||||||
|
self.constructed_object = constructed_object
|
||||||
|
self.build_methods = dict()
|
||||||
|
|
||||||
|
def build(self, build_option, **kwargs):
|
||||||
|
"""
|
||||||
|
Build a piece of the constructed object.
|
||||||
|
|
||||||
|
@param build_option: The part of the object to build. All build options should have been registered in __init__.
|
||||||
|
@type build_option: str
|
||||||
|
@param kwargs: Additional arguments for building.
|
||||||
|
"""
|
||||||
|
self.build_methods[build_option](**kwargs)
|
||||||
|
|
||||||
|
def _register(self, build_option, build_method):
|
||||||
|
"""
|
||||||
|
Register a build option to a build method.
|
||||||
|
|
||||||
|
All concrete builders should call this method in their constructor at least once.
|
||||||
|
|
||||||
|
@param build_option: A string representing the part of the object to build.
|
||||||
|
@type build_option: str
|
||||||
|
@param build_method: The method to call when given build option is selected.
|
||||||
|
"""
|
||||||
|
self.build_methods[build_option] = build_method
|
||||||
146
tests/creational_tests/test_builder.py
Normal file
146
tests/creational_tests/test_builder.py
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
from unittest import TestCase
|
||||||
|
from pypatterns.creational.builder import Director, Builder
|
||||||
|
from abc import ABCMeta, abstractmethod
|
||||||
|
|
||||||
|
|
||||||
|
class BuilderTestCase(TestCase):
|
||||||
|
"""
|
||||||
|
Unit testing class for the Builder class.
|
||||||
|
"""
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
Initialize testing data.
|
||||||
|
"""
|
||||||
|
class Building(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.floor = None
|
||||||
|
self.size = None
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'Floor: {0.floor} | Size: {0.size}'.format(self)
|
||||||
|
|
||||||
|
class HomeBuilder(Builder, metaclass=ABCMeta):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(Building())
|
||||||
|
self._register('floor', self._build_floor)
|
||||||
|
self._register('size', self._build_size)
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def _build_floor(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def _build_size(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class HouseBuilder(HomeBuilder):
|
||||||
|
|
||||||
|
def _build_floor(self):
|
||||||
|
self.constructed_object.floor = 'One'
|
||||||
|
|
||||||
|
def _build_size(self):
|
||||||
|
self.constructed_object.size = 'Big'
|
||||||
|
|
||||||
|
class FlatBuilder(HomeBuilder):
|
||||||
|
|
||||||
|
def _build_floor(self):
|
||||||
|
self.constructed_object.floor = 'More than one'
|
||||||
|
|
||||||
|
def _build_size(self):
|
||||||
|
self.constructed_object.size = 'Small'
|
||||||
|
|
||||||
|
self.house_builder = HouseBuilder()
|
||||||
|
self.flat_builder = FlatBuilder()
|
||||||
|
|
||||||
|
def test_builder(self):
|
||||||
|
"""
|
||||||
|
Test the build method.
|
||||||
|
|
||||||
|
@raise AssertionError: If the test fails.
|
||||||
|
"""
|
||||||
|
self.house_builder.build('floor')
|
||||||
|
self.house_builder.build('size')
|
||||||
|
self.assertEquals('One', self.house_builder.constructed_object.floor)
|
||||||
|
self.assertEquals('Big', self.house_builder.constructed_object.size)
|
||||||
|
self.assertEquals('Floor: One | Size: Big', str(self.house_builder.constructed_object))
|
||||||
|
|
||||||
|
self.flat_builder.build('floor')
|
||||||
|
self.flat_builder.build('size')
|
||||||
|
self.assertEquals('More than one', self.flat_builder.constructed_object.floor)
|
||||||
|
self.assertEquals('Small', self.flat_builder.constructed_object.size)
|
||||||
|
self.assertEquals('Floor: More than one | Size: Small', str(self.flat_builder.constructed_object))
|
||||||
|
|
||||||
|
|
||||||
|
class DirectorTestCase(TestCase):
|
||||||
|
"""
|
||||||
|
Unit testing class for the Director class
|
||||||
|
"""
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
Initialize testing data.
|
||||||
|
"""
|
||||||
|
class Building(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.floor = None
|
||||||
|
self.size = None
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'Floor: {0.floor} | Size: {0.size}'.format(self)
|
||||||
|
|
||||||
|
class HomeBuilder(Builder, metaclass=ABCMeta):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(Building())
|
||||||
|
self._register('floor', self._build_floor)
|
||||||
|
self._register('size', self._build_size)
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def _build_floor(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def _build_size(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class HouseBuilder(HomeBuilder):
|
||||||
|
def _build_floor(self):
|
||||||
|
self.constructed_object.floor = 'One'
|
||||||
|
|
||||||
|
def _build_size(self):
|
||||||
|
self.constructed_object.size = 'Big'
|
||||||
|
|
||||||
|
class FlatBuilder(HomeBuilder):
|
||||||
|
def _build_floor(self):
|
||||||
|
self.constructed_object.floor = 'More than one'
|
||||||
|
|
||||||
|
def _build_size(self):
|
||||||
|
self.constructed_object.size = 'Small'
|
||||||
|
|
||||||
|
class HomeDirector(Director):
|
||||||
|
def construct(self):
|
||||||
|
self.builder.build('floor')
|
||||||
|
self.builder.build('size')
|
||||||
|
|
||||||
|
self.house_builder = HouseBuilder()
|
||||||
|
self.flat_builder = FlatBuilder()
|
||||||
|
self.home_director = HomeDirector()
|
||||||
|
|
||||||
|
def test_construct(self):
|
||||||
|
"""
|
||||||
|
Test the construct method.
|
||||||
|
|
||||||
|
@raise AssertionError: If the test fails.
|
||||||
|
"""
|
||||||
|
self.home_director.builder = self.house_builder
|
||||||
|
self.home_director.construct()
|
||||||
|
house = self.home_director.get_constructed_object()
|
||||||
|
self.assertEquals('One', house.floor)
|
||||||
|
self.assertEquals('Big', house.size)
|
||||||
|
self.assertEquals('Floor: One | Size: Big', str(house))
|
||||||
|
|
||||||
|
self.home_director.builder = self.flat_builder
|
||||||
|
self.home_director.construct()
|
||||||
|
flat = self.home_director.get_constructed_object()
|
||||||
|
self.assertEquals('More than one', flat.floor)
|
||||||
|
self.assertEquals('Small', flat.size)
|
||||||
|
self.assertEquals('Floor: More than one | Size: Small', str(flat))
|
||||||
Reference in New Issue
Block a user