Python Interface Design - Designing Modules Part - 1 | HashedIn

Python Interface Design - Designing Modules Part - 1

Technology - 06 Dec 2016
Harish Thyagarajan

This is the first post in a multi-part series where we will design a reusable python module. You have been given the following requirements:

You are the developer on Blipkart, an e-commerce website. You have been asked to build a reusable SMS module that other developers will use to send SMS’es. Watertel is a telecom company that provides a REST API for sending SMS’es, and your module needs to integrate with Watertel. Your module will be used by other modules such as logistics.py and orders.py.


The first goal for a module should be good Abstraction. Your module sits between client developers on one side, and the API provided by Watertel on the other side. The module’s job is to simplify sending an SMS, and to ensure that changes in Watertel do not affect your client developers.

The essence of abstractions is preserving information that is relevant in a given context, and forgetting information that is irrelevant in that context.

Watertel exposes these concepts – username, password, access_token and expiry, phone number, message and priority. Which of these concepts are relevant to client developers?

Client developers don’t care about username and password. They don’t want to worry about expiry either. These are things our module should handle. They only care about phone number and message – “take this message, and send it to this phone number”.

Interface Design

Since developers only care about phone_number and message, our interface design is very clear – we need a send_sms(phone_number, message) function. Everything else is irrelevant.

Now, looking from an implementation perspective. We need the url, the username and password. We also need to manage the access token and handle it’s expiry. How will we get this information? Let’s carve out an interface design for our solution.

In general, if you need some information, or if you depend on other classes – you must get it via your constructor. Your class must not go about trying to find that information on it’s own (say from django settings).

Here’s how our interface looks like:

Using our Class in Client Code

Next, in order to use our module, our clients need an object of SmsClient. But in order to create an object, they’d need username and password. Our first attempt would be something like this:

There are two problems with this:

  1. First, orders.py shouldn’t care how SmsClient objects are constructed. If we later need additional parameters in the constructor, we would have to change orders.py, and all classes that use SmsClient. Also, if we decide to read our settings from some place else (say for test cases), then orders.py would have to be modified.

  2. The second problem is that we would have to create SmsClient objects everywhere we want to use it. This is wasteful, and also leads to code duplication.

The solution is to create SmsClient object in sms.py module [see footnote 1]. Then orders.py and logistics.py can directly import the object from sms. Here is how it looks.

and in orders.py:

Summary

Before you start implementing, think how your clients will use your module. It’s a good idea to actually write down the client code (i.e. code in orders.py and logistics.py) before you start implementing your module.

Now that we have our interface defined, we can start implementing our module.

Footnotes

In this exercise, we are building a reusable module as part of a larger application. Therefore, it is okay for our module to import django.settings and to construct and object of SmsClient inside the module. However, if we were to make a truly reusable module, that would be installed via pip, then we shouldn’t be constructing the object. It’d then be the client’s responsibility to construct the object and put it in a place from where other modules can get it.

Free tag for commerce

E-book on Digital Business Transformation