OOD


OO Design questions can be tackled in similar ways:

  • handle ambiguity
    • OOD questions are often intentionally vague in order to test whether you'll make assumptions or if you'll ask clarifying questions. Two questions should be asked beforehand:
    • who is going to use it?
    • how they are going to use it?
    • or six "W"s
  • define the core objects
    • after understanding what we're designing, we should consider what the "core objects" in a system are.
    • e.g OODesign for a restaurant, the core objects might be things like Table, Guest, Party, Order, ...Meal, Employee...
  • analyze relationships
    • we need to analyze the relationships between the objects.
      • which objects are members of which other objects?
      • do any objects inherit from any others?
      • are relationships many-to-many or one-to-many?
    • be careful, you can often make incorrect assumptions, should talk to your interviewer about how general purpose your design should be.
  • investigate actions


Singleton Pattern

refer to: https://www.journaldev.com/1377/java-singleton-design-pattern-best-practices-examples

To implement Singleton pattern, we have different approaches but all of them have following common concepts.

  • Private constructor to restrict instantiation of the class from other classes.
  • Private static variable of the same class that is the only instance of the class.
  • Public static method that returns the instance of the class, this is the global access point for outer world to get the instance of the singleton class.

eager initialization

  • benefit:
    • the instance of Singleton Class is created at the time of class loading, this is the easiest method to create a singleton class.
  • drawback:
    • instance is created even though client application might not be using it. If your singleton class is not using a lot of resources, this is the approach to use. But in most of the scenarios, Singleton classes are created for resources such as File System, Database connections etc and we should avoid the instantiation until unless client calls thegetInstancemethod. Also this method doesn’t provide any options for exception handling.
package com.journaldev.singleton;

public class EagerInitializedSingleton {

    private static final EagerInitializedSingleton instance = new EagerInitializedSingleton();

    //private constructor to avoid client applications to use constructor
    private EagerInitializedSingleton(){}

    public static EagerInitializedSingleton getInstance(){
        return instance;
    }
}

Static block initialization

package com.journaldev.singleton;

public class StaticBlockSingleton {

    private static StaticBlockSingleton instance;

    private StaticBlockSingleton(){}

    //static block initialization for exception handling
    static{
        try{
            instance = new StaticBlockSingleton();
        }catch(Exception e){
            throw new RuntimeException("Exception occured in creating singleton instance");
        }
    }

    public static StaticBlockSingleton getInstance(){
        return instance;
    }
}

note: static code block is the group of statements that gets executed when the class is loaded into
 memory by Java ClassLoader. Static block is used to initialize the static variables of the class. 
 Mostly its used to create static resources when the class is loaded.  
 We cant access non-static variables in the static block. We can have multiple static blocks in a class, 
 although it doesnt make much sense. Static block code is executed only once when the class is loaded 
 into memory.

note: similar to the eager initialization, static block initialization creates the instance even before it’s being used and that is not the best practice to use. So in further sections, we will learn how to create Singleton class that supports lazy initialization.

Lazy initialization

Thread-safe singleton

package com.journaldev.singleton;

public class ThreadSafeSingleton {

    private static ThreadSafeSingleton instance;

    private ThreadSafeSingleton(){}

    // Above implementation works fine and provides thread-safety but it reduces the performance 
    // because of cost associated with the synchronized method,
    public static synchronized ThreadSafeSingleton getInstance(){
        if(instance == null){
            instance = new ThreadSafeSingleton();
        }
        return instance;
    }

    // To avoid this extra overhead every time, double checked locking principle is used.
    public static ThreadSafeSingleton getInstanceUsingDoubleLocking(){
        if(instance == null){
            synchronized (ThreadSafeSingleton.class) {    // synchronized code block is used 
                if(instance == null){
                    instance = new ThreadSafeSingleton();
                }
            }
        }
        return instance;
    }

}

Bill Pugh Singleton implementation

package com.journaldev.singleton;

public class BillPughSingleton {

    private BillPughSingleton(){}

    private static class SingletonHelper{
        private static final BillPughSingleton INSTANCE = new BillPughSingleton();
    }

    public static BillPughSingleton getInstance(){
        return SingletonHelper.INSTANCE;
    }
}

Notice the private inner static class that contains the instance of the singleton class. When the singleton class is loaded, SingletonHelper class is not loaded into memory and only when someone calls the getInstance method, this class gets loaded and creates the Singleton class instance.

This is the most widely used approach for Singleton class as it doesn’t require synchronization. I am using this approach in many of my projects and it’s easy to understand and implement also.

Builder Pattern

an alternative way to construct complex objects. This should be used only when you want to build different immutable objects using same object building process.

builder pattern helps us in creating immutable classes with large set of state attributes.

the problem, if you want to make a immutableUserclass, then you must pass all five information as parameters to constructor. Now what if only firstName and lastName are mandatory and rest 3 fields are optional. Problem !! we need to add lots of constructors like the following :

public User (String firstName, String lastName, int age, String phone){ ... }
public User (String firstName, String lastName, String phone, String address){ ...  }
public User (String firstName, String lastName, int age){ ...   }
public User (String firstName, String lastName){ ...    }

How can we manage? Now let’s introduce our sixth attribute i.e. salary. Now it is problem.

  • One way it to create more constructors, and
  • another is to loose the immutability and introduce setter methods.

You choose any of both options, you loose something, right? Here, builder pattern will help you to consume additional attributes while retaining the immutability of Use class.

public class User
{
    //All final attributes
    private final String firstName; // required
    private final String lastName; // required
    private final int age; // optional
    private final String phone; // optional
    private final String address; // optional

    private User(UserBuilder builder) {
        this.firstName = builder.firstName;
        this.lastName = builder.lastName;
        this.age = builder.age;
        this.phone = builder.phone;
        this.address = builder.address;
    }

    //All getter, and NO setter to provde immutability
    public String getFirstName() {
        return firstName;
    }
    public String getLastName() {
        return lastName;
    }
    public int getAge() {
        return age;
    }
    public String getPhone() {
        return phone;
    }
    public String getAddress() {
        return address;
    }

    @Override
    public String toString() {
        return "User: "+this.firstName+", "+this.lastName+", "+this.age+", "+this.phone+", "+this.address;
    }

    public static class UserBuilder
    {
        private final String firstName;
        private final String lastName;
        private int age;
        private String phone;
        private String address;

        public UserBuilder(String firstName, String lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }
        public UserBuilder age(int age) {
            this.age = age;
            return this;
        }
        public UserBuilder phone(String phone) {
            this.phone = phone;
            return this;
        }
        public UserBuilder address(String address) {
            this.address = address;
            return this;
        }
        //Return the finally consrcuted User object
        public User build() {
            User user =  new User(this);
            validateUserObject(user);
            return user;
        }
        private void validateUserObject(User user) {
            //Do some basic validations to check
            //if user object does not break any assumption of system
        }
    }
}

And below is the way, we will use theUserBuilderin our code:

public static void main(String[] args) {
    User user1 = new User.UserBuilder("Lokesh", "Gupta")
    .age(30)
    .phone("1234567")
    .address("Fake address 1234")
    .build();

    System.out.println(user1);

    User user2 = new User.UserBuilder("Jack", "Reacher")
    .age(40)
    .phone("5655")
    //no address
    .build();

    System.out.println(user2);

    User user3 = new User.UserBuilder("Super", "Man")
    //No age
    //No phone
    //no address
    .build();

    System.out.println(user3);
}

Output:

User: Lokesh, Gupta, 30, 1234567, Fake address 1234
User: Jack, Reacher, 40, 5655, null
User: Super, Man, 0, null, null

Please note that above created user object does not have any setter method, so it’s state can not be changed once it has been built. This provides the desired immutability.

note: something like Abstract factory pattern, where you find a factory (or builder) for a specific type of object, and then factory gives you a concrete instance of that object

Static factory methods

refer to: https://jlordiales.me/2012/12/26/static-factory-methods-vs-traditional-constructors/

a useful pattern to instantiate classes with several (possibly optional) attributes that results in easier to read, write and maintain client code, among other benefits.

we cannot differentiate several constructors because constructors by definition has no names. JVM identify the constructor only through the signature return type, name and parameters type). So it brings kind of restriction when using constructors.

we can use: static factory methods, which are simply public static methods that return an instance of the class.

eg. Boolean.valueOf()

public static Boolean valueOf(boolean b) {
  return (b ? TRUE : FALSE);
}

eg. more complicated static factory methods.

public class RandomIntGenerator {
  private final int min;
  private final int max;

  private RandomIntGenerator(int min, int max) {
    this.min = min;
    this.max = max;
  }

  public static RandomIntGenerator between(int max, int min) {
    return new RandomIntGenerator(min, max);
  }

  public static RandomIntGenerator biggerThan(int min) {
    return new RandomIntGenerator(min, Integer.MAX_VALUE);
  }

  public static RandomIntGenerator smallerThan(int max) {
    return new RandomIntGenerator(Integer.MIN_VALUE, max);
  }

  public int next() {...}
}

Note how the constructor was made private to ensure that the class is only instantiated through its public static factory methods. Also note how your intent is clearly expressed when you have a client with RandomIntGenerator.between(10,20) instead of new RandomIntGenerator(10,20).

the benefits:

  • they have names, and can provide a meaningful name for our constructors
  • are not required to return a new object every time they are invoked. like Boolean.valueOf(). Notice that this static method returns either TRUE or FALSE, both immutable Boolean objects.

  • they can return an object of any subtype of their return type.

results matching ""

    No results matching ""