kkwiatkowski.dev

Hey, I'm Kamil. Welcome to my private place on the World Wide Web. You can find here entries, mostly about programming, technology, and web development, but sometimes also about life. Make yourself at home.

Coupling and cohesion

July 25, 2023, 1:02 p.m.

These two terms slip my mind very often and then I have to remind myself what they mean, what role they play, and how they are different from each other (from what I have seen on the internet, a lot of people, especially junior developers have trouble remembering them.). I read somewhere that these terms refer to object-oriented programming but I think, that not only there they are useful.

So Maybe that's a good idea to write a few words about how I look at it. In the beginning, I want to add, I'm an amateur, not a professional coder with 35 years of experience so I'm going to write as an amateur using simple words.

 

Cohesion

Cohesion refers to the responsibilities or purposes of individual parts of code and their consistency. We can imagine, that we have a class that is responsible for a few things or a class which have one specific, defined goal. Let's get out of the programming world for a second. Let's imagine a huge supermarket and shop with alcohol. A supermarket is a place where customers can buy a lot of different things, some small household appliances, fruits, vegetables, bread, frozen pizza, wine, and detergent. In this case, the supermarket is responsible for a few not necessarily related to each other things, whereas a shop with alcohol is going to have 'high cohesion' because it is responsible only for one thing - alcohol.

Of course, we can make mega-high cohesion by making a shop with wine only, or go even further and make ultra mega-high cohesion by making a shop that offers wine from a specific year only. We can go even further narrowing it down to a specific country, and then to a specific grape variety, etc. So the question arises, where it should stop so that is 'good' that is 'correct', so code written by us could be understandable and easy to use?

Unfortunately, I do not have an answer where it should stop. The answer depends on context, on project, and its needs. Trying to get 'high cohesion' we should not get to the moment in which it is become impractical, limiting flexibility, downright disturbing the job, and damn complicated. Moving back to the alcohol shop example with mega-high cohesion, where we have vintage - what is going to happen if next year, we would like to add wines that are 5 years older? It starting to get confusing. The level of cohesion we are talking about depends on individual needs for a given situation / for a given project and it requires no to overdo it like in our example. Ok, let's go back to code.

Talking about code we can ask ourselves - is this function or is this class responsible for one thing? if yes, we say we have high cohesion and it's cool. Staying on the subject of wine, let's do some simple example which is going to have low cohesion. As you can see below, we have a snippet where declared is WineShop class. This class has a few attributes (name, is_open, street, city, zip, number, items) and a few methods (add_item, remove_item, shop_status, process_oder). Well, let's approach this with common sense, why there is low cohesion?

code showing low cohesion

1.We have data associated with an address. On the one hand, ok, we have a class which should represent shop with wine, it's good to have an address there right? But what if we would like to expand our business with another shop in a different place? Or what if we would like to make an aggregation of every wine shop from the whole country? Then we have a problem, so it is worth collecting all data related to address in one place and creating new class out of them. Next, we can add a field in WineShop class and that field is going to be the Address type.


2. Field 'items[str]' and methods add_item, and remove_item look fine. It is not the case of cohesion, but it's worth keeping items not in an array of strings, but creating separate class e.g. Item, in which we can store data associated with specific item like price, name, and maybe quantity. But overall it looks okay, we have WineShop which has wines, so we can add or remove them - everything is correct. Specific goal for the class, so fields and methods are strongly related to this goal.


3. Method process_order does not fit here well. Especially, when it comes to processing an order which a client ordered. It comes to my mind now, that if the order represented the order made by the shop, like to wholesaler because they want to replenish their stock, then it would fit in WineShop class. As I planned it at the beginning I had in my mind the customer order, who just wanted to buy one or two bottles for a super romantic supper. In this case, it does not have so much in common with the shop. A different class like 'Order' which would have all data associated with a specific order, seems to be more appropriate for our process_order method.

The fields 'name', 'is_open', and method shop_status are ok in my opinion. They can stay here as they are. Below is the code refactored a little taking into account these 3 points. It is not ideal code, I just want to show more or less what a change from low to high cohesion might look like.

code showing high cohesion

As you can see, functionality associated with order processing has been separated into a class I called 'Order'. The same thing happened with data associated with the address also went to a separate class 'Address'. What I also could do is to keep variable is_open not as a string but make an Enum with two values inside: open and close - that would increase cohesion even more.

I'm not able to give you the algorithm that gives you the answer if a given function, class, or module has low or high cohesion. As I already mentioned, often it depends on the project itself and individual needs. After some time it comes out naturally. It's worth to remember about asking the question "Does this serve one specific function"?. I think it is worth trying to achieve high cohesion in general, our code will be more elastic, easy to expand, and easier to write unit tests to, and reading code like that is more pleasant as well as understanding it.

Coupling

What then is our second concept, i.e. coupling? I guess the simplest definition will be - that it is the degree to which modules are interdependent (here I am not sure whether modules is the right term, should I write libraries or classes?). If the modules are very dependent on each other and a change in one causes the other to stop working as it did before, we say that we have high coupling. However, if two modules are very little dependent on each other and a change in one minimally affects the other, we talk about low coupling.

Before we move on to the piece of code, I just came up with an analogy to our topic, I'm not sure if it's suitable, we'll see. Car assembly line. We have a line and we assemble supercars (maybe I'd better not say what brand). And now if we have a situation that:

- changing the paint on the bumper means we have to change the approach to engine assembly

- or tinting one of the windows requires rebuilding the entire electronic system

- or changing the seat adjustment means that the trunk lid now does not want to close

then we have an example of high coupling and this is because processes or parts are very closely interconnected and influence each other. There is no flexibility in this process. If I want to provide customers with an updated model with new lamps and a sportier steering wheel next year, it suddenly turns out that I have to engage the entire team because the entire car and the entire assembly line have to be modified. Chaaaaaaaos!

futuristic car

Otherwise, we have low coupling. Then the parts are produced more independently. Of course, they still connect in certain places - after all, together they form a whole, i.e. a car! But we will be able to make some changes on the same assembly line, e.g.

- add a spare wheel to the back of the car, you may need to slightly adjust the rear of the car to accommodate this, but this does not mean that we have to make any changes to the engine.

- we can change the color of the car, but this will not mean changes in the onboard computer or changes in the size and material of the armrest.

I hope you know what I mean. Looking for more real-life examples has always helped me, but this approach may not suit everyone. When it comes to coupling, there are different types. I will present the main ones, or those that I know and remember, and describe them very briefly, perhaps also showing a short piece of code. Here we go

1. Common Coupling, although it is also known as Global Coupling - When modules use constant globally available data, it reads or changes that data. Below is just an example

code showing common coupling

2. External Coupling - Using an external API. Sometimes it happens that the API changes and does not take into account backward compatibility, so we may wake up with the information that the fragments of our code that were based on this API no longer work like that before.

3. Content Coupling - that is, one module directly uses and modifies data in another module. In the example below, we see, let's say, two modules, i.e. the Door class and the global open_door function, which directly modifies the data of another module. I hope that I chose the right example to present such a basic example of content coupling.

python code as example of content coupling

4. Data Coupling -two modules using the data of the other one, passing it to each other as a parameter. A bit similar to the example above.

5. Control Coupling - Modules are related by passing control information (e.g. flags, indicators) between modules.

6. Stamp coupling - Modules are related because they convey comprehensive data structures, but only part of this data is used by other modules.

7. Message coupling - Modules are related by exchanging messages or messages between them, for example using interfaces or events.

These are 7 types that I more or less remembered off the top of my head. I once saw in an article that there are many more of them, but I don't want to delve into them, and I don't see the point in memorizing who knows how many of such theoretical views. It is important to simply remember that coupling refers to how dependent the modules are on each other and depending on how they are connected, they have a different name, just like the 7 modules mentioned above. It is also important to remember that when designing software, it is worth striving to minimize connections between modules to increase flexibility and ease of developing and maintaining the code. Try to minimize connections and not remove them completely, because, as I wrote about the assembly line - these elements must touch somewhere to finally create a car, or in our case, a working program that fulfills its purpose.
 

 

Summary

I think we can conclude that these concepts do not have much in common, each of them represents an approach to software architecture. We discussed cohesion, which refers to whether a module has one specific goal and all its components are strongly related to this goal, creating a coherent whole; we also discussed coupling, which refers to how closely the modules are related to each other and how far they are from each other. dependent on each other. A general idea can be drawn from this that it is worth striving for low coupling and high cohesion. A thorough understanding of these concepts will allow developers to create more flexible, scalable systems that are easier to maintain.

That's all for today, thanks for your attention. If something is wrong here and I made some mistakes, remember that there is such a thing as Cunningham's Law :)