• About Blog

    What's Blog?

    A blog is a discussion or informational website published on the World Wide Web consisting of discrete, often informal diary-style text entries or posts.

  • About Cauvery Calling

    Cauvery Calling. Action Now!

    Cauvery Calling is a first of its kind campaign, setting the standard for how India’s rivers – the country’s lifelines – can be revitalized.

  • About Quinbay Publications

    Quinbay Publication

    We follow our passion for digital innovation. Our high performing team comprising of talented and committed engineers are building the future of business tech.

Showing posts with label Design Principles. Show all posts
Showing posts with label Design Principles. Show all posts

Saturday, November 5, 2022

Understanding Spring AOP

Understanding Spring AOP
Photo by Glenn Carstens Peters

What is Spring AOP?

Spring AOP enables Aspect-Oriented Programming in spring applications. It provides the way to dynamically add the cross-cutting concerns(logging, security, transaction management, auditing, i18n etc.) before, after or around the actual logic using simple pluggable configurations. It makes easy to maintain code in the present and future as well. You can add/remove concerns without recompiling complete source code simply by changing configuration files (if you are applying aspects using XML configuration).


What is advice, joinpoint or pointcut?

  • An important term in AOP is advice. It is the action taken by an aspect at a particular join-point. 
  • Joinpoint is a point of execution of the program, such as the execution of a method or the handling of an exception. In Spring AOP, a joinpoint always represents a method execution.
  • Pointcut is a predicate or expression that matches join points.
  • Advice is associated with a pointcut expression and runs at any join point matched by the pointcut.
  • Spring uses the AspectJ pointcut expression language by default.


Pointcut

Pointcut determines the join point of interest and in the code it appears as pointcut expression. It works similarly to regular expressions, using the special syntax it matches methods with advices. Please note, that Spring AOP supports only those classes that are defined as Spring beans/component otherwise they won’t be available. Here is a pointcut expression general syntax (those parts that are in red are mandatory) with some examples:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)

The following examples show some common pointcut expressions:

The execution of any public method, First * means that it will match any return type, and *(..) means that the expression will match any method, no matter how much arguments it contains.
    execution(public * *(..))

The execution of any method with a name that begins with set:
    execution(* set*(..))

The execution of any method defined by the OrderService interface/class:
    execution(* com.bhargav.service.AccountService.*(..))

The execution of any method defined in the service package or one of its sub-packages:
    execution(* com.bhargav.service..*.*(..))

This will be matched only for those methods in the DemoClass, which has int as a first parameter, return type as int and method should be public:
    execution(public int DemoClass.*(int, ..))

Any join point (method execution only in Spring AOP) within the service package:
    within(com.bhargav.service.*)

Advices

Spring AOP includes the following types of advice:

@Before: 
Advice that runs before a join point but that does not have the ability to prevent execution flow proceeding to the join point (unless it throws an exception).

@After: 
Advice to be run regardless of the means by which a join point exits (normal or exceptional return).

@AfterReturning: 
Advice to be run after a join point completes normally (for example, if a method returns without throwing an exception).

@AfterThrowing: 
Advice to be run if a method exits by throwing an exception.

@Around:
Advice that surrounds a join point such as a method invocation. This is the most powerful kind of advice. Around advice can perform custom behaviour before and after the method invocation. It is also responsible for choosing whether to proceed to the join point or to shortcut the advised method execution by returning its own return value or throwing an exception.


References:

Monday, July 25, 2022

Understand Code Refactoring Techniques to Improve your Code Quality

Code Refactoring Techniques
Photo by Danial Igdery

Now a days, agile teams are under tremendous pressure to write code faster with enhanced functionality in short time. There would be some or the other functionality added at the last moment or just before the release. As engineers are under pressure, the functionality gets implemented in a sloppy manner, which may technically work but may lead to dirty code.

Dirty code usually results from a developer’s inexperience, shortcuts taken to meet increasingly tight deadlines, poor management of the project, several different developers working on a project over time, or some combination of all of the above.

Bad code comes at a price, and writing good code isn’t that complicated. Let's understand what's code refactoring.

Code Refactoring

In computer programming and software design, code refactoring is the process of restructuring existing computer code—changing the factoring—without changing its external behavior. Refactoring is intended to improve the design, structure, and/or implementation of the software (its non-functional attributes), while preserving its functionality. 

Potential advantages of refactoring may include improved code readability and reduced complexity; these can improve the source code's maintainability and create a simpler, cleaner, or more expressive internal architecture or object model to improve extensibility. 

Another potential goal for refactoring is improved performance; software engineers face an ongoing challenge to write programs that perform faster or use less memory.

Code refactoring helps to change this dirty code into clean code and it helps to make it easier to extend the code and add new features easily in the future. Also, helps to improve the more objective attributes of code such as code length, code duplication, and coupling and cohesion, all of which correlate with ease of code maintenance and your code will use less memory and perform better and faster.

How to Perform Code Refactoring?

Now that you know the answer to the question of what is code refactoring, and you know many of its potential benefits, how exactly do you refactor code?

There are many approaches and techniques to refactor the code. Let’s discuss some popular ones.

Red-Green Refactor

Red-Green is the most popular and widely used code refactoring technique in the Agile software development process. This technique follows the test-first approach to design and implementation, this lays the foundation for all forms of refactoring.

Red - The first step starts with writing the failing red-test. You stop and check what needs to be developed.

Green - In the second step, you write the simplest enough code and get the development pass green testing.

Refactor - Find ways to improve the code and implement those improvements, without adding new functionality.

Refactoring by Abstraction

This technique is mostly used by developers when there is a need to do a large amount of refactoring. Mainly we use this technique to reduce the redundancy (duplication) in our code. This involves class inheritances, hierarchy, creating new classes and interfaces, extraction, replacing inheritance with the delegation, and vice versa.

Pull-Up Method - It pulls code parts into a superclass and helps in the elimination of code duplication.

Push-Down Method - It takes the code part from a superclass and moves it down into the subclasses.

Refactoring by abstraction allows you to make big changes to large chunks of code gradually. In this way, you can still release the system regularly, even with the change still in progress.

Composing Method

Code that is too long is difficult to understand and difficult to implement. The composing method is a code refactoring approach that helps to streamline code and remove any code duplications. This is done through extraction and inline techniques.

Extraction - We break the code into smaller chunks to find and extract fragmentation. After that, we create separate methods for these chunks, and then it is replaced with a call to this new method. Extraction involves class, interface, and local variables.

Inline - Refactoring also helps to create simpler, more streamlined code. It helps to remove unnecessary methods within the code and replaces them with the content of the method. After that, we delete the method from our program.

Simplifying Methods

As legacy code gets older and older, it tends to become more polluted and complex. In this sense, simplifying methods help to simplify the logic. These methods include adjusting the interaction between different classes, along with adding a new parameter or removing and replacing certain parameters with explicit methods.

Simplifying Conditional Expressions - Conditional statement in programming becomes more logical and complicated over time. You need to simplify the logic in your code to understand the whole program. There are so many ways to refactor the code and simplify the logic. Some of them are: consolidate conditional expression and duplicate conditional fragments, decompose conditional, replace conditional with polymorphism, remove control flag, replace nested conditional with guard clauses, etc.

Simplifying Method Calls - In this approach, we make method calls simpler and easier to understand. We work on the interaction between classes, and we simplify the interfaces for them. Examples are: adding, removing, and introducing new parameters, replacing the parameter with the explicit method and method call, parameterize method, making a separate query from modifier, preserve the whole object, remove setting method, etc.

Extract Method

The extract method is one of the techniques for code refactoring that helps to decrease complexity, while also increasing the overall readability of the code.

When you find that a class has so many responsibilities and too much thing is going on or when you find that a class is unnecessary and doing nothing in an application, you can move the code from this class to another class and remove it completely from the existing class. It involves the moving of a fragment or block of code from its existing method and into a newly created method, which is clearly named in order to explain its function.

Conclusion

Engineers are the only ones responsible for writing good and quality code. We should all make it a habit to write good code from the very beginning. Writing clean code isn’t complicated and doing so will help both you and your colleagues. A clean and well-organized code is always easy to change, easy to understand, and easy to maintain.

Clean Code: A Handbook of Agile Software Craftsmanship by Robert C. Martin

Even bad code can function. Every year, countless hours and significant resources are lost because of poorly written code but it doesn't have to be that way.

Refactoring: Improving the Design of Existing Code by Martin Fowler

Any fool can write code that a computer can understand. Good programmers write code that humans can understand.


Quick Tips

  • Always perform code refactoring in small chunks by making your code slightly better and leaves the application in a working state. Run jUnit tests after making small changes in the refactoring process. Without running these tests, you create a risk of introducing new bugs.

  • Do not create any new features or functionality during the refactoring process. You should refactor the code before adding any updates or new features into your existing code.

  • Refactoring process always results in complete regression, don't forget to involve your QA team in the process.

Wednesday, August 4, 2021

Retry or Stability Pattern

Retry or Stability Pattern Image
Photo by Brett Jordan

One of the key characteristic of microservices architecture is inter-service communication. We can split a monolithic application into multiple smaller applications called microservices. Each microservice is responsible for a single feature or domain and can be deployed, scaled, and maintained independently.

Since microservices are distributed in nature, various things can go wrong at any point of time. The network over which we access other services or services themselves can fail. There can be intermittent network connectivity errors or firewall issues. Individual services can fail due to service unavailability, coding issue, out of memory errors, deployment failure, hardware failure and etc., to make our services resilient to these failures, we adopt the retry pattern which is also known as stability pattern.

Retry Pattern

The idea behind the retry pattern is quite simple. If service A makes a call to service B and receives an unexpected response for a request, then service A will send the same request to service B again hoping to get an expected response.

Retry Pattern Image
Retry Pattern Representation

There are several retry strategies that can be applied depending on the failure type or nature of the requirements.

Immediate Retry

This strategy is the basic one. In this approach, calling service handles the unexpected failure and immediately makes the request again. This strategy can be useful for unusual failures that occur intermittently. The chances of success are high by just retrying in these cases.

Retry After Delay

In this strategy, we introduce a delay before retrying service call again, hoping that the cause of the fault would have been rectified. Retry after delay is an appropriate strategy when a request timeout occurs due to busy or failures or network-related issue.

Sliding Retry

In this strategy, the service will continue to retry the service call by adding an incremental time delays on each subsequent attempts. For example, the first retry may wait 500 MS, the second will wait 1000 MS, the third will wait 1500 MS until the retry count has not been exceeded. By adding an increasing delay, we reduce the number of retries to the service and avoid adding any additional load to a service which is already overloaded.

Retry with Exponential Backoff

In this strategy, we take the Sliding Retry strategy and ramp up the retry delay exponentially. If we started with a 500 MS delay, we would retry again after 1500 MS, then 3000 MS. Here we are trying to give the service more time to recover before we try to invoke it again.

Abort Retry

As we understand, we can't have a retry process happening forever. We need to have a threshold on the maximum number of retry attempts, we try for a failed service call. We need to maintain the counter and when it reaches the threshold value, our best strategy is to abort the retry process and let the error propagate to the calling service.

Conclusion

The retry pattern allows the calling service to retry failed attempts with a hope that the service will respond within an acceptable time.

With the varying interval between retries we provide the dependent service more time to recover and respond for our request.

It is recommend that, we need to keep a track of failed operations as it will be very useful information to find recurring errors and also the required infrastructure like thread pool, thread strategy etc.

At some point, we just need to abort the retry and we must acknowledge that the service is not responding and notify the calling service with an error.

References

Thursday, June 24, 2021

Template Method Pattern - Skeleton is Defined in Base Class

Template Method Design Pattern
Photo Courtesy Unsplash

What is the Template Method Design Pattern?

The Template Method Design Pattern defines a sequence of steps of an algorithm and allows the subclasses to override the steps but do not allow to change the sequence. The Key to the Template Design Pattern is that we put the general logic in the abstract parent class and let the child classes define the specifics.

Template Method pattern falls under the behavioural design pattern, is one of the easiest to understand and implement. This design pattern is used popularly in framework development and also helps to avoid code duplication.

An abstract class contains the templateMethod which should be made final so that it cannot be overridden. This template method makes use of other operations available in order to run the algorithm but is decoupled for the actual implementation of these methods. Concrete class implements all the methods required by the templateMethod that were defined as abstract in the parent class.

Pattern Implementation

As am a coffee person, I will take an example of preparing the coffee using a template method pattern which should allow to understand the pattern easily instead of taking some framework implementations such as AbstractController, RequestProcessor, HttpServlet, InputStream, OutputStream etc.

public abstract class CoffeeMaker {
   protected boolean sugarFree = false;

   public void prepareCoffee() {
      boilWater();
      addMilk();
      if (!isSugarFree()) {
         addSugar();
      } else {
 System.out.println("- No Sugar")
      }
      addCoffeePowder();
      System.out.println("- Coffee is Ready!!!");
   }

   public final void boilWater() {
      System.out.println("- Boiling Water");
   }

   public boolean isSugarFree() {
      return sugarFree;
   }

   public void setSugarFree(boolean sugarFree) {
      this.sugarFree = sugarFree;
   }

   abstract void addMilk();
   abstract void addSugar();
   abstract void addCoffeePowder();
}

Now we will create 2 concrete classes which will provide different implementations.

public class CothasCoffeeMaker extends CoffeeMaker {
   @Override
   public void addMilk() {
      System.out.println("- Adding Milk");
   }

   @Override
   public void addSugar() {
      System.out.println("- Adding Sugar");
   }

   @Override
   public void addCoffeePowder() {
      System.out.println("- Adding Cothas Coffee Powder");
   }
}

public class BruCoffeeMaker extends CoffeeMaker {
   @Override
   public void addMilk() {
      System.out.println("- Adding Milk");
   }

   @Override
   public void addSugar() {
      System.out.println("- Adding Sugar");
   }

   @Override
   public void addCoffeePowder() {
      System.out.println("- Adding Bru Coffee Powder");
   }
}

Now it's time to write the main method to execute the above code and see the output of the two variations of the coffee.

public class CoffeeMakerExample {
   public static void main(String[] args) {
      System.out.println("Cothas Coffee Preparation")
      CoffeeMaker coffeeMaker = new CothasCoffeeMaker();
      coffeeMaker.prepareCoffee();

      System.out.println("Bru Coffee Preparation");
      coffeeMaker = new BruCoffeeMaker();
      coffeeMaker.setSugarFree(true);
      coffeeMaker.prepareCoffee();
   }

}

The CoffeeMaker class is an abstract class containing the algorithm skeleton. The prepareCoffee() is the method that contains the process steps. The boilWater() method is common step for the process of any coffee preparation and no customization is required, hence it has been made as final so the subclasses will not override and provide a different implementation. 

We have two subclasses CothasCoffeeMaker and BruCoffeeMaker which follows the same preparation process but implementation are given at each individual subclass.

If you execute the above program, you should see the following output.

Cothas Coffee Preparation
- Boiling Water
- Adding Milk
- Adding Sugar
- Adding Cothas Coffee Powder
- Coffee is Ready!!!

Bru Coffee Preparation
- Boiling Water
- Adding Milk
- No Sugar
- Adding Bru Coffee Powder
- Coffee is Ready!!!

Conclusion

  • The pattern promotes the code reusability and decoupling, but at the expense of using inheritance.
  • The pattern adhers to the Single Responsibility and Open/Closed principles of S.O.L.I.D - Design Principles.

Reference

Monday, February 22, 2021

S.O.L.I.D — Design Principles

 

S.O.L.I.D — Design Principles Image

Software Design Principles

In today’s world, customer requirements keep changing at an unprecedented pace. It becomes essential for the technical teams to accommodate the new requirements and deliver those very quickly. To develop and deliver faster, it’s necessary to reduce software development and testing time.

At the same time, new technologies are introduced every few months. It’s common to experiment with more optimal and efficient technologies by replacing the existing ones. Thus, it’s important to write the code that is flexible and loosely coupled to introduce any changes.

Well written code is easy to grasp as new developer doesn’t have to spend more time reading the code. A well maintained software thus enhances developer’s and the team’s productivity. In addition, high test coverage increases the confidence to deploy a new change to the production.


Where do SOLID principles come from?

SOLID principles came from an essay written in 2000 by Robert Martin, known as Uncle Bob, where he discussed that a successful application will change and, without good design, can become rigid, fragile, immobile and viscous.

  • Rigid — Things are very fixed. You can’t move or change things without affecting other things, but it’s clear what will break if you make a change.
  • Fragile — Easy to move and change things but not obvious what else might break as a result.
  • Immobile — Code works fine but you can’t re-use code without duplicating or replicating it.
  • Viscous — Everything falls apart when you make a change, you quickly push it back together and get your change working. The same thing happens when somebody else comes along to make a change.

The principles that Robert Martin talks about to avoid the four design anti-patterns above have evolved to be known as the SOLID principles. Although he didn’t invent the principles, he pulled together some good coding practices that already existed around a central theme of managing dependencies and put forward a good argument for using those practices together.

In object-oriented world, S.O.L.I.D is a mnemonic acronym for five design principles intended to make software designs more understandable, flexible, and maintainable. Let’s go through all five principles.

Single Responsibility Principle

One of the simplest principles to understand. It states that, a class should only have one responsibility. Furthermore, it should only have one reason to change.

Open/Closed Principle

Simply put, classes should be open for extension, but closed for modification. In doing so, we stop ourselves from modifying existing code and causing potential new bugs.

Liskov Substitution Principle

The principle states that, objects of the same superclass should be able to substitute each other without breaking an existing code.

Interface Segregation Principle

According to this principle, the larger interfaces should be split into smaller ones. By doing so, we can ensure that implementing classes only need to be concerned about the methods that are of interest to them.

Dependency Inversion Principle

The principle of Dependency Inversion refers to the decoupling of software modules. This way, instead of high-level modules depending on low-level modules, both will depend on abstractions.

Conclusion

The above five principles form a foundation for the best practices followed in Software Engineering. Practicing the above principles in day to day work helps improve the readability, modularity, extensibility and testability of the software.

One thing you can guarantee with any application, if it’s successful and used extensively, it will change over the time. As it changes, the complexity factor gradually increases until you hit a tipping point where it becomes more difficult and takes longer time to ship new features on top of the poorly written code that was quickly shipped once.

I’ll leave you with following questions which I’ve found useful to ask myself while writing code:
  • Is the class DRY(Don’t Repeat Yourself)?
  • Does everything in a class change at the same rate?
  • Have I abstracted out something that is likely to change?
  • Have I abstracted out something that is not used in all classes that inherit it?

Featured Post

Your AI Sidekick: How Claude took over Pritee’s Repetitive tasks

  It was a classic Wednesday morning in our Bengaluru office . Pritee, one of our sharpest Project Managers, had just stepped out of a stake...