The super simple explaination: “on this, do this”
Functions are added to a “callback list” that happen when an event is triggered
Events are sort of a variation of the observer pattern
Observer:
It’s not about calling a function, it’s about sending a message…
Observer/events about “something alerts callbacks that something changed/happened”
Aspects are “cross-cutting concerns”
Things that weave their way throughout the program:
When refactoring code, you might accidentally stumble upon this.
This code has all of these together:
“Logging” was a “cross-cutting concern”
Aspects
“Logging” was spread throughout the entire codebase
Let’s not care about calling it, at first.
class LogFileAspect(HaroldAspect):
def __init__(self, logfn):
self.logfile = open(logfn, "a")
def on_play(self, varID, uid, song):
# I know...legacy reasons. I'll switch to csv module soon
print(time.strftime('%Y/%m/%d %H:%M:%S,{0},{1},{2}'
.format(varID, uid, song)), file=self.logfile)
def on_terminate(self):
self.logfile.close()
GPIO is a nice one
Different circumstances trigger different GPIO reactions:
class GPIOAspect(HaroldAspect):
def __init__(self, *pins):
self.pins = pins
GPIO.setmode(GPIO.BOARD)
for pin in pins:
GPIO.setup(pin, GPIO.OUT)
GPIO.output(pin, True)
def setPins(self, val):
for pin in self.pins:
GPIO.output(pin, val)
def on_play(self, varID, uid, song):
self.setPins(False)
def on_stop(self):
self.setPins(True)
Notice the on_play
functions:
class LogFileAspect(HaroldAspect):
# ...
def on_play(self, varID, uid, song):
# I know...legacy reasons. I'll switch to csv module soon
print(time.strftime('%Y/%m/%d %H:%M:%S,{0},{1},{2}'
.format(varID, uid, song)), file=self.logfile)
# ...
class GPIOAspect(HaroldAspect):
# ...
def on_play(self, varID, uid, song):
self.setPins(False)
Both happen at the “same time”!
We have cross-cutting concerns located in their own aspect classes
Let’s make a function to call them:
The two were put together pretty seamlessly:
template method
design pattern)
The original code was designed as “what’s architecture?”
Code for each subsystem (GPIO, logging, etc) is in one place
Code triggering parts of each subsystem is in one place:
self.trigger
is a “call all aspects here”before/after/around
arbitrary calls
around
before
calls for lambda:None
around
pointcuts