Implemented Decorator Pattern.

This commit is contained in:
tylerlaberge
2016-08-20 13:03:30 -04:00
parent 9a6dc2b27c
commit e59f84105b
2 changed files with 113 additions and 21 deletions

View File

@@ -3,31 +3,71 @@ from abc import ABCMeta, abstractmethod
class Decorator(object, metaclass=ABCMeta): class Decorator(object, metaclass=ABCMeta):
"""
Base Decorator class that all decorator classes inherit from.
"""
def __get__(self, instance, owner): def __get__(self, instance, owner):
"""
Override __get__ in order to get the instance of a bound of method call.
"""
return partial(self.__call__, instance) return partial(self.__call__, instance)
@abstractmethod @abstractmethod
def __call__(self, *args, **kwargs): def __call__(self, *args, **kwargs):
"""
All decorators must implement a __call__ method.
"""
pass pass
class DecoratorSimple(Decorator, metaclass=ABCMeta): class DecoratorSimple(Decorator, metaclass=ABCMeta):
"""
A Base Decorator class for decorators with no arguments.
"""
def __init__(self, func): def __init__(self, func):
"""
Initialize a new DecoratorSimple instance.
@param func: The function being decorated.
"""
self.func = func self.func = func
class DecoratorArgs(Decorator, metaclass=ABCMeta): class DecoratorComplex(Decorator, metaclass=ABCMeta):
"""
A Base Decorator class for decorators with arguments.
"""
@abstractmethod @abstractmethod
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"""
Initialize a new DecoratorComplex instance.
@param args: Args for the decorator.
@param kwargs: Keyword args for the decorator.
"""
pass
@abstractmethod
def __call__(self, func, *args, **kwargs):
"""
Concrete DecoratorComplex instances must override the __call__ method.
@param func: The function being decorated.
@param args: Arguments for the decorated function.
@param kwargs: Keyword arguments for the decorated function.
@return:
"""
pass pass
class Wrap(DecoratorSimple): class CallWrapper(DecoratorSimple):
"""
A Decorator for wrapping DecoratorComplex __call__ methods.
"""
def __call__(self, instance, func): def __call__(self, instance, func):
"""
Wrap a concrete DecoratorComplex __call__ method.
"""
def wrapped(*args, **kwargs): def wrapped(*args, **kwargs):
return self.func(instance, func, *args, **kwargs) return self.func(instance, func, *args, **kwargs)

View File

@@ -1,6 +1,6 @@
import time import time
from unittest import TestCase from unittest import TestCase
from pypatterns.structural.decorator import DecoratorSimple, DecoratorArgs, Wrap from pypatterns.structural.decorator import DecoratorSimple, DecoratorComplex, CallWrapper
class DecoratorSimpleTestCase(TestCase): class DecoratorSimpleTestCase(TestCase):
@@ -51,20 +51,20 @@ class DecoratorSimpleTestCase(TestCase):
self.assertAlmostEqual(2.0, slow_function.end, delta=1.0) self.assertAlmostEqual(2.0, slow_function.end, delta=1.0)
class DecoratorArgsTestCase(TestCase): class DecoratorComplexTestCase(TestCase):
""" """
Unit testing class for the DecoratorArgs class. Unit testing class for the DecoratorComplex class.
""" """
def setUp(self): def setUp(self):
""" """
Initialize testing data. Initialize testing data.
""" """
class Alert(DecoratorArgs): class Alert(DecoratorComplex):
def __init__(self, alert_time): def __init__(self, alert_time):
self.alert_time = alert_time self.alert_time = alert_time
@Wrap @CallWrapper
def __call__(self, func, *args, **kwargs): def __call__(self, func, *args, **kwargs):
start = time.time() start = time.time()
return_val = func(*args, **kwargs) return_val = func(*args, **kwargs)
@@ -81,12 +81,20 @@ class DecoratorArgsTestCase(TestCase):
@raise AssertionError: If the test fails. @raise AssertionError: If the test fails.
""" """
@self.alert(1) class SlowClass(object):
def slow_function():
time.sleep(2)
return 'foo'
self.assertEquals(('foo', True), slow_function()) @self.alert(1)
def slow_function_true(self):
time.sleep(2)
return 'foo'
@self.alert(1)
def slow_function_false(self):
return 'bar'
slow_class = SlowClass()
self.assertEquals(('foo', True), slow_class.slow_function_true())
self.assertEquals(('bar', False), slow_class.slow_function_false())
def test_decorate_args(self): def test_decorate_args(self):
""" """
@@ -95,9 +103,53 @@ class DecoratorArgsTestCase(TestCase):
@raise AssertionError: If the test fails. @raise AssertionError: If the test fails.
""" """
@self.alert(1) class SlowClass(object):
def slow_function(n):
time.sleep(n)
return 'foo'
self.assertEquals(('foo', True), slow_function(2)) @self.alert(1)
def slow_function_true(self, n):
time.sleep(n)
return 'foo'
@self.alert(1)
def slow_function_false(self, n):
return n
slow_class = SlowClass()
self.assertEquals(('foo', True), slow_class.slow_function_true(2))
self.assertEquals((10, False), slow_class.slow_function_false(10))
class WrapTestCase(TestCase):
"""
Unit testing class for the CallWrapper decorator class.
"""
def test_wrap(self):
"""
Test the wrap decorator
@raise AssertionError: If the test fails.
"""
class SlowClass(DecoratorComplex):
def __init__(self, sleep_time):
self.sleep_time = sleep_time
self.end_time = None
@CallWrapper
def __call__(self, func, *args, **kwargs):
start_time = time.time()
return_val = func(*args, **kwargs)
time.sleep(self.sleep_time)
end_time = time.time() - start_time
self.end_time = end_time
return return_val, self.sleep_time, self.end_time
@SlowClass(1)
def hello_world(n):
return ['hello world' for _ in range(n)]
return_val, sleeptime, end_time = hello_world(5)
self.assertEquals(1, sleeptime)
self.assertAlmostEquals(1, end_time, delta=1)
self.assertListEqual(['hello world' for _ in range(5)], return_val)