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:

Pyc. 50 Two example classes to illustrate instance decoration
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:

Pyc. 51 Some activities when working with observer interface methods
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

  1. instantiates the native classes (1), then

  2. decorates the relevant instance methods (2), and then

  3. 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.


◄ prev

up

next ►

Legal Notice

Privacy Policy

Cookie Consent

Sphinx 7.2.6 & Alabaster 0.7.12

© Copyright 2020-, Martin Abel, eVation. All Rights Reserved.