So there you are, with a class that has over 100 (200? 500? 1000?) lines of code in Python. Wow, such an impressive achievement. Life with a monster like this can’t be easy.

History of our giant class is irrelevant. What’s important is that our patient is sick and needs a treatment. We’ll cure it, but making a proper diagnosis first is crucial. Then we’ll apply appropriate therapy called refactoring. Hopefully lessons learned this way will save you some nerves in the future and help avoiding mistakes.

Before we take a look at our patient, let me start with telling you what this code is supposed to do. We are building a commercial VOD platform. Access to videos is paid, so we have to implement payments module support. A 3rd party service for payment gateway has been chosen – Respectable (naturally, I made up this one). We still have to write a client code, because Respectable does not provide any SDK. At least it has a REST API. To make this example a bit simpler, let’s say we need only two of its methods – sale for charging customer’s payment card and refund for giving their money back.

We hand these requirements over to the dev team and after a few days we get an implementation. Please read it carefully to better understand next steps I made:

There are few different problems with this class, but let’s start with the most noticeable one – its size. RespectableApi is so big because it combines several responsibilites – communication with external API through requests library, parsing responses, handling errors. It exposes two public methods – sale and refund. The rest of code in this class is just for these methods, which are very similar by the way.

Mixins!

Let’s try splitting the class. You might think of an approach with mixins. First, we take all the code that is used by both sale and refund and create some fuzzy Base class:

Then we take remaining bits of code and create two smaller classes (mixins) for each API method:

And finally we tie all three together to be able to use it just like we did before:

So now we have four significantly smaller classes with a bit of code separation. All sale specific code now belongs to SaleRespectableApiMethodMixin class. So is true with refund and its RefundRespectableApiMethodMixin.

Is that a good code we can live with?

There is a designated area in hell for people creating such abominations. It does not really help with coupling between API methods and transport mechanism which is REST in this case. Imagine how it would look like to add support for more API methods. A nightmare.

In fact, reading this code to fully understand what RespectableApi is requires a lot of jumping through all classes involved. They will be probably scattered through few files. Having them in just one .py file makes situation even worse. Cognition load is not reduced – it is increased by extra classes. At least changing anything in sale specific code won’t break refunds and vice versa.

Usually this approach is the worst idea you might have.

Inheritance and standalone API commands classes

A slightly better approach is to use BaseRespectableApi as a base class for our API methods, so we’ll get:

The good part is that clients of Respectable API still have to use just one class (SaleRespectableApiMethod or RefundRespectableApiMethod) to achieve what they need. Code of sales is completely separated from refunds. But again, this solution does not reduce complexity of individualy API method. It still relies on a base class and we have to read it to fully understand what a single ApiMethod class does when it’s invoked. Inheritance introduces a very rigid dependency between classes.

Separation by concerns

Wait, there must be a better way. There is one. Let’s split code by concerns it should care about.

RespectableAPI – main class that will handle REST communication and handling errors. We could also move transport details to another class, but this could obfuscate this example (and obviously there is no visible value in this since this is the only transport mechanism Respectable handles). It will have one public method execute, that will accept…

RespectableAPIMethod – abstract base class for all API methods. It does not use (neither know anything about) RespectableAPI. The latter uses methods defined in base class to prepare params and parse response.

SaleRespectableAPIMethod, RefundRespectableAPIMethod – concrete classes inheriting from RespectableAPIMethod.

Let’s take a look at code:

As you can see, execute method argument should be an object with two methods: get_params and parse_response. Base class for API methods looks like this:

Implementation of refund (I omit sale, because it won’t contribute much at this stage):

Notice that we no longer need to pass extra arguments to execute method, because they are saved in RefundRespectableApiMethod instance.

To me, this is the most elegant solution of all three presented in this article. API methods classes are small and can be very easily unit tested. Our original class, RespectableApi, is now simpler.

Adding new API methods should not be any pain in the neck.

The downside is that now client has to use two classes to achieve what they want instead of one. We can still be nice though if we hide this little inconvenience using Facade design pattern:

 

 

That’s all folks. Looking forward to hearing from you about other patterns/approaches to decoupling code.

DISCLAIMER: Code in this post was hardly tested, because it was not taken from a piece of working software. My intention was to present an idea, not to provide working examples.