KenPowerClassroom / SoftwareEngineeringforGamesI

3 stars 0 forks source link

Cohesion vs Coupling #80

Open kenpower opened 2 years ago

kenpower commented 2 years ago

Increase Cohesion -

Cohesion describes interrelatedness inside a unit.

A unit (class or function) has high cohesion when all of its elements (methods or lines of code) are working towards a single goal. A unit has low cohesion if it has too many responsibilities or have elements not working together.

Cohesion is the degree to which the elements inside a module belong together. A module could be a class or a package or even a microservice. Simply put, it means “the code that changes together, stays together”.

A module with high cohesion contains elements that are tightly related to each other and united in their purpose. For example, all the methods within a "Enemy" class should represent the enemy behavior (not drawing or collision detection).

A module is said to have low cohesion if it contains unrelated elements. For example, a "Enemy" class containing a method on how to check for colliding rectangles. Enemy class can be responsible for storing the position and size of the Enemy but not for detecting collisions:

Topics which help improve cohesion

  1. Humble Object
  2. SRP - Each unit has a single responsibility
  3. ISP - Each interface has a single responsibility
  4. single public member - good
  5. long method smell - bad

Advantages to High Cohesion

Cohesive code is easier to:

Modules with a single, well-defined purpose are easy to understand and much more readable. The name of such modules clearly announces their purpose and such modules do just that. It helps the reader to build an intuition about what the module is doing without reading every line in the module. Also, there are no surprises while reading the code of such modules.

For example, it would be confusing to the reader if the Enemy class contains the code for rendering sprites.

It is easier to make code changes since all the related code is within the module. For example, if a developer has to make changes to the Enemy behavior, they can jump to the Enemy class and make all the changes in one class. This will keep the area of impact limited. Compare this to design where Enemy behavior is spread across multiple classes and each has to be changed to achieve the desired change.

It is easier to test the code. Since such modules do not depend on other modules for their purpose, they are easy to unit test. Also, it is easy to build and deploy the changes when they are limited to a single module.

Changes to such modules are less prone to bugs. It is easier to make mistakes when you are making changes across modules.

Finally, they are reusable. Since such modules perform single responsibility, they tend to be used wherever there is such a need.

Reduce Coupling

Coupling is the degree of interdependence between software modules. A module could be a class or a library (SFML::Graphics). Effectively, the coupling is about how changing one thing required change in another.

A collection of units units (class or function) has high coupling when the units are tightly interconnected and dependent. A change to one unit required changing other units. We want units to be as independent as possible, we aim for low coupling.

For example an Enemy class might contain an array of Bullets that the enemy has fired. This has coupled the Enemy to the Bullet class. If we want to change the type of bullet fired we might need to change the Enemy class. If a bullet needs to disappear we need to interact with the Enemy object. This is needless coupling.

Modules with low coupling among them work mostly independently of each other.

Advantages of Low Coupling

Loosely coupled modules are easier to develop and maintain. Since they are independent of each other, we can develop and test them in parallel. Also, they can be modified and updated without affecting each other. We can independently build and deploy such modules, significantly reducing the deployment time.

Topics which help improve coupling

Encapsulation DIP OCP Law of Demeter Polymorphisim Observer Pattern

image

image

Challenges

Overemphasizing One Principle Over the Other

Pitfall: Focusing too much on achieving high cohesion within modules may lead to overlooking the importance of low coupling between modules, or vice versa. Reality: Both high cohesion and low coupling are equally important and should be balanced. Overemphasis on one can lead to neglect of the other, diminishing the overall design quality.

Misunderstanding Cohesion as Simply Having Less Code

Pitfall: Equating high cohesion with having minimal code in a module. Reality: High cohesion means that all the module's functionalities are strongly related and focused on a single purpose, not necessarily that the module contains less code.

Confusing Low Coupling with No Coupling

Pitfall: Assuming that the goal is to eliminate coupling entirely. Reality: Coupling is unavoidable in most software systems. The objective is to minimize and manage coupling, not eliminate it, ensuring that modules interact through well-defined interfaces and dependencies are logical and necessary.

Over-Modularization in Pursuit of Cohesion

Pitfall: Breaking down the system into too many small modules in an attempt to achieve high cohesion, leading to an excessive number of modules. Reality: Over-modularization can make the system more complex and harder to navigate. It's crucial to find the right balance between having a focused module and maintaining a manageable number of modules.

Underestimating the Complexity of Reducing Coupling

Pitfall: Believing that reducing coupling is simply about reducing direct interactions between modules. Reality: Achieving low coupling often involves sophisticated design patterns and principles, such as dependency inversion and interface segregation. It requires careful planning and understanding of the system's architecture.

Ignoring the Evolution of Software Over Time

Pitfall: Designing for high cohesion and low coupling without considering future changes and extensions to the software. Reality: Software requirements and functionalities evolve over time. A design that initially seems well-cohesive and loosely coupled can become less so as new features are added. Continuous refactoring and design adjustment are necessary to maintain these qualities.

Equating Physical Separation with Low Coupling

Pitfall: Assuming that simply separating code into different files, classes, or packages automatically leads to low coupling. Reality: Low coupling is about the degree of interdependence between modules, not their physical separation. Modules can be in the same file and still be loosely coupled if they interact through well-defined interfaces and do not directly depend on each other's internal workings.