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

30 September 2019

SOLID Software Design Principles in Java

Code Fragility:
Fragility is the tendency of the software to break in many places every time it is changed.
- Robert C. Martin

Code Rigidity:
Rigidity is the tendency for software to be difficult to change, even in simple ways. Every change causes a cascade of subsequent changes in dependent modules.
- Robert C. Martin

The choice you have to make is
Fast Delivery
Easiest fix/change
Fast
Poor written code
Low Customer Responsiveness over the time
High cost of implementing change

Vs

Code quality
Takes more time
Adds a bit of complexity
Maintainable
High Customer Responsiveness over the time.
Avg cost of implementing change

Technical Debt: The cost of prioritizing fast delivery over code quality over longer period of time.

No matter how good the team is, technical debt will accumulate over time. If it is left uncontrolled, it will kill your project. The key is to keep it under control.
How to pay technical debt: write code and pay debt(refactor) then write some more code and refactor again.

Benefits of SOLID code:
- Easy to understand
- Changes are faster and have a minimum risk level.
- Highly maintainable over long periods of time.

Other ways to keep your architecture clean
- Constant refactoring
- Design patterns
- Unit Testing (TDD)

SOLID Principles:
Single Responsibility Principle
Open Closed Principle
Liskovs Substitution Principle
Interface Segregation Principle
Dependency Injection Principle



Single Responsibility Principle:
A class or function or Module should have only one reason for change. reason meaning responsibility here, beyond specific responsibility assigned to one function/class/module we should not make any other changes to it.

Name of the Single Responsibility Principle class is expressive and name of functions with in the class need to be inline with the purpose of the class.

Advantages of single responsibility principle:
- It makes code easier to understand, code and design.
- Classes are less coupled and more resilient to change.
- More testable design.




SRP Example:
While reading class or component name we need to feel name is implicit with purpose and methods also inline with purpose.



class ConsoleLogger {
void logInfo(String msg) {
    System.out.println(msg);
    }
void logError(String msg, Exception e) {...}
}


Symptoms of Not Using SRP
- Code is more difficult to read and reason about.
- Decreased quality due to testing difficulty.
- Side effects.
-  High coupling : the level of inter - dependency between various software components.


Open Closed Principle:
Classes functions and Modules should be closed for modification but open for extension.

Closed for Modification meaning, new feature should not change/modify existing source code. Open for extension meaning, a component should be extendable to behave it in new ways.

Advantages of OCP: 
- new features can be added with minimal cost.
- minimizes regression bugs.
- enforce decoupling by isolating changes in some components, works along with SRP.

Best Practices for changing APIs
-do not change existing public contracts: data classes, signatures
-expose abstractions to your customers, let them add new features on top of your framework. 
- if breaking change is inevitable, give your customers time to adapt.

Liskov Substitution Principle: 
Any object of a type must be substitutable by object of derived type without altering correctness of the program.

Ex:
Honda Amaze is a Kind of Car
Peacock is a kind of Bird

Bird class is substitutable by Peacock
Car is substitutable by Honda Amaze 

Interface segregation principle:

States that no client is forced to use methods it does not use. Imagine a scenario where there is an interface with lot many number of methods and the implementing classes of this interface need to implement only some of the methods. 

03 July 2017

Singleton Design Pattern in Java

Singleton Pattern:
This is useful in an application where we have to use just one instance of the class and at any point of time more than one instance cannot be created for a class.
How to create Singleton design pattern?
To create the singleton class, we need to have static member of class, private constructor and static factory method.
Static member: It gets memory only once because of static, it contains the instance of the Singleton class.
Private constructor: It will prevent to instantiate the Singleton class from outside the class.
Static factory method: This provides the global point of access to the Singleton object and returns the instance to the caller.
Code Example 1:
package singleton;
public class Logger
{
private static Logger obj = new Logger();
private Logger() {}
public static Logger getLoggerObject()
{
return obj;
}
public void performLogging( String x)
{
       // write into console
       System.out.println("Error "+x);

}
}

package singleton;

public class SingletonExample
{
public static void main(String[] args)
{

Logger.getLoggerObject().performLogging("Test Singleton");
      
}
}
DisAdvantage: Object created before its use.
Code Example 2: Lazy instantiation of singleton design pattern
//Code Example 2: Lazy instantiation of singleton design pattern
package singleton2;

public class Logger
{
private static Logger obj;
private Logger() {}
public static Logger getLoggerObject()
{
       if(obj == null)
              obj = new Logger();
return obj;
}
public void performLogging( String x)
{
       // write into console
       System.out.println("Error "+x);

}
}

package singleton2;

public class SingletonExample
{
public static void main(String[] args)
{

Logger.getLoggerObject().performLogging("Test Singleton with lazy initialization");
      
}
}
DisAdvantage: Not thread safe.
Code Example3 : Thread safe Lazy instantiation of singleton design pattern
// Code Example3 : Thread safe Lazy instantiation of singleton design pattern
package singleton3;

public class Logger {
       private static Logger obj;
       private Logger() {}
       public static synchronized Logger getLoggerObject()
       {
              if(obj == null)
                     obj = new Logger();
       return obj;
       }
       public void performLogging( String x)
       {
              // write into console
              System.out.println("Error "+x);

       }
}
// client calling singleton class
package singleton3;

public class SingletonExample {
       public static void main(String[] args)
       {

       Logger.getLoggerObject().performLogging("Test Singleton with lazy initialization and thread safe");
             
       }

}
DisAdvantage: Not performant. Above implementation works fine and provides thread-safety but it reduces the performance because of cost associated with the synchronized method. The object creation need to be synchronized only for first few threads.



Code Example 4: Thread safe cum performant singleton with double checked locking principle
// Code Example 4: Thread safe cum performant singleton with double checked locking principle
package singleton4;

public class Logger
{
private static Logger obj;
private Logger() {}
public static Logger getLoggerObject()
{
       if(obj == null)
              synchronized(Logger.class)
              {
                     if(obj == null)
                     {
                     obj = new Logger();
                     }
                                        
              }
       return obj;
}
public void performLogging( String x)
{
       // write into console
       System.out.println("Error "+x);

}
}
package singleton4;

import singleton4.Logger;

public class SingletonExample
{
public static void main(String[] args)
{

Logger.getLoggerObject().performLogging("Test Singleton with lazy initialization with thread safe and performant");
      
}
}
Code Example 5: Avoiding breaking singleton pattern using deserialization of object
If singleton class is serializable, you can serialize singleton class object to a file and deserialize it to get one more instance of the same class, to avoid this override readResolve() method which will be called just after the deserialization. In this method return singleton object as below code example:
package singleton5;

import java.io.Serializable;

public class Logger implements Serializable {
       private static Logger obj;
       private Logger() {}
       public static Logger getLoggerObject()
       {
              if(obj == null)
                     synchronized(Logger.class)
                     {
                           if(obj == null)
                           {
                           obj = new Logger();
                           }
                                               
                     }
              return obj;
       }
       protected Object readResolve()
       {
              System.out.println("in readResolve after deserialization");
              return getLoggerObject();
       }
       public void performLogging( String x)
       {
              // write into console
              System.out.println("Error "+x);

       }

}


package singleton5;

import java.io.*;

public class SingletonExample {
       public static void main(String[] args) throws ClassNotFoundException
       {
        Logger test1,test2;
     test1 = Logger.getLoggerObject();
        test1.performLogging("Test Singleton with lazy initialization with thread safe and performant");      
       // calling singleton class with deserialization
       try {
              ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("Logger.ser"));
              out.writeObject(test1);
              out.close();
             
              ObjectInputStream in = new ObjectInputStream(new FileInputStream("Logger.ser"));
              test2 =(Logger) in.readObject();
              in.close();
              test2.performLogging(" Testing with deserialization");
              System.out.println("Hashcode of instance with name test1:"+test1.hashCode());
              System.out.println("Hashcode of instance with name test2:"+test2.hashCode());
       } catch (FileNotFoundException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
       } catch (IOException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
       }
             
       }
}

Code Example 6: Using reflection to break singleton.
Using reflection we can break singleton design pattern, review the below code for example. Here we read the constructors of singleton class using reflection and create new instance to the singleton class which is different from actual object returned by getLoggerObject() method
package singleton6;

import java.io.Serializable;

public class Logger implements Serializable {
       private static Logger obj;
       private Logger() {}
       public static Logger getLoggerObject()
       {
              if(obj == null)
                     synchronized(Logger.class)
                     {
                           if(obj == null)
                           {
                           obj = new Logger();
                           }
                                               
                     }
              return obj;
       }
       protected Object readResolve()
       {
              System.out.println("in readResolve after deserialization");
              return getLoggerObject();
       }
       public void performLogging( String x)
       {
              // write into console
              System.out.println("Error "+x);

       }

}


package singleton6;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class SingletonExample {
public static void main(String[] args)
{
       Logger obj1 = Logger.getLoggerObject();
       obj1.performLogging("logging using obj1");
       System.out.println("Hashcode of obj1:"+obj1.hashCode());
       // breaking singleton using reflection
       Logger obj2 = null;
       try {
              Constructor[] cons = Logger.class.getDeclaredConstructors();
              for(Constructor constructor:cons)
              {
                     constructor.setAccessible(true);
                     obj2 = (Logger) constructor.newInstance();
                     obj2.performLogging("logging using obj2");
                     System.out.println("Hashcode of obj2:"+obj2.hashCode());
              }
       } catch (Exception e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
       }
      
}
}

Overcome reflection issue: To overcome issue raised by reflection, enums are used because java ensures internally that enum value is instantiated only once. Since java Enums are globally accessible, they can be used for singletons. Its only drawback is that it is not flexible i.e it does not allow lazy initialization.

//Java program for Enum type singleton
public enum Logger
{
  INSTANCE;
}
Code Example 7: Breaking Singleton using Cloning and how to avoid it
Java has the ability to create a copy of object with similar attributes and state form original object. This concept in java is called cloning. To implement cloning, we have to implement java.lang.Cloneable interface and override clone() method from Object class. It is a good idea to prevent cloning in a singleton class. To prevent cloning on singleton object, let us explicitly throw CloneNotSupportedException exception in clone() method.
// Code Example 7: Breaking Singleton using Cloning and how to avoid it
package singleton7;

import singleton7.Logger;

public class Logger implements Cloneable
{
private static Logger obj;
private Logger() {}
public static Logger getLoggerObject()
{
       if(obj == null)
              synchronized(Logger.class)
              {
                     if(obj == null)
                     {
                     obj = new Logger();
                     }
                                        
              }
       return obj;
}
public Object clone() throws CloneNotSupportedException
{
       throw new CloneNotSupportedException();
      
}
public void performLogging( String x)
{
       // write into console
       System.out.println("Error "+x);

}
}

//Code Example 7: Breaking Singleton using Cloning and how to avoid it
package singleton7;

public class SingletonExample {
       public static void main(String[] args)
       {
Logger obj1 = Logger.getLoggerObject();
obj1.performLogging("logging using obj1");
System.out.println("Hashcode of obj1:"+obj1.hashCode());
              try
              {
              Logger obj2 = (Logger)obj1.clone();
              }
              catch (CloneNotSupportedException e)
              {
       // TODO Auto-generated catch block
              System.out.println("clone failed due to override of clone method"+e);
              }
       }
}

Here above we have stopped user to create clone of singleton class. If you dont want to throw exception you can also return the same instance of class from clone method.
Code Example 8 within JDK:
getRuntime() method of java.lang.Runtime
The java.lang.Runtime class allows the application to interface with the environment in which the application is running. The java.lang.Runtime.getRuntime() method returns the runtime object associated with the current Java application which implements singleton pattern. Most of the methods of class Runtime are instance methods and must be invoked with respect to the current runtime object.
For example availableProcessors() method returns the number of processors available to the Java virtual machine. freeMemory() method of Runtime class returns the amount of free memory in the Java Virtual Machine.
package singleton8;

public class SingletonExample {

          public static void main(String[] args) {

             // print when the program starts
             System.out.println("Program starting...");

             // get the current runtime singleton object associated with this process
             Runtime run1 = Runtime.getRuntime();
             Runtime run2 = Runtime.getRuntime();
             System.out.println("Hashcode of two objects of java.lang.Runtime class:: \nrun1:"+run1.hashCode()+"\nrun2:"+run2.hashCode());

             // print the current free memory for this runtime
             System.out.println("JVM free memory:" + run1.freeMemory());
             System.out.println("JVM available processors:" + run1.availableProcessors());
             System.out.println("JVM Max Memory:" + run1.maxMemory());
            
          }
       }

Note: If singleton class is loaded by two class loaders then we can create two instances of same singleton class will be created, one for each class loader.

Factory Method Design Pattern
We have super class and N number of sub classes, and based on the input data provided we need to return object of one of the sub-classes, here we use Factory method pattern.
Example within JDK: java.util.Calendar # getInstance()