I have two coupled classes DhcpServer
and SessionManager
. I got the following requirements in my specs that led to that coupling:
DhcpServer
must not issue an IP address lease ifSessionManager
forbids that (e.g. an error occurred while creating a session)SessionManager
must start a session upon creation of a new lease byDhcpServer
and destroy a session as soon as that lease expires or gets released explicitly by a client- On the other hand
DhcpServer
must destroy the lease ifSessionManager
stopped a corresponding session (e.g. by sysadmin's request)
At first it was tempting to put all the code into a single class. But the responsibilities were distinct, so I split them into two and created two interfaces:
class ISessionObserver(object):
def onSessionStart(**kwargs): pass
def onSessionStop(**kwargs): pass
class IDhcpObserver(object):
def onBeforeLeaseCreate(**kwargs):
"""
return False to cancel lease creation
"""
pass
def onLeaseCreate(**kwargs): pass
def onLeaseDestroy(**kwargs): pass
Then I implemented IDhcpObserver
in SessionManager
and ISessionObserver
in DhcpServer
. And that led to coupling. Even though the classes do not depend on each other directly they do depend on the interfaces declared in each other's packages.
Later I want to add another protocol for session initiation leaving SessionManager
's logic intact. I don't want it to implement IAnotherProtocolObserver
as well.
Also DHCP server as such has nothing to do with my notion of session. And since there's no DHCP protocol implementation for Twisted (which I'm using) I wanted to release it as a separate project that has no dependencies neither on SessionManager
nor on its package.
How can I satisfy my spec requirements while keeping the code pieces loosely coupled?