How to avoid class coupling when the specs insist on it

architecture, decoupling, design-patterns, python

Solution

A good way to decouple classes is to use events.

So what you need to do is to "fire" events when something happens. Example: Send an event "session created" when the `SessionManager` could create a session. Make the `DhcpServer` listen for that event and prepare a lease when it receives it.

Now all you need is a third class which creates the other two and configures the event listeners.

The beauty of this solution that it keeps everything simple. When you write unit tests, you will always only need one of the classes because all you need is to check whether the correct event has been fired.

Problem

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 if `SessionManager` forbids that (e.g. an error occurred while creating a session) - `SessionManager` must start a session upon creation of a new lease by `DhcpServer` 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 if `SessionManager` 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?

Original source