This project contains some design pattern which will be useful for android developers, Java Developer as it has advantages such as reusability, mutability, transparency.
Creational design patterns are concerned with the way of creating objects. These design patterns are used when a decision must be made at the time of instantiation of a class.
This pattern is used for allowing users to use only one instance of a class. Single Design Pattern has two instantiation i) Early instantiation ii) Lazy instantiation
The Singleton class creates an object initialy by using new keyword. Whether the user calls the getInstance() method or not Singleton class creates an object in the memory.
static Singleton instance = new Singleton();
private Singleton(){};
public static Singleton getInstance()
{
return instance;
}
In this instantiation, the Singleton classes object is only created when user need it by checking whether it is NULL or not.
static Singleton instance;
private Singleton(){};
public static Singleton getInstance()
{
if(instance == null)
instance = new Singleton();
return instance;
}
Why used--
1) It is used when we have too many arguments to send in constructor & it's hard to maintain the order
2) When we don't want to send all parameters in object initialization (Generally we send optional parameters as NULL)
It has a static nested class which contains all arguments of outer class and named using nameing convention 'ClassaNmeBuilder'. This class has a build method that will return the final Object.
Why used--
1) Used when we have multiple sub-classes of a Super class & based on input we want to return one class instance
public static FVehicle getInstance(String type, int wheel){
if(type == "car")
return new Car(wheel);
else if(type == "bike")
return new Bike(wheel);
return null;
}
2) It provides abstraction between implementation & user classes
Super class can be interface or abstract class or basic class.
abstract public class FVehicle {
public abstract int getWheel();
public String toString(){
return "Wheel: " + this.getWheel();
}
}
Structural design pattern is a blueprint of how different objects and classes are combined together to form a bigger structure for achieving multiple goals altogether. The patterns in structural designs show how unique pieces of a system can be combined together in an extensible and flexible manner.
According to the definition of Gang of Four - Decouple an abstraction from its implementation so that the two can vary independently.
Simply put, a facade encapsulates a complex subsystem behind a simple interface. It hides much of the complexity and makes the subsystem easy to use.
Besides a much simpler interface, there's one more benefit of using this design pattern. It decouples a client implementation from the complex subsystem.
It has three part-
- Complex Subsystem
- Facade
- Client Code
// Facade
class HomeTheaterFacade {
private Amplifier amplifier;
private DvdPlayer dvdPlayer;
private Projector projector;
public HomeTheaterFacade() {
this.amplifier = new Amplifier();
this.dvdPlayer = new DvdPlayer();
this.projector = new Projector();
}
public void watchMovie(String movie) {
System.out.println("Get ready to watch a movie...");
amplifier.on();
amplifier.setVolume(10);
dvdPlayer.on();
dvdPlayer.play(movie);
projector.on();
projector.setInput(dvdPlayer);
}
public void endMovie() {
System.out.println("Shutting down the home theater...");
amplifier.off();
dvdPlayer.stop();
dvdPlayer.off();
projector.off();
}
}
We've hid all the complexity in two methods: watchMovie() and endMovie().
Now the client code are:
// Client Code
public class Main {
public static void main(String[] args) {
HomeTheaterFacade homeTheater = new HomeTheaterFacade();
// Start watching a movie
homeTheater.watchMovie("The Avengers");
System.out.println("\n");
// End the movie session
homeTheater.endMovie();
}
}
Output:
Get ready to watch a movie...
Amplifier turned on
Setting amplifier volume to 10
DVD player turned on
Playing movie: The Avengers
Projector turned on
Setting input to DVD player
Shutting down the home theater...
Amplifier turned off
Movie stopped
DVD player turned off
Projector turned off
- Simplified Interface
- Decoupling
- Encapsulation
- Improved Maintainability
- Code Reusability
When we need to connect to two interfaces that are not compatible to each other, we can use Adpter Design Pattern. This situation arises when a legacy code has to be integrated with new code. Let's dive into example...
Student.java
public interface Student {
public String getName();
public String getSurName();
public String getEmail();
}
This interface is implemented by collegeStudent* class.
CollegeStudent.java
public class CollegeStudent implements Student{
String name;
String SurName;
String email;
public CollegeStudent(String name, String surname, String email){
this.name = name;
this.SurName = surname;
this.email = email;
}
public String getName() {
return name;
}
@Override
public String getSurName() {
return SurName;
}
@Override
public String getEmail() {
return email;
}
}
Again, we have another class called SchoolStudent which does not implement Student interface.
SchoolStudent.java
public class SchoolStudent{
private String firstName;
private String lastName;
private String emailAddress;
SchoolStudent(String firstName, String lastName, String emailAddress){
this.firstName = firstName;
this.lastName = lastName;
this.emailAddress = emailAddress;
}
public String getFirstName(){
return firstName;
}
public String getLastName(){
return lastName;
}
public String getEmailAddress(){
return emailAddress;
}
}
In the StudentClient class we are adding Student class that is adding CollegeStudent as it is implementing Student but SchoolStudent not implementing it. So we cannot add it students lists. So, we need a an adapter that will bind the SchoolStudent class info to the adapter which will again implement the Student interface. So, now can add it to students list.
SchoolStudentAdapter.java
public class SchoolStudentAdapter implements Student{
private SchoolStudent schoolStudent;
public SchoolStudentAdapter(SchoolStudent schoolStudent){
this.schoolStudent = schoolStudent;
}
@Override
public String getName() {
return this.schoolStudent.getFirstName();
}
@Override
public String getSurName() {
return this.schoolStudent.getLastName();
}
@Override
public String getEmail() {
return this.schoolStudent.getEmailAddress();
}
}
StudentClient.java
public class StudentClient {
public List<Student> getStudentList(){
List<Student> students = new ArrayList<>();
SchoolStudent schoolStudent = new SchoolStudent("x", "y", "z");
CollegeStudent collegeStudent = new CollegeStudent("x", "y", "z");
students.add(collegeStudent);
students.add(new SchoolStudentAdapter(schoolStudent));
return students;
}
}
Provide a class which will limit access to another class like when you want to control access i.e. database. For example, you would want to contorl the delete query available only for certain users like admin. The Proxy acts as an intermediary between the client and the real object, allowing the proxy to perform additional tasks before or after accessing the real object.
// Step 1: Create an interface for the real object and define its methods
interface Image {
void display();
}
// Step 2: Create a concrete implementation of the real object
class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadFromDisk(); // Simulating the time-consuming task of loading an image from disk
}
private void loadFromDisk() {
System.out.println("Loading image from disk: " + filename);
}
public void display() {
System.out.println("Displaying image: " + filename);
}
}
// Step 3: Create a proxy class that implements the same interface as the real object
class ImageProxy implements Image {
private String filename;
private RealImage realImage;
public ImageProxy(String filename) {
this.filename = filename;
}
public void display() {
if (realImage == null) {
realImage = new RealImage(filename);
}
realImage.display();
}
}
// Step 4: Demonstrate the usage of the proxy
public class Main {
public static void main(String[] args) {
// Create a proxy object
Image image = new ImageProxy("image.jpg");
// The real object is loaded only when the display() method is called
image.display();
// The real object is not loaded again as it has already been loaded
image.display();
}
}
The Composite Design Pattern is a structural design pattern to compose objects into tree structures to represent part-whole hierarchies. This pattern allows you to treat individual objects and compositions of objects uniformly.
Component: This is the base interface or abstract class for all objects in the composition. It declares the common operations that can be performed on both simple and complex objects.
Leaf: A Leaf is a basic element in the composition that does not have any children. It implements the Component interface, performing the operations defined in the Component.
Composite: This is a complex component that may have children. A Composite implements the Component interface and is capable of holding other components (either Leafs or other Composites). It delegates the operations to its children.
Client: The client interacts with the objects in the composition through the Component interface. It treats all objects (both simple and composite) uniformly.
If leaf node performs any operation then the same operation should be performed on composite node.