Modifiability – What is it? And some Java Solutions

Modifiability: Ability of a system to be able to admit changes due to a new requirement or by detecting an error that needs to be fixed.

The changes can happen due to:

  • New functionalities
  • Change functionalities
  • Remove functionalities
  • Fix issues
  • Strengthen security
  • Improve user experience
  • Incorporate new technologies
  • Incorporate new platforms
  • Incorporate new protocols
  • Incorporate new standards

Questions an architect would make when starting to consider modifiability as a matter to be solved or instead of taking into account:

  • What can be changed? You have to consider all the aspects of the changes the system could have.
  • What is most likely to change? The architect has to decide which changes are most likely to happen and which are not.
  • When will the changes be made and who will be in charge?
  • What is the cost of the change?

To understand modifiability, it is essential that coupling and cohesion be understood.

Coupling and Cohesion

When a change is made in a module, what is the impact on the rest of the system? How strong are the responsibilities each module shares? In order to answer these questions, the concept of coupling arises. Coupling measures how the responsibilities of the modules are related. In other words, when changes are made in a module, coupling measures how the functionalities in the other modules are affected. 

In a module, we handle different functionalities, how much those functionalities are related to each other? That is what cohesion is about.

The approach we are going to follow to help with coupling and cohesion is with the use of tactics.

Which Tactics?

  • Those that divide modules and thus reducing the cost of making changes.
  • Others reduce the coupling between the modules. It generally involves the use of intermediaries between the modules.
  • And in case of low cohesion, those that are to remove responsibilities that are not affected by anticipated changes and put those removed functionalities in the appropriate module (which is refactoring).

Tactics

Tactics lists:

Reduce the Size of a Module

Split Modules -> When a module has many capacities, any change has a high cost. Refining a module into several smaller modules will make it less difficult to make changes in it.

Increase Cohesion

Increase Semantic Coherence -> It is important to maintain semantic consistency in modules, this may involve moving functionalities that do not have the same purpose from one module to another or creating a new module with those functionalities.

Reduce Coupling

Encapsulate -> Add interfaces to a module and that the interaction of the outside with it is through these interfaces.

Using an Intermediary -> When you want to break the dependency between two responsibilities A and B, you put an intermediary in the middle.

Restrict dependencies -> It consists of restricting the visibility of modules with respect to others; an example is a layered architecture and how the different modules interact.

Refactor -> Involves duplicate code removal, overly complex code cleanup, moving code from one place or another, and more.

Abstracting Common Services -> If that functionality is replicated in different places of our code, this technique suggests centralizing the code in one place and in an abstract way.

Defer Binding

Deferred Binding is how functionality can change its features accordingly to the context in which it is running.

Some ways to do that is using parameterization:

Adding parameters in a method -> a method changes its functionality accordingly to the parameters.

Properties files -> we can use files to change how our program behaves, for example connections to other components or modules, also change starting variables and more.

Java Solutions

Already presented the different tactics to improve modifiability, now we will introduce how some of them can be implemented in java.

Split Modules

Java gives you the freedom to organize the classes in namespaces called packages, the elements that are in them have some visibility to each other, and also have common functionality. JARS also allows you to group packages given a common functionality.

Using packages, we can create modules that organize the code in the desired way.

Encapsulate

Java allows you to use interfaces as a way to hide the details of the implementation. Mainly one use may be to separate the implementation of a service from which consumes it by providing only one interface.

We mentioned above that using packages makes it easy to encapsulate as well as the access modifiers, the ability to bundle packages with JARS and design patterns that expose an interface and hide the implementation. Some of those patterns are:

Pattern Adapter

Pattern Iterator (Java has a standard library considering that pattern)

Pattern Facade

Using an Intermediary

In java, there are several solutions and examples to break the dependency between consumer and producer. Dependency injection and solutions for publish-subscribe are some of them.

The facade pattern is widely used to help with the separation of dependencies between responsibilities. Some frameworks make things easier with dependency injection and control inversion favoring encapsulation and decoupling. Spring is one of those frameworks that collaborates with the wiring of our system, dependency injection, and various other things.

For publish-subscribe problems, there are several solutions like ActiveMQ, RabbitMQ, or Kafka that can be implemented with java.

Restrict dependencies

As previously explained, this tactic is commonly seen in layered architectures, where the system is separated into specific functionalities.

Spring Framework is commonly used for the creation of layered Architectures because it helps with the configuration, dependency injection, connection to the database, exposing services, and more.

Refactor

There are several techniques to refactor code, below some of them:

  • Rename
  • Move class
  • Extract Method: For example, when a very long method is divided into smaller methods, we extract methods from its code.
  • Extract Superclass: when we centralize the common code in an abstract class and the specific code in classes that implements it (Polymorphism).
  • Replace Conditionals with Polymorphism.

Clearly, Java is a very diverse language with lots of solutions. Here only some solutions were presented (in other words, they are not the only ones) to improve modifiability.

Bibliography:

You may also like

Spring Boot

Spring Boot 1 to Spring Boot 2

How to Build Custom Java Annotations

Reactive Microservices

Testing Reactive Microservices With Spring-Webflux

Menu