This chapter is a bit of a side-bar conversation, but it’s an important one. If you’re still fairly new to programming, it’s time we introduce the concept of WET and DRY code as well as knowing when to take the time to refactor (clean-up) your code.
In the previous chapter, we wrote a simple low-level REST API Adapter. We hadn’t even passed 30 lines of code when it started to become apparent that our code was getting sloppy.
Here’s all 30 lines of our RestAdapter
class so far:
import requests
import requests.packages
from typing import List, Dict
class RestAdapter:
def init(self, hostname: str, api_key: str = '', ver: str = 'v1', ssl_verify: bool = True):
self.url = f"https://{hostname}/{ver}"
self._api_key = api_key
self._ssl_verify = ssl_verify
if not ssl_verify:
# noinspection PyUnresolvedReferences
requests.packages.urllib3.disable_warnings()
def get(self, endpoint: str, ep_params: Dict = None) -> List[Dict]:
full_url = self.url + endpoint
headers = {'x-api-key': self._api_key}
response = requests.get(url=full_url, verify=self._ssl_verify, headers=headers, params=ep_params)
data_out = response.json()
if response.status_code >= 200 and response.status_code <= 299: # OK
return data_out
raise Exception(data_out["message"])
def post(self, endpoint: str, ep_params: Dict = None, data: Dict = None):
full_url = self.url + endpoint
headers = {'x-api-key': self._api_key}
response = requests.post(url=full_url, verify=self._ssl_verify, headers=headers, params=ep_params, json=data)
data_out = response.json()
if response.status_code >= 200 and response.status_code <= 299: # OK
return
raise Exception(data_out["message"])
In the software engineering world, there is a concept of WET code and DRY code.
DRY stands for “Don’t Repeat Yourself”, while WET stands for “We Enjoy Typing” or “Write Everything Twice” (or Thrice!)
Good code is generally pretty DRY. Never repeating itself.
Sloppy code is often pretty WET. The same lines (or variations of the same lines) are often repeated over and over in different places.
Why is repeating code bad? Imagine having the same block of code repeated in 3 different sections of your program. Then imagine changing the logic to that block of code because you wanted to fix a bug or improve performance. Well, now you have to find all the instances of that code block and update it in all 3 locations.
When you first write out new code, you’ll often have WET code. And that’s both common and fairly normal. What is bad is if you decide not to refactor and leave it WET.
By choosing to Refactor your code, you can clean it up, tighten things in places and DRY it out. 😊 So let’s refactor our get
and post
methods.
What we’re going to do is create a more generic (and private) method called _do
which can do all the same things as the get
and post
methods. If we look above at both of those methods, we can see that 5 of the 7 lines are exactly the same, so let’s copy those over.
def _do(self, endpoint: str, ep_params: Dict = None, data: Dict = None):
full_url = self.url + endpoint
headers = {'x-api-key': self._api_key}
# some request line here
data_out = response.json()
if response.status_code >= 200 and response.status_code <= 299: # OK
return #something
raise Exception(data_out["message"])
The only major difference between get
and post
are, well, that they’re HTTP GET and HTTP POST commands, so we need to take this into account. And it turns out that even the requests
module has support for this. So let’s add a new parameter to _do
method called http_method
.
def _do(self, http_method: str, endpoint: str, ep_params: Dict = None, data: Dict = None):
And let’s add a new request line:
response = requests.request(method=http_method, url=full_url, verify=self._ssl_verify, headers=headers, params=ep_params, json=data)
So now our _do
method should look like this:
def _do(self, http_method: str, endpoint: str, ep_params: Dict = None, data: Dict = None):
full_url = self.url + endpoint
headers = {'x-api-key': self._api_key}
response = requests.request(method=http_method, url=full_url, verify=self._ssl_verify, headers=headers, params=ep_params, json=data)
data_out = response.json()
if response.status_code >= 200 and response.status_code <= 299: # OK
return data_out
raise Exception(data_out["message"])
Now that we have a private and generic _do
method, we can refactor our get
and post
methods to:
def get(self, endpoint: str, ep_params: Dict = None) -> List[Dict]:
return self._do(http_method='GET', endpoint=endpoint, ep_params=ep_params)
def post(self, endpoint: str, ep_params: Dict = None, data: Dict = None):
return self._do(http_method='POST', endpoint=endpoint, ep_params=ep_params, data=data)
Wow! Super simple and DRY, right? Hey, let’s implement the delete
method, too!
def delete(self, endpoint: str, ep_params: Dict = None, data: Dict = None):
return self._do(http_method='DELETE', endpoint=endpoint, ep_params=ep_params, data=data)
And so here we are now. A basic REST Adapter that has implemented GET, POST, and DELETE in only 30 lines of code!
This leads us to the next topic of Exception handling and Raising our own exceptions in the next chapter… Step 4: Exception Handling and Raising New Exceptions
Source code: https://github.com/PretzelLogix/py-cat-api/tree/03_refactor_rest_adapter
4 replies on “Step 3: Understanding WET code, DRY code, and Refactoring the low-level REST Adapter”
[…] This brings us to a side-bar about WET code, DRY code, and Refactoring in the next chapter… Step 3: Understanding WET code, DRY code, and Refactoring the low-level REST Adapter […]
[…] Inheritance makes code more rigid… less flexible. But it does provide a kind of “contract” on how things can or will be done. And in certain instances, it can make your code DRY-er. (Remember WET vs DRY? If not, feel free to review Chapter 3…) […]
[…] Step 3: Understand the difference between WET code and DRY code, and then Refactor the low-level RES… […]
[…] our last chapter on “WET code, DRY code, and Refactoring the low-level REST Adapter”, we refactored our code into a basic, yet reasonable low-level REST API […]