Becoming the Best Software Engineer: Pythonic Pathological Coupling

Spread the love

In this blog post, you will learn what pathological coupling is, also known as content coupling, and why it is important. Also, you will see code that demonstrates pathological coupling, and you will learn how to avoid pathological coupling in your code so that your code is incredible.

This is the sixth blog post in a series of posts on Pythonic coupling. The previous blog post was on Becoming the Best Software Engineer: Pythonic Common Coupling.

What Is Pathological Coupling and Why Is It Important?

According to Ingeno, pathological coupling is the highest and worst type of coupling where one module directly accesses another module’s internal or private data (2018). It is important to be aware of pathological coupling because it can make testing and maintaining the code more complicated. Being aware of pathological coupling enables you to avoid it in your own code, and your colleagues and future self will thank you for doing so.

A Demonstration of Pathological Coupling

The following is the code for an “OddNumber” object in the number.py module, which is an object used to store odd numbers:

class OddNumber:
    def __init__(self, odd_number) -> None:

        if odd_number % 2 != 1:
            raise ValueError("The number is not odd.")

        self.odd_number = odd_number

    def __str__(self) -> str:
        return f"OddNumber ({self.odd_number})"

The following is the code for the main.py module:

from number import OddNumber

def main():
    my_odd_number = OddNumber(5)
    my_odd_number.odd_number = 2

    print(my_odd_number) 

if __name__ == "__main__":
    main()

As you can see, the main.py module is directly manipulating the internal data of the “OddNumber” by setting its instance variable “odd_number” to two, which breaks the expectations of what an “OddNumber” object represents since two is not an odd number.

How Do You Avoid Pathological Coupling?

By using properties in Python, we can avoid pathological coupling. The implementation for the “OddNumber” object becomes the following:

class OddNumber:
    def __init__(self, odd_number : int) -> None:

        if odd_number % 2 != 1:
            raise ValueError("The number is not odd.")

        self.__odd_number = odd_number

    @property
    def odd_number(self):
        return self.__odd_number
    
    @odd_number.setter
    def odd_number(self, value : int):
        if value % 2 != 1:
            raise ValueError("The number is not odd.")

        self.__odd_number = value

    def __str__(self) -> str:
        return f"OddNumber ({self.__odd_number})"

If you run the same code in the main.py module, you’ll notice a “ValueError” is raised with the message “The number is not odd.” The “odd_number” attribute now has accessor and mutator methods. When the “odd_number” attribute attempted to be set to two, the “odd_number” method with the “@odd_number.setter” decorator is invoked, which performs a check on whether the number is odd before setting the “odd_number” attribute. By doing so, pathological coupling is avoided, and the expectations that an “OddNumber” object represents a number that is odd is maintained.

In conclusion, you learned what pathological coupling is, and why it is important to be aware of. You also learned how to avoid pathological coupling in your own code so that your code is beautiful.


If you found this blog post to be valuable, then you should consider doing the following:

Subscribe

* indicates required

Intuit Mailchimp


References

Ingeno, J. (2018). Software architect’s handbook. Packt Publishing.