1. What are Design Patterns?
Design Patterns are general reusable solutions to common problems occurring in software design. They represent best practices evolved over time by experienced software developers.
2. Can you name some creational design patterns?
Creational design patterns deal with object creation mechanisms. Examples include Singleton, Factory Method, and Abstract Factory.
3. How can you describe a design pattern?
For describing a design pattern, we follow the below things:
- Define a pattern name and what classification of design pattern the pattern would fall to.
- Define a Problem and what is the corresponding solution
- What are the variations and language-dependent alternatives for the problem that needs to be addressed?
- What are the real-time use cases and the efficiency of the software that uses these patterns?
4. What is Inversion of Control?
Inversion of control is a pattern used to decouple the dependencies between layers and components in the system. The Dependency-Injection (DI) pattern is an example of an IoC pattern that helps in removing dependencies in the code.
Let us understand this with the help of an example. Consider we have a class A
that makes use of class
B
as shown below:
public class A{ private B b; public A(){ this.b = new B(); }}
Here, we have a dependency between classes A and B. If we had the IoC pattern implemented, we would not have used the new operator to assign value to the dependent variable. It would have been something as shown below:
public class A { private IocB b; public A(IocB b) { this.b = b; }}
We have inverted the control of handing the dependency of instantiating the object of class B
to the IoC
class IocB
.
5. What do you understand by the Open-Closed Principle (OCP)?
The Open close principle states that any class, component or entity should be open for extension but closed for modification. A class can be extended via Inheritance, Interfaces, Composition whenever required instead of modifying the code of the class. Consider an instance where we have a class that calculates the area of a square. Later, we get the requirement of calculating the area of a rectangle. Here, instead of modifying the original class, we can create one base class and this base class can be extended by the new class rectangle.
6. What are some of the design patterns used in Java’s JDK library?
Following are some design patterns used in Java’s JDK library:
- Decorator pattern are used by the Wrapper classes.
- Singleton pattern is used in classes like Calendar and Runtime.
- Factory pattern is used for methods like Integer.valueOf methods in wrapper classes.
- Observer pattern is used for handling event frameworks like awt, swing etc.
7. How are design principles different from design patterns?
- Design principles are those principles that are followed while designing software systems for any platform by making use of any programming language. SOLID principles are the design principles that we follow as guidelines to develop robust, extensible and scalable software systems. These apply to all aspects of programming.
- Design Patterns are the reusable template solutions for commonly occurring problems that can be customized as per the problem requirements. These are well-implemented solutions that are tested properly and are safe to use. Factory Design Pattern, Singleton pattern, Strategy patterns are a few of the examples of design patterns.
8. How are design patterns different from algorithms?
Both Design Patterns and Algorithms describe typical solutions to any given problem. But the main difference is that the algorithm defines a clear set of actions for achieving a goal and a design pattern provides a high-level description of any solution. Design patterns applied to two different problems might be the same but the logic of implementation would be different and is based on the requirements.
9. What are some instances where we prefer abstract classes over interfaces in Java?
Both Abstract classes and interfaces in Java follow the principle of writing code for interface rather than the implementation. This principle ensures that flexibility is added to the code to tackle dynamic requirements. Some of the pointers for deciding what to prefer over what are as follows:
- Java lets to extend only one class and let’s implement multiple interfaces. If we extend one class then we cannot extend other classes. In such cases, it is better to implement the interfaces wherever possible and reserve the inheritance of classes to only important ones.
- Interfaces are used for representing the behaviour of the class. Java lets to implement multiple interfaces which is why we can take the help of interfaces to help classes have multiple behaviours at the same time.
- Abstract classes are slightly faster than interfaces. It can be used for time-critical applications.
- In cases where there are common behaviours across the inheritance hierarchy, these can be coded at one place in abstract classes. Interfaces and abstract classes can also be used together to define a function in interface and functionality in abstract class.
10. What problem does Builder Pattern try to solve?
A builder pattern is a type of creational design pattern that lets to construct complex objects in a step by step manner. The pattern lets to produce different representations of an object using the same construction logic. It helps in creating immutable classes having a large set of attributes. In the Factory and Abstract Factory Design Patterns, we encounter the following issues if the object contains a lot of attributes:
- When the arguments are too many, the program will be error-prone while passing from the client to the Factory Class in a specific order. It becomes tedious to maintain the order of arguments when the types are the same.
- There might be some optional attributes of the object and yet we would be forced to send all parameters and optional attributes as Null.
- When the object creation becomes complex due to heavy attributes, the complexity of this class would become confusing.
The above problems can also be solved by using constructors of required parameters alone. But this causes an issue when there would be new parameters that are added as part of new requirements. This would result in inconsistency. That’s where Builder comes into the picture. This pattern solves the issue of a large number of optional attributes and the inconsistent state by providing means to build an object in a step-by-step way and return the final object utilizing another method.
Builder pattern can be implemented by following the below steps:
-
Create a static nested class, copy all arguments from the outer class. This nested class would be called the Builder
class.
-
Proper naming convention has to be followed while naming this builder class. For example, if the name of the
class is
Interviewbit
, then the name of the builder would beInterviewbitBuilder
.
-
Proper naming convention has to be followed while naming this builder class. For example, if the name of the
class is
- The builder class should have a public constructor with all required attributes sent as parameters.
- The builder class should have methods for setting optional parameters and return the same builder object post setting these values.
- The last step is to have a build() method inside the builder class that returns the Object needed by the client. This would require a private constructor in the class that takes the Builder class as the parameter.
Following is the sample example of the builder pattern implementation. We have a User class and we will be building UserBuilder class to build the objects of the User class.
class User { //All final attributes private final String firstName; // required private final String lastName; // required private final int age; // required private final String phoneNbr; // optional private final String address; // optional private final String nationality; //optional private User(UserBuilder builder) { this.firstName = builder.firstName; this.lastName = builder.lastName; this.age = builder.age; this.phoneNbr = builder.phoneNbr; this.address = builder.address; this.nationality = builder.nationality; } //Setters are not provided to make it immutable public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public int getAge() { return age; } public String getPhoneNbr() { return phoneNbr; } public String getAddress() { return address; } public String getNationality() { return nationality; } @Override public String toString() { return "User: "+this.firstName+" "+this.lastName+", "+this.age+", "+this.nationality+", "+this.phoneNbr+", "+this.address; } public static class UserBuilder { private final String firstName; private final String lastName; private int age; private String phoneNbr; private String address; private String nationality; public UserBuilder(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public UserBuilder age(int age) { this.age = age; return this; } public UserBuilder phoneNbr(String phoneNbr) { this.phoneNbr = phoneNbr; return this; } public UserBuilder address(String address) { this.address = address; return this; } public UserBuilder nationality(String nationality) { this.nationality = nationality; return this; } // method to return the constructed object public User build() { User user = new User(this); validateUserObject(user); return user; } private void validateUserObject(User user) { //Validate of the object does not break anything } }}public class Driver{ public static void main(String[] args) { User firstUser = new User.UserBuilder("Harry", "Potter") .age(30) .phoneNbr("1234567") .address("221B Baker Street - London") .build(); System.out.println(firstUser); User secondUser = new User.UserBuilder("Ron", "Weasley") .age(32) .phoneNbr("5655") //no address .build(); System.out.println(secondUser); User thirdUser = new User.UserBuilder("Hermoine", "Granger").age(20).nationality("English") //No age //No phone //no address .build(); System.out.println(thirdUser); }}
The output of the above code would be:
User: Harry Potter, 30, null, 1234567, 221B Baker Street - LondonUser: Ron Weasley, 32, null, 5655, nullUser: Hermoine Granger, 20, English, null, null
11. Consider a scenario where you are writing classes for providing market data and we have the flexibility to switch to different vendors or we can be directed to the Direct Exchange Feed. How will you approach this problem to design the system?
We can do this by having an interface called “MarketData” which will consist of the methods required by the Client. The MarketData should have the MarketDataProvider as the dependency by employing Dependency Injection. This ensures that even if the provider changes, the market data will not be impacted.
The implementation of this problem is left as an exercise to the reader.
12. What do you understand by the Null Object pattern?
In this pattern, a null object is used for replacing the check of validating if the object instance is null or not. This Null Object has a “do nothing” relationship and these can be used for providing default behaviour if the data is unavailable.
13. What are the components of the Composite Entity pattern?
This pattern is used in EJB (Enterprise Java Beans) persistence mechanism. A composite entity represents the object graph and is an EJB entity. Whenever a composite entity is updated, the object beans that are internally dependent on this bean are updated automatically. There are 4 main components of the Composite Entity Pattern:
- Composite Entity - Primary entity bean that can have a coarse-grained object that is meant for persistence.
- Coarse-Grained Object - This contains the dependent objects which have their life cycle and in turn manages the lifecycle of dependent objects.
- Dependent Object - This object is dependent on the coarse-grained object throughout the persistence lifecycle.
- Strategies - These represent how to implement the composite entity.
14. What is the main advantage of using a prototype design pattern over object creation using a new keyword?
Prototype design pattern is used for creating duplicate objects based on the prototype of the already existing object
using cloning. Doing this has a positive impact on the performance of object creation. Creating objects using the
new
keyword requires a lot of resources and is a heavyweight process that impacts performance. Hence, the
prototype design pattern is more advantageous than the object created using a new keyword.
15. How can you achieve thread-safe singleton patterns in Java?
A thread-safe singleton class is created which helps in object initialization in the presence of multiple threads. It can be done using multiple ways:
- Using Enums: Enums are the simplest means of creating a thread-safe singleton class in Java because the synchronization support is inherently done by Java itself. Enums are by default final and this also helps in preventing multiple initializations at the time of serialization.
public enum ThreadSafeSingleton{ SINGLETON_INSTANCE; public void display(){ System.out.println("Thread-safe singleton Display"); } } // The Singleton class methods can be invoked as below ThreadSafeSingleton.SINGLETON_INSTANCE.show();
- Using Static Field Initialization: Thread-safe singleton can also be created by creating the instance at the time of class loading. This is achieved by making use of static fields as the Classloader guarantees that the instances are initialized during class loading and the instance is not visible until that has been fully created.
public class ThreadSafeSingleton{ private static final ThreadSafeSingleton INSTANCE = new ThreadSafeSingleton(); private ThreadSafeSingleton(){ } public static ThreadSafeSingleton getInstance(){ return INSTANCE; } public void display(){ System.out.println("Thread-safe Singleon"); } } ThreadSafeSingleton.getInstance().display();
But the disadvantage of this way is that the initialization cannot be done lazily and the getInstance() method is called even before any client can call.
-
Using synchronized keyword: We can make use of the synchronized keyword upon the getInstance method
as shown below.
- In this method, we can achieve lazy initialization, and also since we use synchronized keywords, the object initialization is thread-safe.
- The only problem is that since the whole method is synchronized, the performance is impacted in the presence of multiple threads.
public class ThreadSafeSingleton { // Creating private instance to make it accessible only by getInstance() method private static ThreadSafeSingleton instance; private ThreadSafeSingleton() { // Making constructor private so that objects cant be initialized outside the class } //synchronized getInstance method synchronized public static ThreadSafeSingleton getInstance(){ if (this.instance == null) { // if instance is null, initialize this.instance = new ThreadSafeSingleton(); } return this.instance; } }
- Double-check locking: Here, we will be using a synchronized block of code within the getInstance method instead of making the whole method synchronized. This ensures that only a handful of threads have to wait only for the first time thereby not impacting the performance.
public class ThreadSafeSingleton { // Creating private instance to make it accessible only by getInstance() method private static ThreadSafeSingleton instance; private ThreadSafeSingleton(){ // Making constructor private so that objects cant be initialized outside the class } public static ThreadSafeSingleton getInstance(){ if (instance == null){ //synchronized block of code synchronized (ThreadSafeSingleton.class){ if(instance==null) { // initialize only if instance is null instance = new ThreadSafeSingleton(); } } } return instance; } }
16. What would happen if we do not have a synchronized method for returning Singleton instance in a multi-threaded environment?
In a multi-threaded environment, if we have a non-synchronized method for returning instances, there are chances that the method can create more than one instance. Consider that we have 2 threads and both enter the condition for checking if the instance already exists. Both threads will find that the instance has not been created and hence both will create the class instances. This goes against the principle of the Singleton pattern. Hence, in a multi-threaded environment, it is recommended to use synchronized checks.
17. Write a Java Program to display the pyramid as per the below design.
* * * * * * * * * * * * * * *
This can be achieved by using nested loops and calculatingly adding spaces and stars as shown in the logic below:
public class InterviewBitPyramid{ public static void printPyramid(int n) { for (int i=0; i<n; i++){ // for number of rows for (int j=n-i; j>1; j--) { System.out.print(" "); //print space } //for number of columns for (int j=0; j<=i; j++ ) { System.out.print("* "); // print star } //end-line after every row System.out.println(); } } public static void main(String args[]){ printPyramid(5); //Print Pyramid stars of 5 rows }}
Output:
* * * * * * * * * * * * * * *
18. Write a Java Program to display the left triangle star pattern on the system console.
This can be again be achieved by using nested loops and calculatingly adding spaces and stars as shown in the logic below:
public class InterviewBitLeftPyramid{ public static void printLeftTriangleStars(int n) { int j; for(int i=0; i<n; i++){ // outer loop for number of rows(n) for(j=2*(n-i); j>=0; j--){ // for spaces System.out.print(" "); // to print space } for(j=0; j<=i; j++){ // for columns System.out.print("* "); // Print star and give space } System.out.println(); // Go to next line after every row } } public static void main(String args[]){ printLeftTriangleStars(5); //print stars of 5 rows in left triangle fashion }}
Output:
* * * * * * * * * * * * * * *
19. Write a Java program to print Diamond Number Pattern.
public class InterviewBitDiamondNumber{ public static void diamondNumberDisplay(int maxNum){ for (int i = 1; i <= maxNum; i++){ int n = maxNum; for (int j = 1; j<= n - i; j++) { System.out.print(" "); } for (int k = i; k >= 1; k--) { System.out.print(k); } for (int l = 2; l <= i; l++) { System.out.print(l); } System.out.println(); } for (int i = maxNum-1; i >= 1; i--){ int n = maxNum-1; for (int j = 0; j<= n - i; j++) { System.out.print(" "); } for (int k = i; k >= 1; k--){ System.out.print(k); } for (int l = 2; l <= i; l++){ System.out.print(l); } System.out.println(); } } public static void main(String[] args) { int n = 5; diamondNumberDisplay(n); }}
Output:
1 212 3212343212345432123454321234 32123 212 1
20. Write a Java program that takes a String as an input and prints the pattern in the increasing order of odd numbers as shown in the example below:
If String input is PATTERN, then the pattern will be: P (First Character Once) AAA (Second Character thrice) TTTTT (Third Character 5 times) TTTTTTT : EEEEEEEEE : RRRRRRRRRRR :NNNNNNNNNNNNN :
Solution:
import java.util.Scanner;public class InterviewBitStringPattern{ public static void printStringPattern(String input){ for (int i = 1; i <= input.length(); i++) { for (int j = input.length(); j > i; j--){ System.out.print(" "); } for (int k = i * 2 - 1; k >= 1; k--){ System.out.print(input.charAt(i - 1)); } System.out.println(""); } } public static void main(String[] args) { // to create a new Scanner object Scanner scanner = new Scanner(System.in); // to get the String from the user System.out.println("Enter Input String: "); String input = scanner.nextLine(); System.out.println("Printing Pattern....."); printStringPattern(input); }}
Output:
Enter Input String: PATTERNPrinting Pattern..... P AAA TTTTT TTTTTTT EEEEEEEEERRRRRRRRRRRNNNNNNNNNNNNN
21. Code Snippet: Singleton Design Pattern
// Singleton implementation in a programming language
class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}