I have this problem in a fun project where I have to tell a class to execute an operation for a certain time period with the start and end times specified (along with the interval ofc).
For the sake of argument consider that class A (the class that has the API that I call) has to call classes B (which calls class C and which in turn calls class D) and E (which in turn calls class F and class G).
A -> B -> C -> D
A -> E -> G
Now class B C and E require the context about the time. I've currently set it up so that class A passes the context to class B and E and they in turn pass the context around as needed.
I'm trying to figure out the best way to solve this requirement without passing context around and as much as I hate it, I was considering using the Highlander (or the Singleton) or the Borg pattern (a variant on the Monostate). I just wanted to know what my options were with regard to these patterns.
Option 1
Use a traditional borg e.g.:
class Borg:
__shared_state = {}
def __init__(self):
self.__dict__ = self.__shared_state
I could simply instantiate this class everywhere and have access to the global state that I want.
Option 2
Another variant on the monostate:
class InheritableBorg:
__time = (start, end, interval)
def __init__(self):
pass
@property
def time(self):
return __time
@time.setter
def time(self, time):
__time = time
This in theory would allow me to simply extend by doing:
class X(InheritableBorg):
pass
and to extend I would just do:
class NewInheritableBorg(InheritableBorg):
__time = (0, 0, 'd')
Then in theory I could leverage multiple inheritance and then would be able to get access to multiple borgs in one go e.g.:
class X(InheritableBorg1, InheritableBorg2):
pass
I could even selectively override stuff as well.
Option 3
Use a wrapped nested function as a class decorator/ wrapper if possible. However, I could only use this once and would need to pass the function handle around. This is based offa a mix of the proxy/delegation idea.
This is not concrete in my head but in essence something like:
def timer(time)
def class_wrapper(class_to_wrap):
class Wrapper(class_to_wrap):
def __init__(self, *a, **kw):
super().__init__(*a, **kw)
def get_time(self):
return time
return Wrapper
return class_wrapper
@timer(time)
class A_that_needs_time_information:
pass
I think that might work... BUT I still need to pass the function handle.
Summary
All of these are possible solutions, and I'm leaning towards the multiple inheritance Borg pattern (though the class wrapper is cool).
-
The regular Borg pattern has to be instantiated soo many times that it seems like too much overhead just to store one set of values.
-
The Borg mixin is instantiated as many times as the class is instantiated. I don't see how it would be any harder to test.
-
The wrapper is ultra generic and would be relatively easy to test. Theoretically since its a function closure I should be able to mutate stuff but then it essentially becomes a singleton (which just seems too complicated in which case I might as well just use the regular singleton pattern). Also, passing the function handle around rather defeats the purpose.
Barring these three ways, are there any other ways of doing this? Any better ways (the Borg doesn't seem easily testable since no DI)? Are there any drawbacks that I seem to have missed?
Alternatively I could just stick with what I have now... passing the time around as required. It satisfies the loose coupling requirement and DI best practices... but its just soo cumbersome. There has to be a better way!
Aucun commentaire:
Enregistrer un commentaire