Instance Decoration¶
The classic way to exchange information between objects with the observer
pattern is through the active use of the register
, dispatch
, and
unregister
interface methods that an observable exposes. Individual
information can be given to the right recipients at relevant places in the
code. For this, classes are not decorated and dynamic decoration
Pyc. 1 comes into play. Very often dynamic decoration is used
in connection with getter, setter, property constructions since data changes
take place meaningfully over these methods.
Consider the following two example classes:
class Note: # Observer without decoration!
def info(self, thing):
print(f"Note.info: val = {thing.a}")
class Thing: # Observable without decoration!
def __init__(self, a=0): # Initializer, define variable 'a'
self._a = a
def inc(self): # Instance method, modifying 'a'
self._a += 1
def get_a(self): # Getter, setter, property,
return self._a # modifying variable 'a'
def set_a(self, value):
self._a = value
a = property(get_a, set_a)
Initially, all these classes are undecorated and typical actions might be:
from decoratory.observer import Observable
from decoratory.basic import F
# (1) Setup instances
nti = Note() # Note instance
tgi = Thing() # Thing instance
# (2) Dynamic decoration of some methods: Late binding
tgi.inc = Observable(tgi.inc) # Late method decoration
Thing.set_a = Observable(Thing.set_a) # Late property decoration
Thing.a = property(Thing.get_a, Thing.set_a)
# (3) Register the observer (Note) with the observable (Thing)
tgi.inc.observable.register(F(nti.info, tgi))
tgi.set_a.observable.register(F(nti.info, thing=tgi))
# Case 1: Change self.a = 0 using inc()
tgi.inc() # Note.info: val = 1
# Case 2: Change self.a = 1 using setter via property
tgi.a = 2 # Note.info: val = 2
# Case 3: Notification from inc() to nti.info() about Thing(3)
tgi.inc.observable.dispatch(nti.info, Thing(3))
# Note.info: val = 3
# Case 4: Notification from set_a() to nti.info() about Thing(4)
tgi.set_a.observable.dispatch(nti.info, Thing(4))
# Note.info: val = 4
# Case 5: Print the current value of tgi.a
print(f"a = {tgi.a}") # a = 2 (no changes, notification)
# Case 6: Print list of all observers
print(tgi.inc.observable.observers(classbased=True))
# ---> {'Note': ['F(info, <__main__.Thing object at ..)']}
print(tgi.set_a.observable.observers(classbased=True))
# ---> {'Note': ['F(info, thing=<__main__.Thing object at ..)']}
# Case 7: Unregister nti.info from tgi
tgi.inc.observable.unregister(nti.info)
print(tgi.inc.observable.observers(classbased=True)) # {}
In contrast to Class Decoration, this Instance Decoration
instantiates the native classes (1), then
decorates the relevant instance methods (2), and then
registers the observers with the associated observables (3).
Of course, observer dispatching can be initiated in response to individual conditions.
This method of instance decoration is certainly the most flexible. However, it bears the risk of losing track of all dependencies.
© Copyright 2020-, Martin Abel, eVation. All Rights Reserved. |