I'm always excited to take on new projects and collaborate with innovative minds.

Phone

+1 234 567 890

Email

contact@botble.com

Website

https://botble.com

Address

123 Main Street, New York, NY 10001

Social

Design Pattern Complete

Design patterns are common solutions to recurring design problems in software development. They represent best practices and offer reusable and adaptable solutions to commonly faced challenges in software architecture and design.

Design Pattern Complete

                            Design patterns are reusable solutions to common software design problems. They improve readability, reusability, scalability, and maintainability. Key types include:

Creational (e.g., Singleton, Factory)
Structural (e.g., Adapter, Decorator)
Behavioral (e.g., Observer, Strategy)
They enhance code quality and team communication.
 

                                              Creational Design Pattern 
                                               
package com.niteshsynergy.designPattern.CreationalDesign;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;

public class Demo1 {
   public static void main(String[] args) throws Exception {
     /*  Singleton s1 = Singleton.getInstance();
       System.out.println(s1.hashCode());
       Singleton s2 = Singleton.getInstance();
       System.out.println(s2.hashCode());
       */
       BreakWithReflection.breakWithReflection();
       BreakWithSerialization.breakWithSerialization();
       BreakWithCloning.breakWithCloning();
       BreakWithMultithreading.breakWithMultithreading();
       BreakWithGarbageCollection.breakWithGarbageCollection();
       BreakWithSubclassing.breakWithSubclassing();
   }
   //Prevention Strategies:
   //Prevent Reflection: Add a check in the private constructor.
  /* private Singleton() {
       if (instance != null) {
           throw new IllegalStateException("Instance already created!");
       }
   }*/
   //Serialization Protection: Implement readResolve().

 /*  protected Object readResolve() {
       return instance;
   }*/
   //Prevent Cloning: Override clone() and throw an exception.
   //java
   //Copy code
   @Override
   protected Object clone() throws CloneNotSupportedException {
       throw new CloneNotSupportedException("Cannot clone singleton");
   }

   //Thread-Safe Initialization: Use Bill Pugh Singleton or Double-Checked Locking.
   //Final Class: Declare the class as final to prevent subclassing.


}
//Code to break: 1. Reflection
class BreakWithReflection {
   public static void breakWithReflection() {
       try {
           Singleton instance1 = Singleton.getInstance();

           Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
           constructor.setAccessible(true); // Bypass private access

           Singleton instance2 = constructor.newInstance();
           System.out.println(" From breakWithReflection");
           System.out.println(instance1.hashCode());
           System.out.println(instance2.hashCode());
       } catch (Exception e) {
           e.printStackTrace();
       }
   }
}
//2. Serialization/Deserialization way break
class BreakWithSerialization {
   public static void breakWithSerialization() throws Exception {
       Singleton instance1 = Singleton.getInstance();

       // Serialize instance
       ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("singleton.ser"));
       out.writeObject(instance1);
       out.close();

       // Deserialize instance
       ObjectInputStream in = new ObjectInputStream(new FileInputStream("singleton.ser"));
       Singleton instance2 = (Singleton) in.readObject();
       in.close();
       System.out.println(" From breakWithSerialization");
       System.out.println(instance1.hashCode());
       System.out.println(instance2.hashCode());
   }
}
// 3. Cloning way break
class BreakWithCloning {
   public static void breakWithCloning() throws CloneNotSupportedException {
       Singleton instance1 = Singleton.getInstance();

       Singleton instance2 = (Singleton) instance1.clone(); // If clone() is accessible
       System.out.println(" From breakWithCloning");

       System.out.println(instance1.hashCode());
       System.out.println(instance2.hashCode());
   }

}
// 4. Multithreading (Race Condition)

class BreakWithMultithreading {
   public static void breakWithMultithreading() {
       Runnable task = () -> {
           Singleton instance = Singleton.getInstance();
           System.out.println(instance.hashCode());
       };

       Thread thread1 = new Thread(task);
       Thread thread2 = new Thread(task);
       System.out.println(" From BreakWithMultithreading");
       thread1.start();
       thread2.start();
   }
}
//5. Garbage Collection
class BreakWithGarbageCollection {
   public static void breakWithGarbageCollection() {
       Singleton instance = Singleton.getInstance();
       WeakReference<Singleton> weakRef = new WeakReference<>(instance);

       instance = null; // Nullify the strong reference
       System.gc(); // Force garbage collection
       System.out.println(" From breakWithGarbageCollection");

       Singleton newInstance = Singleton.getInstance();
       System.out.println(weakRef.get() == newInstance); // False if GC removed the weak reference
   }
}
//6. Subclassing
class BreakWithSubclassing {
   public static void breakWithSubclassing() {
    //   Singleton subclassInstance = new Singleton() {}; // Anonymous subclass
   //    System.out.println(subclassInstance.hashCode());
   }
}
class Singleton{
   private static Singleton instance;
   private Singleton(){

   }
   public static Singleton getInstance() {
       if (instance == null) {
           instance = new Singleton();
       }
       return instance;
   }
   // // If clone() is accessible, // 3. Cloning way break
   @Override
   public Object clone() throws CloneNotSupportedException {
       return super.clone();
   }
   public void showMessage() {
       System.out.println("Singleton Instance");
   }
}
/*
Design Pattern Types:
1. Creational Patterns
Creational patterns deal with object creation mechanisms, optimizing object creation based on requirements.
//  Singleton Pattern
Use Case:
Database connection pool.
Logger class.
Configuration management in applications.
*/

Here's the summary of perfect Singleton rules in bullet points:
Private Constructor: Prevent instance creation from outside the class.
Single Static Instance: Use a static variable to hold the single instance.
Global Access: Provide a public static method (getInstance()) to access the instance.
Thread-Safety: Ensure thread safety with methods like Double-Checked Locking or Bill Pugh Singleton.
Prevent Reflection: Throw an exception in the constructor if an instance already exists.
Prevent Cloning: Override the clone() method and throw CloneNotSupportedException.
Serialization Protection: Implement the readResolve() method to ensure the Singleton instance is maintained during serialization/deserialization.
Garbage Collection Protection: Avoid Singleton object being garbage collected by maintaining a strong reference.
Prevent Subclassing: Declare the Singleton class as final to prevent subclassing.
Efficient Initialization: Use Lazy Initialization (with Double-Checked Locking) or Eager Initialization to initialize the instance.


package com.niteshsynergy.designPattern.CreationalDesign;
import java.io.Serializable;

public final class Demo02PerfectSingleton implements Serializable, Cloneable {

   // Private static inner class for lazy-loaded, thread-safe instance creation
   private static class SingletonHelper {
       private static final Demo02PerfectSingleton INSTANCE = new Demo02PerfectSingleton();
   }

   // Private constructor to prevent instantiation
   private Demo02PerfectSingleton() {
       if (SingletonHelper.INSTANCE != null) {
           throw new IllegalStateException("Instance already created!");
       }
   }

   // Public method to access the singleton instance
   public static Demo02PerfectSingleton getInstance() {
       return SingletonHelper.INSTANCE;
   }

   // Prevents instance creation during deserialization
   protected Object readResolve() {
       return getInstance();
   }

   // Prevents instance creation through cloning
   @Override
   protected Object clone() throws CloneNotSupportedException {
       throw new CloneNotSupportedException("Cloning is not allowed for Singleton");
   }

   // Example method to demonstrate functionality
   public void showMessage() {
       System.out.println("Perfect Singleton Instance");
   }
}


package com.niteshsynergy.designPattern.CreationalDesign;

public class Demo03FactoryPattern {
   public static Payment getPayment(String type) {
       return switch (type) {
           case "CREDIT" -> new CreditCardPayment();
           case "PAYPAL" -> new PayPalPayment();
           default -> throw new IllegalArgumentException("Unknown payment type");
       };


   }
   public static void main(String[] args) {
       // Usage
       Payment payment = Demo03FactoryPattern.getPayment("CREDIT");
       payment.processPayment();
   }
}
interface Payment {
   void processPayment();
}

class CreditCardPayment implements Payment {
   public void processPayment() {
       System.out.println("Processing Credit Card Payment");
   }
}

class PayPalPayment implements Payment {
   public void processPayment() {
       System.out.println("Processing PayPal Payment");
   }
}


/*
Factory Pattern
Use Case:
Creation of different types of objects with a common interface, such as vehicle or payment types.
*/


package com.niteshsynergy.designPattern.CreationalDesign;

import java.util.HashMap;
import java.util.Map;

public class Demo04PrototypePattern {
   //1. Resource-Intensive Object Creation:When the creation of an object is expensive (e.g., involves complex initialization or significant memory and time overhead), cloning an existing object is much faster and more efficient.
   //Game Development: Copying enemies, characters, or terrain objects with slightly different attributes.
   //Database Connections: Creating a pool of pre-configured database connections to avoid reinitializing connections repeatedly.


   //2. Object Initialization with Many Configurations:When objects require numerous configuration settings or parameters, cloning a prototype with pre-configured settings simplifies the process.

   //Document Templates: Copying a prototype document template (e.g., invoices, resumes, letters) to create customized versions without rebuilding the structure.
   //UI Components: Copying pre-configured buttons or UI elements with different labels or sizes.

 

   //3. Avoiding Subclass Explosion:In scenarios where an application needs many variations of an object, the Prototype Pattern reduces the need for an extensive hierarchy of subclasses.
   //Shape Objects: Instead of creating separate classes for each shape variation (Circle, Rectangle, Triangle, etc.), use a prototype to clone and modify the properties like size and color.


   //4. Dynamic System Configurations
   //GUI Customization: Allowing users to define their GUI themes, and then cloning the customized theme for new windows or components.
   //Workflow Engines: Duplicating task definitions or states in workflow systems.

   //5. Creating Objects Without Exposing Complex Logic:When the object creation process involves complex logic, the Prototype Pattern encapsulates the logic inside the prototype object, making it easier for clients to clone rather than recreate.

   //Cryptographic Keys: Cloning pre-generated cryptographic keys instead of recalculating them.

   //6. Performance Optimization in High-Frequency Object Creation:When the application frequently creates similar objects, cloning reduces the performance bottleneck caused by repeated initialization.
   //Particle Systems in Graphics: Copying particles in animations or visual effects like explosions, rain, or fire.

   //7. Versioning of Objects:When you need to maintain multiple versions of an object or preserve its state before modifications, the Prototype Pattern provides an easy mechanism for doing so.
   //State Management: In a text editor, cloning the document before making changes allows for undo/redo functionality.

   public static void main(String[] args) {
       // Get a cloned Goblin
       Goblin goblin1 = (Goblin) EnemyFactory.getEnemy("Goblin");
       goblin1.setHealth(120); // Customize the clone
       System.out.println("Cloned Goblin 1: " + goblin1);

       // Get another cloned Goblin
       Goblin goblin2 = (Goblin) EnemyFactory.getEnemy("Goblin");
       System.out.println("Cloned Goblin 2: " + goblin2);

       // Get a cloned Orc
       Orc orc1 = (Orc) EnemyFactory.getEnemy("Orc");
       orc1.setDefense(150); // Customize the clone
       System.out.println("Cloned Orc 1: " + orc1);

       // Get another cloned Orc
       Orc orc2 = (Orc) EnemyFactory.getEnemy("Orc");
       System.out.println("Cloned Orc 2: " + orc2);

   }


}
interface EnemyPrototype extends Cloneable {
   EnemyPrototype clone(); // Method to clone the object
}
class Goblin implements EnemyPrototype {
   private int health;
   private int attackPower;

   public Goblin(int health, int attackPower) {
       this.health = health;
       this.attackPower = attackPower;
   }

   // Getter and Setter methods
   public int getHealth() {
       return health;
   }

   public void setHealth(int health) {
       this.health = health;
   }

   public int getAttackPower() {
       return attackPower;
   }

   public void setAttackPower(int attackPower) {
       this.attackPower = attackPower;
   }

   // Clone method implementation
   @Override
   public EnemyPrototype clone() {
       try {
           return (EnemyPrototype) super.clone();
       } catch (CloneNotSupportedException e) {
           throw new AssertionError();
       }
   }

   @Override
   public String toString() {
       return "Goblin{health=" + health + ", attackPower=" + attackPower + "}";
   }
}

class Orc implements EnemyPrototype {
   private int health;
   private int defense;

   public Orc(int health, int defense) {
       this.health = health;
       this.defense = defense;
   }

   // Getter and Setter methods
   public int getHealth() {
       return health;
   }

   public void setHealth(int health) {
       this.health = health;
   }

   public int getDefense() {
       return defense;
   }

   public void setDefense(int defense) {
       this.defense = defense;
   }

   // Clone method implementation
   @Override
   public EnemyPrototype clone() {
       try {
           return (EnemyPrototype) super.clone();
       } catch (CloneNotSupportedException e) {
           throw new AssertionError();
       }
   }

   @Override
   public String toString() {
       return "Orc{health=" + health + ", defense=" + defense + "}";
   }
}

class EnemyFactory {
   private static final Map<String, EnemyPrototype> prototypes = new HashMap<>();

   static {
       // Register default prototypes
       prototypes.put("Goblin", new Goblin(100, 50));
       prototypes.put("Orc", new Orc(200, 100));
   }

   public static EnemyPrototype getEnemy(String type) {
       EnemyPrototype prototype = prototypes.get(type);
       if (prototype != null) {
           return prototype.clone(); // Clone the prototype
       }
       throw new IllegalArgumentException("Enemy type not recognized: " + type);
   }
}
/*
The Prototype Pattern is a creational design pattern that allows you to create new objects by copying an existing object (the prototype) rather than creating them from scratch. It provides a mechanism for object cloning, which is useful when object creation is resource-intensive.


Key Features of the Prototype Pattern
Object Cloning: New objects are created by duplicating an existing object.
Improves Performance: Reduces the overhead of creating objects from scratch.
Avoids Subclassing: You don't need a hierarchy of classes to customize the object creation.

*/


package com.niteshsynergy.designPattern.CreationalDesign;

public class Demo05AbstractFactoryPattern {
   public static void main(String[] args) {
       // Decide the platform at runtime
       GUIFactory factory;

       String osName = System.getProperty("os.name").toLowerCase();
       if (osName.contains("mac")) {
           factory =null; //new MacOSFactory();
       } else {
           factory =null;// new WindowsFactory();
       }

       // Create the application
       Application app = new Application(factory);
       app.paint();
   }
}
//1. Abstract Factory Interface (GUIFactory)
interface GUIFactory {
   Button createButton();
   Checkbox createCheckbox();
}
//2. Abstract Product Interfaces (Button, Checkbox)
interface Button {
   void paint();
}
//3. Concrete Products for Windows (WindowsButton, WindowsCheckbox)
interface Checkbox {
   void paint();
}
//4. Concrete Products for MacOS (MacOSButton, MacOSCheckbox)
class WindowsButton implements Button {
   @Override
   public void paint() {
       System.out.println("Rendering a Windows Button.");
   }
}
//5. Concrete Factories (WindowsFactory, MacOSFactory)
class WindowsCheckbox implements Checkbox {
   @Override
   public void paint() {
       System.out.println("Rendering a Windows Checkbox.");
   }
}
//6. Client Code (Application)
class Application {
   private Button button;
   private Checkbox checkbox;

   public Application(GUIFactory factory) {
       this.button = factory.createButton();
       this.checkbox = factory.createCheckbox();
   }

   public void paint() {
       button.paint();
       checkbox.paint();
   }
}

/*
Scenario: Cross-Platform UI Framework

The Abstract Factory Pattern is used when you need to create families of related objects (e.g., buttons, checkboxes) for different platforms (e.g., Windows, MacOS).
Problem:

You need to create platform-specific UI components (e.g., buttons, checkboxes) but want the client code to remain independent of the specific platform or implementation.
Solution:

The Abstract Factory Pattern allows the creation of a common interface (GUIFactory) for platform-specific factories (WindowsFactory, MacOSFactory). These factories create platform-specific UI components (like WindowsButton, MacOSButton) that implement common interfaces (like Button).
Use Case in Detail:

Cross-Platform Compatibility: A UI framework must create different components for different operating systems (e.g., Windows vs. MacOS). You want the same code to run on both platforms but create the correct type of UI component for each.
UI Consistency: Each platform has a different style for its components, so you need to ensure that buttons, checkboxes, etc., follow the appropriate design style for the platform.
Example in Practice:

Windows: On Windows, a button might have a specific appearance, and checkboxes may behave differently from MacOS.
MacOS: On MacOS, the buttons and checkboxes will have distinct designs that match the operating system's style guide.
How the Abstract Factory Solves This:

The client code (e.g., Application) doesn’t know which factory it is using; it just asks for a Button or a Checkbox. The correct platform-specific factory (WindowsFactory, MacOSFactory) is provided based on the environment, and the client gets the corresponding platform-specific objects.
When to Use the Abstract Factory Pattern:

Multiple Variants of a Product: When there are multiple variations of a product (e.g., different UI components for different platforms) that should be created by a single factory interface.
Platform-Specific Object Creation: When you need to create different objects depending on the environment or system (e.g., different components for different OS like Windows, MacOS, or Linux).
Decoupling Object Creation from Client Code: When you want to isolate the client code from the actual instantiation of objects. This helps in maintaining a clean separation of concerns.
Real-World Applications:

Operating Systems: A cross-platform application, such as an IDE (Integrated Development Environment), needs different buttons, dialogs, and other UI components for Windows and MacOS. The Abstract Factory Pattern allows the same codebase to run on multiple platforms while creating the correct UI components for each.
UI Frameworks: Frameworks that support different themes or platforms, like Bootstrap (web) or JavaFX (desktop), can use this pattern to create UI components specific to each platform.

*/

/*
Explanation of Code
Abstract Factory Interface (GUIFactory): This interface defines methods for creating abstract products, in this case, buttons and checkboxes.
Abstract Products (Button and Checkbox): These interfaces define the common methods (e.g., paint()) for the product family. Both Button and Checkbox have specific implementations depending on the platform.
Concrete Products for Windows (WindowsButton, WindowsCheckbox): These classes implement the Button and Checkbox interfaces for Windows.
Concrete Products for MacOS (MacOSButton, MacOSCheckbox): These classes implement the Button and Checkbox interfaces for MacOS.
Concrete Factories (WindowsFactory, MacOSFactory): These factories implement the GUIFactory interface and create platform-specific objects. Depending on the platform, the correct factory will be used.
Client (Application): The client (Application) uses the abstract factory to get instances of Button and Checkbox. It remains agnostic to the actual type of objects being created, only interacting with them through their common interfaces.
Demo Class (AbstractFactoryDemo): This class determines the platform at runtime (Windows or MacOS) and uses the appropriate factory to create the UI components. Then, it calls the paint() method to render the components.

*/
package com.niteshsynergy.designPattern.CreationalDesign;

public class Demo06BuilderPattern {
   //Scenario: Creating a Complex Meal Order
   /*
   Consider a Meal Order at a restaurant. A meal can consist of several components such as a main dish, a side dish, a drink, and a dessert. Each of these components can be customized, and you can have various combinations based on customer preferences.

Rather than creating a complicated constructor or a large number of setter methods, the Builder Pattern can help construct the meal in a step-by-step manner.
    */

   //4. Client Code (using MealBuilder)

   public static void main(String[] args) {
       // Use builder pattern to construct complex objects
       MealBuilder mealBuilder = new MealBuilder();
       MealDirector director = new MealDirector(mealBuilder);

       // Build a vegetarian meal
       Meal vegetarianMeal = director.createVegetarianMeal();
       System.out.println("Vegetarian Meal: " + vegetarianMeal);

       // Build a non-vegetarian meal
       Meal nonVegetarianMeal = director.createNonVegetarianMeal();
       System.out.println("Non-Vegetarian Meal: " + nonVegetarianMeal);

       // Use builder directly for custom meal
       Meal customMeal = mealBuilder.setMainDish("Pasta")
               .setSideDish("Garlic Bread")
               .setDrink("Wine")
               .setDessert("Tiramisu")
               .build();
       System.out.println("Custom Meal: " + customMeal);

   }
}

//1. Product Class (Meal)
class Meal {
   private String mainDish;
   private String sideDish;
   private String drink;
   private String dessert;

   public void setMainDish(String mainDish) {
       this.mainDish = mainDish;
   }

   public void setSideDish(String sideDish) {
       this.sideDish = sideDish;
   }

   public void setDrink(String drink) {
       this.drink = drink;
   }

   public void setDessert(String dessert) {
       this.dessert = dessert;
   }

   @Override
   public String toString() {
       return "Meal{" +
               "mainDish='" + mainDish + '\'' +
               ", sideDish='" + sideDish + '\'' +
               ", drink='" + drink + '\'' +
               ", dessert='" + dessert + '\'' +
               '}';
   }
}
//2. Builder Class (MealBuilder)
class MealBuilder {
   private Meal meal;

   public MealBuilder() {
       meal = new Meal();
   }

   public MealBuilder setMainDish(String mainDish) {
       meal.setMainDish(mainDish);
       return this;
   }

   public MealBuilder setSideDish(String sideDish) {
       meal.setSideDish(sideDish);
       return this;
   }

   public MealBuilder setDrink(String drink) {
       meal.setDrink(drink);
       return this;
   }

   public MealBuilder setDessert(String dessert) {
       meal.setDessert(dessert);
       return this;
   }

   public Meal build() {
       return meal;
   }
}
//3. Director Class (MealDirector) (Optional)
class MealDirector {
   private MealBuilder builder;

   public MealDirector(MealBuilder builder) {
       this.builder = builder;
   }

   public Meal createVegetarianMeal() {
       return builder.setMainDish("Vegetable Stir Fry")
               .setSideDish("Salad")
               .setDrink("Fruit Juice")
               .setDessert("Fruit Salad")
               .build();
   }

   public Meal createNonVegetarianMeal() {
       return builder.setMainDish("Grilled Chicken")
               .setSideDish("Fries")
               .setDrink("Soda")
               .setDessert("Chocolate Cake")
               .build();
   }
}


/*
Use Case for Builder Pattern
The Builder Pattern is useful when the object you are trying to create is complex and involves several steps, or when you need to create different representations of the same type of object. It allows for constructing an object step by step, giving you control over the construction process while keeping the code clean and readable.
*/
 

                        Structural Design Pattern
                          AdapterPattern
package com.niteshsynergy.designPattern.StructuralDesign;

public class Demo01AdapterPattern {
   //Scenario: Integrating Old Library with New System
   /*
   Imagine you are working with a new system that uses a set of modern USB-based devices, but you have an old system that
   communicates with devices using serial ports (RS-232). The old system can't be directly connected to USB devices.
    The Adapter Pattern can be used to adapt the old serial system interface to the new USB-based system by creating an adapter.
    */
   public static void main(String[] args) {
       // Old serial device
       SerialDevice serialDevice = new SerialDevice();

       // Using adapter to connect the serial device as if it were a USB device
       USBDevice usbDevice = new SerialToUSBAdapter(serialDevice);
       usbDevice.connectWithUSB();  // The new system now interacts with the old system via USB
   }
   /*
   When to Use the Adapter Pattern
Incompatible Interfaces: When you need to integrate an existing class into a system that expects a different interface.

Legacy System Integration: When you have a legacy system or third-party library whose interface cannot be changed but needs to be used in a modern system.

Multiple Implementations: When you have different systems using different interfaces, but you want to create a unified way to communicate with all of them.

Decoupling Systems: When you want to decouple the client from the implementation details of the underlying system.
    */
}
//1. Target Interface (USBDevice)
interface USBDevice {
   void connectWithUSB();
}
//2. Adaptee Class (SerialDevice)
class SerialDevice {
   public void connectWithSerialPort() {
       System.out.println("Connecting with serial port...");
   }
}

//3. Adapter Class (SerialToUSBAdapter)
class SerialToUSBAdapter implements USBDevice {
   private SerialDevice serialDevice;

   public SerialToUSBAdapter(SerialDevice serialDevice) {
       this.serialDevice = serialDevice;
   }

   @Override
   public void connectWithUSB() {
       System.out.println("Adapting to USB interface...");
       serialDevice.connectWithSerialPort();  // Delegating call to the adaptee
   }
}

/*
Use Case for Adapter Pattern
The Adapter Pattern is a structural design pattern that allows incompatible interfaces to work together. It acts as a bridge between two interfaces. The Adapter Pattern is often used when you have an existing class or library, but its interface is not compatible with the system you want to integrate it with.

*/
/*
Explanation of Code
Target Interface (USBDevice): This defines the interface that the new system expects. In this case, the new system communicates with devices that use the connectWithUSB() method.

Adaptee Class (SerialDevice): This is the existing class that provides the connectWithSerialPort() method. It's the old system or interface that is incompatible with the new one.

Adapter Class (SerialToUSBAdapter): This class implements the USBDevice interface, but internally it delegates the call to the SerialDevice class. It adapts the old serial device interface to the new USB interface by calling the connectWithSerialPort() method on the SerialDevice.

Client Code (Client): In the client code, the new system (which expects a USBDevice) interacts with the old system (which provides SerialDevice). The SerialToUSBAdapter adapts the old interface to the new one, so the client code doesn't need to change.
*/

                          ProxyPattern
package com.niteshsynergy.designPattern.StructuralDesign;

public class Demo02ProxyPattern {
   //Scenario: Image Display with Proxy (Lazy Loading)
   /*
   Imagine you have a large image file that is not immediately needed by the client application. To optimize performance and reduce memory usage, you can use the Proxy Pattern to delay the loading of the image until it is actually required. This is known as lazy loading.

In this case, the Proxy acts as a placeholder that only loads the real image when necessary, while the client interacts with the proxy as if it were the actual image.
    */
   public static void main(String[] args) {
       Image image1 = new ProxyImage("image1.jpg");
       Image image2 = new ProxyImage("image2.jpg");

       // Image is loaded and displayed only when the `display` method is called
       image1.display();
       System.out.println();

       // Image is already loaded, so only display it
       image2.display();
   }
}
//1. Subject Interface (Image)
interface Image {
   void display();
}
//2. Real Subject (RealImage)
class RealImage implements Image {
   private String fileName;

   public RealImage(String fileName) {
       this.fileName = fileName;
       loadImage();
   }

   private void loadImage() {
       System.out.println("Loading image: " + fileName);
   }

   @Override
   public void display() {
       System.out.println("Displaying image: " + fileName);
   }
}
//3. Proxy (ProxyImage)
class ProxyImage implements Image {
   private RealImage realImage;
   private String fileName;

   public ProxyImage(String fileName) {
       this.fileName = fileName;
   }

   @Override
   public void display() {
       // Only load and display the image if it's not already loaded
       if (realImage == null) {
           realImage = new RealImage(fileName);
       }
       realImage.display(); // Delegate to the real object
   }
}

/*
Use Case for Proxy Pattern
The Proxy Pattern is a structural design pattern that provides an object representing another object. A proxy is used to control access to the original object, enabling additional functionality like lazy loading, access control, logging, or caching without changing the original object.

The Proxy Pattern can be particularly useful when:

You want to control access to a resource or object.
Performance optimization is required (e.g., lazy loading or caching).
Security is needed by controlling access permissions to a remote or sensitive resource.
Network communication is involved, where you want to act as an intermediary to access the actual service or resource.
*/

/*
Explanation of Code
Subject Interface (Image): This is the common interface that both the RealSubject and the Proxy implement. It defines the display() method that will be used by the client.

RealSubject (RealImage): This is the actual class that represents the real image. It is responsible for loading the image and displaying it. The image is loaded when the object is created, which can be resource-intensive.

Proxy (ProxyImage): The proxy class controls access to the RealSubject. It does not load the image until the display() method is called, implementing the lazy loading functionality. The proxy checks if the RealSubject has been instantiated before calling its display() method.

Client Code (ProxyPatternDemo): The client interacts with the ProxyImage as though it were the real image. The proxy ensures that the real image is loaded only when needed, optimizing resource usage.
*/
                      CompositePattern
package com.niteshsynergy.designPattern.StructuralDesign;

import java.util.ArrayList;
import java.util.List;

public class Demo03CompositePattern {
   //Scenario: File System
   /*
   A file system is a classic example where the Composite Pattern can be applied.
   In a file system, you can have files and directories (which can contain other files
   and directories). The directories act as composite objects, while files are leaf objects. Both can be treated uniformly as items in the file system, allowing operations like displaying contents or calculating sizes to be applied to both individual files and directories, regardless of the hierarchy level.
    */
   public static void main(String[] args) {
       // Creating files
       File file1 = new File("File1.txt", 10);
       File file2 = new File("File2.jpg", 20);
       File file3 = new File("File3.pdf", 30);

       // Creating directories
       Directory directory1 = new Directory("Documents");
       Directory directory2 = new Directory("Images");

       // Adding files to directories
       directory1.addItem(file1);
       directory1.addItem(file3);
       directory2.addItem(file2);

       // Creating the root directory and adding subdirectories
       Directory rootDirectory = new Directory("Root");
       rootDirectory.addItem(directory1);
       rootDirectory.addItem(directory2);

       // Displaying details of the entire directory structure
       rootDirectory.showDetails();
   }

}
//1. Component Interface (FileSystemItem)

interface FileSystemItem {
   void showDetails();
}
//2. Leaf Class (File)
class File implements FileSystemItem {
   private String name;
   private long size;

   public File(String name, long size) {
       this.name = name;
       this.size = size;
   }

   @Override
   public void showDetails() {
       System.out.println("File: " + name + " | Size: " + size + "KB");
   }
}
//3. Composite Class (Directory)
class Directory implements FileSystemItem {
   private String name;
   private     List<FileSystemItem> items = new ArrayList<>();

   public Directory(String name) {
       this.name = name;
   }

   public void addItem(FileSystemItem item) {
       items.add(item);
   }

   public void removeItem(FileSystemItem item) {
       items.remove(item);
   }

   @Override
   public void showDetails() {
       System.out.println("Directory: " + name);
       for (FileSystemItem item : items) {
           item.showDetails();  // Recursively show details of all items in the directory
       }
   }
}


/*
The Composite Pattern is a structural design pattern that allows you to compose objects into tree-like structures to represent part-whole hierarchies. It enables clients to treat individual objects and compositions of objects uniformly.

This pattern is useful when you have a hierarchy or tree structure of objects, where the individual objects (leaves) and the composite objects (branches) should be treated in the same way. This allows for building complex structures while maintaining simplicity in how they are accessed and interacted with.
*/

/*
Explanation of Code
Component Interface (FileSystemItem): This is the common interface that both leaf objects (like File) and composite objects (like Directory) implement. It defines the showDetails() method, which will be used to display the details of both files and directories.

Leaf Class (File): This represents individual objects (files) that do not contain other objects. It implements the FileSystemItem interface and provides the showDetails() method, which outputs the file's name and size.

Composite Class (Directory): This represents a collection of FileSystemItem objects, which could be both files and directories. It implements the FileSystemItem interface and provides the showDetails() method, which iterates through all items in the directory, calling their showDetails() method recursively.

Client Code (CompositePatternDemo): In the client code, files and directories are created and organized into a tree-like structure. The showDetails() method is called on the root directory, which then recursively displays the details of all files and subdirectories in the tree.


*/
                           FacadePattern
package com.niteshsynergy.designPattern.StructuralDesign;

public class Demo04FacadePattern {

   //Scenario: Home Theater System
   /*
   Imagine you have a home theater system with several components such as a TV, DVD player, Sound System, Lights, and Air Conditioning. Each of these components has its own set of methods and configurations, and interacting with them individually can be complex.

A Facade can be used to create a HomeTheaterFacade that simplifies the process of turning on or off the home theater system. Instead of the client interacting with each component directly, it interacts with the Facade, which delegates the tasks to the appropriate components.
    */
   public static void main(String[] args) {
       // Creating subsystem objects
       TV tv = new TV();
       DVDPlayer dvdPlayer = new DVDPlayer();
       SoundSystem soundSystem = new SoundSystem();
       Lights lights = new Lights();
       AirConditioner airConditioner = new AirConditioner();

       // Creating the facade
       HomeTheaterFacade homeTheaterFacade = new HomeTheaterFacade(tv, dvdPlayer, soundSystem, lights, airConditioner);

       // Using the facade to watch a movie
       homeTheaterFacade.watchMovie("Inception");

       System.out.println("\nMovie has ended!");

       // Using the facade to shut down the system
       homeTheaterFacade.endMovie();
   }
   /*

Facade Pattern: Use Case & Code Example
Use Case for Facade Pattern
The Facade Pattern is a structural design pattern that provides a simplified interface to a complex subsystem, hiding its complexities from the client. The facade acts as a wrapper that delegates the client’s requests to the appropriate components of the subsystem, providing a cleaner and easier-to-use interface.

The Facade Pattern is particularly useful when:

You want to simplify interactions with a complex subsystem or set of APIs.
You want to reduce dependencies on external code by providing a higher-level interface.
You need to provide a unified interface to a set of related functionalities that are spread across multiple classes.
Scenario: Home Theater System
Imagine you have a home theater system with several components such as a TV, DVD player, Sound System, Lights, and Air Conditioning. Each of these components has its own set of methods and configurations, and interacting with them individually can be complex.

A Facade can be used to create a HomeTheaterFacade that simplifies the process of turning on or off the home theater system. Instead of the client interacting with each component directly, it interacts with the Facade, which delegates the tasks to the appropriate components.

Code Implementation
1. Subsystem Classes (TV, DVDPlayer, SoundSystem, etc.)
java
Copy code
// TV class
public class TV {
   public void turnOn() {
       System.out.println("TV is now ON");
   }

   public void turnOff() {
       System.out.println("TV is now OFF");
   }
}

// DVDPlayer class
public class DVDPlayer {
   public void turnOn() {
       System.out.println("DVD Player is now ON");
   }

   public void turnOff() {
       System.out.println("DVD Player is now OFF");
   }

   public void playMovie(String movie) {
       System.out.println("Playing movie: " + movie);
   }
}

// SoundSystem class
public class SoundSystem {
   public void turnOn() {
       System.out.println("Sound System is now ON");
   }

   public void turnOff() {
       System.out.println("Sound System is now OFF");
   }

   public void setVolume(int volume) {
       System.out.println("Sound System volume set to " + volume);
   }
}

// Lights class
public class Lights {
   public void dimLights() {
       System.out.println("Lights are dimmed");
   }

   public void turnOn() {
       System.out.println("Lights are ON");
   }
}

// AirConditioner class
public class AirConditioner {
   public void turnOn() {
       System.out.println("Air Conditioner is ON");
   }

   public void turnOff() {
       System.out.println("Air Conditioner is OFF");
   }
}
2. Facade Class (HomeTheaterFacade)
java
Copy code
public class HomeTheaterFacade {
   private TV tv;
   private DVDPlayer dvdPlayer;
   private SoundSystem soundSystem;
   private Lights lights;
   private AirConditioner airConditioner;

   public HomeTheaterFacade(TV tv, DVDPlayer dvdPlayer, SoundSystem soundSystem, Lights lights, AirConditioner airConditioner) {
       this.tv = tv;
       this.dvdPlayer = dvdPlayer;
       this.soundSystem = soundSystem;
       this.lights = lights;
       this.airConditioner = airConditioner;
   }

   public void watchMovie(String movie) {
       System.out.println("Get ready to watch a movie...");
       lights.dimLights();
       airConditioner.turnOn();
       tv.turnOn();
       dvdPlayer.turnOn();
       dvdPlayer.playMovie(movie);
       soundSystem.turnOn();
       soundSystem.setVolume(5);
   }

   public void endMovie() {
       System.out.println("Shutting down the home theater...");
       lights.turnOn();
       airConditioner.turnOff();
       tv.turnOff();
       dvdPlayer.turnOff();
       soundSystem.turnOff();
   }
}
3. Client Code (FacadePatternDemo)
java
Copy code
public class FacadePatternDemo {
   public static void main(String[] args) {
       // Creating subsystem objects
       TV tv = new TV();
       DVDPlayer dvdPlayer = new DVDPlayer();
       SoundSystem soundSystem = new SoundSystem();
       Lights lights = new Lights();
       AirConditioner airConditioner = new AirConditioner();

       // Creating the facade
       HomeTheaterFacade homeTheaterFacade = new HomeTheaterFacade(tv, dvdPlayer, soundSystem, lights, airConditioner);

       // Using the facade to watch a movie
       homeTheaterFacade.watchMovie("Inception");

       System.out.println("\nMovie has ended!");

       // Using the facade to shut down the system
       homeTheaterFacade.endMovie();
   }
}
Explanation of Code
Subsystem Classes: These represent the complex components of the system, such as the TV, DVD Player, Sound System, Lights, and Air Conditioner. Each class has specific methods to turn the component on or off, and in the case of the DVD player, additional methods to play a movie.

Facade Class (HomeTheaterFacade): This class provides a simplified interface to the client. It aggregates the various subsystem objects and offers higher-level methods like watchMovie() and endMovie(), which internally invoke the corresponding actions on the subsystem objects. This way, the client does not need to deal with each component individually.

Client Code (FacadePatternDemo): The client uses the Facade to interact with the system. The client calls the watchMovie() method to set everything up (turning on the TV, playing the DVD, adjusting the sound system, etc.), and the endMovie() method to shut everything down. The client does not need to interact with each subsystem directly.

Sample Output
vbnet
Copy code
Get ready to watch a movie...
Lights are dimmed
Air Conditioner is ON
TV is now ON
DVD Player is now ON
Playing movie: Inception
Sound System is now ON
Sound System volume set to 5

Movie has ended!
Shutting down the home theater...
Lights are ON
Air Conditioner is OFF
TV is now OFF
DVD Player is now OFF
Sound System is now OFF
When to Use the Facade Pattern
Simplify Complex Subsystems: When you have a complex subsystem with many interdependent classes, the facade pattern simplifies the interaction with the subsystem by providing a unified, easy-to-use interface.

Reduce Client Dependency: When you want to minimize the number of classes a client needs to interact with. The facade pattern hides the complexities of the subsystem from the client, making the client code cleaner and easier to maintain.

Decouple Subsystems: When you want to decouple the client from the subsystems to avoid tight coupling. This allows subsystems to evolve independently without affecting the client code.

Group Related Functionality: When you have related classes (e.g., a set of components that together represent a subsystem) and you want to provide a simple interface for them.

Advantages of Facade Pattern
Simplifies Complex Systems: The facade pattern makes it easier to interact with complex subsystems by providing a simple interface.
Reduces Client Code Complexity: By using the facade, the client only interacts with one object, reducing the number of objects it needs to manage.
Loose Coupling: The client is decoupled from the subsystem, which makes it easier to modify or replace components of the subsystem without affecting the client.
Centralized Control: The facade provides a centralized location for managing interactions between the client and the subsystem, making the system easier to understand and maintain.
Disadvantages of Facade Pattern
Hides Subsystem Complexity: While the facade simplifies interaction with the system, it may also hide important details of the subsystem, making debugging or extending the system more challenging.
Single Point of Failure: The facade itself can become a bottleneck or a single point of failure if it does not delegate actions properly to the subsystem.
Real-World Examples of Facade Pattern
Home Automation Systems: In a smart home system, a facade might be used to control lights, temperature, security, and entertainment systems. The user interacts with a single interface to control everything without having to understand the underlying systems.

Database Access: A complex database system might have many classes for managing connections, transactions, queries, etc. A facade can provide a simple interface for the client to interact with the database without worrying about the internal complexities.

API Integration: When interacting with external services (like payment gateways, social media APIs, etc.), a facade can be used to simplify the integration process, providing a unified interface for various APIs that may have different communication protocols.

Video Games: In video games, a facade might control the interaction between various subsystems like graphics, physics, sound, and AI. The game developer interacts with the facade to trigger complex game behaviors without directly managing each subsystem.
    */
}
//1. Subsystem Classes (TV, DVDPlayer, SoundSystem, etc.)

// TV class
class TV {
   public void turnOn() {
       System.out.println("TV is now ON");
   }

   public void turnOff() {
       System.out.println("TV is now OFF");
   }
}

// DVDPlayer class
class DVDPlayer {
   public void turnOn() {
       System.out.println("DVD Player is now ON");
   }

   public void turnOff() {
       System.out.println("DVD Player is now OFF");
   }

   public void playMovie(String movie) {
       System.out.println("Playing movie: " + movie);
   }
}

// SoundSystem class
class SoundSystem {
   public void turnOn() {
       System.out.println("Sound System is now ON");
   }

   public void turnOff() {
       System.out.println("Sound System is now OFF");
   }

   public void setVolume(int volume) {
       System.out.println("Sound System volume set to " + volume);
   }
}

// Lights class
class Lights {
   public void dimLights() {
       System.out.println("Lights are dimmed");
   }

   public void turnOn() {
       System.out.println("Lights are ON");
   }
}

// AirConditioner class
class AirConditioner {
   public void turnOn() {
       System.out.println("Air Conditioner is ON");
   }

   public void turnOff() {
       System.out.println("Air Conditioner is OFF");
   }
}
//2. Facade Class (HomeTheaterFacade)
class HomeTheaterFacade {
   private TV tv;
   private DVDPlayer dvdPlayer;
   private SoundSystem soundSystem;
   private Lights lights;
   private AirConditioner airConditioner;

   public HomeTheaterFacade(TV tv, DVDPlayer dvdPlayer, SoundSystem soundSystem, Lights lights, AirConditioner airConditioner) {
       this.tv = tv;
       this.dvdPlayer = dvdPlayer;
       this.soundSystem = soundSystem;
       this.lights = lights;
       this.airConditioner = airConditioner;
   }

   public void watchMovie(String movie) {
       System.out.println("Get ready to watch a movie...");
       lights.dimLights();
       airConditioner.turnOn();
       tv.turnOn();
       dvdPlayer.turnOn();
       dvdPlayer.playMovie(movie);
       soundSystem.turnOn();
       soundSystem.setVolume(5);
   }

   public void endMovie() {
       System.out.println("Shutting down the home theater...");
       lights.turnOn();
       airConditioner.turnOff();
       tv.turnOff();
       dvdPlayer.turnOff();
       soundSystem.turnOff();
   }
}
/*
The Facade Pattern is a structural design pattern that provides a simplified interface to a complex subsystem, hiding its complexities from the client. The facade acts as a wrapper that delegates the client’s requests to the appropriate components of the subsystem, providing a cleaner and easier-to-use interface.

The Facade Pattern is particularly useful when:

You want to simplify interactions with a complex subsystem or set of APIs.
You want to reduce dependencies on external code by providing a higher-level interface.
You need to provide a unified interface to a set of related functionalities that are spread across multiple classes.
*/

/*
Explanation of Code
Subsystem Classes: These represent the complex components of the system, such as the TV, DVD Player, Sound System, Lights, and Air Conditioner. Each class has specific methods to turn the component on or off, and in the case of the DVD player, additional methods to play a movie.

Facade Class (HomeTheaterFacade): This class provides a simplified interface to the client. It aggregates the various subsystem objects and offers higher-level methods like watchMovie() and endMovie(), which internally invoke the corresponding actions on the subsystem objects. This way, the client does not need to deal with each component individually.

Client Code (FacadePatternDemo): The client uses the Facade to interact with the system. The client calls the watchMovie() method to set everything up (turning on the TV, playing the DVD, adjusting the sound system, etc.), and the endMovie() method to shut everything down. The client does not need to interact with each subsystem directly.
*/
                      FlyweightPattern
package com.niteshsynergy.designPattern.StructuralDesign;

import java.util.HashMap;
import java.util.Map;

public class Demo05FlyweightPattern {
   //Scenario: Text Formatting System
   /*
   Imagine you are building a text formatting system that displays large amounts of text. Each character in the text could be an object, but each character only differs in terms of the character itself (e.g., 'a', 'b', 'c') and the formatting style (e.g., bold, italic). Instead of creating a new object for each character every time a new document is created, the Flyweight Pattern can be used to store the shared character data once and reuse it across multiple text documents.
    */
   public static void main(String[] args) {
       // Reusing Flyweights
       Character charA = CharacterFactory.getCharacter('A');
       Character charB = CharacterFactory.getCharacter('B');
       Character charC = CharacterFactory.getCharacter('C');

       // Contextual state: applying styles
       TextStyle styleBold = new TextStyle(true, false);
       TextStyle styleItalic = new TextStyle(false, true);

       // Applying styles to the characters
       styleBold.applyStyle(charA);
       styleItalic.applyStyle(charB);
       styleBold.applyStyle(charC);

       // Reusing the same 'A' character object
       Character charA2 = CharacterFactory.getCharacter('A');
       styleItalic.applyStyle(charA2);

       // Checking if the reused object is the same as the original 'A'
       System.out.println("Are both 'A' characters the same instance? " + (charA == charA2));
   }
   /*

Flyweight Pattern: Use Case & Code Example
Use Case for Flyweight Pattern
The Flyweight Pattern is a structural design pattern that is used to minimize memory usage by sharing as much data as possible between objects. The Flyweight pattern is particularly useful when there are many objects that are similar in nature and require similar resources, but where maintaining a large number of individual objects would be inefficient.

The Flyweight Pattern is most useful when:

You have a large number of objects that share common properties or behaviors.
You want to reduce the memory consumption of an application by reusing shared objects instead of creating new ones each time.
You can break down the object’s state into two parts: intrinsic (shared) and extrinsic (contextual) states.
The Flyweight Pattern essentially allows you to share the intrinsic state (which is common across objects) and separate the extrinsic state (which is unique to each object) that can be passed into the Flyweight objects when needed.

Scenario: Text Formatting System
Imagine you are building a text formatting system that displays large amounts of text. Each character in the text could be an object, but each character only differs in terms of the character itself (e.g., 'a', 'b', 'c') and the formatting style (e.g., bold, italic). Instead of creating a new object for each character every time a new document is created, the Flyweight Pattern can be used to store the shared character data once and reuse it across multiple text documents.

Code Implementation
1. Flyweight Class (Character)
java
Copy code
// Flyweight class
public class Character {
   private char symbol;

   public Character(char symbol) {
       this.symbol = symbol;
   }

   public char getSymbol() {
       return symbol;
   }

   // The Flyweight class has the intrinsic state (symbol) that is shared.
}
2. Flyweight Factory Class (CharacterFactory)
java
Copy code
import java.util.HashMap;
import java.util.Map;

// Flyweight Factory class
public class CharacterFactory {
   private static final Map<Character, Character> characterPool = new HashMap<>();

   // Retrieves the Flyweight (reusing if already created)
   public static Character getCharacter(char symbol) {
       Character character = characterPool.get(symbol);
       if (character == null) {
           character = new Character(symbol);
           characterPool.put(symbol, character);
           System.out.println("Creating new character: " + symbol);
       }
       return character;
   }
}
3. Contextual State Class (TextStyle)
java
Copy code
// Contextual state class
public class TextStyle {
   private boolean bold;
   private boolean italic;

   public TextStyle(boolean bold, boolean italic) {
       this.bold = bold;
       this.italic = italic;
   }

   public void applyStyle(Character character) {
       System.out.print("Character " + character.getSymbol());
       if (bold) {
           System.out.print(" in Bold");
       }
       if (italic) {
           System.out.print(" in Italic");
       }
       System.out.println();
   }
}
4. Client Code (FlyweightPatternDemo)
java
Copy code
public class FlyweightPatternDemo {
   public static void main(String[] args) {
       // Reusing Flyweights
       Character charA = CharacterFactory.getCharacter('A');
       Character charB = CharacterFactory.getCharacter('B');
       Character charC = CharacterFactory.getCharacter('C');

       // Contextual state: applying styles
       TextStyle styleBold = new TextStyle(true, false);
       TextStyle styleItalic = new TextStyle(false, true);

       // Applying styles to the characters
       styleBold.applyStyle(charA);
       styleItalic.applyStyle(charB);
       styleBold.applyStyle(charC);

       // Reusing the same 'A' character object
       Character charA2 = CharacterFactory.getCharacter('A');
       styleItalic.applyStyle(charA2);

       // Checking if the reused object is the same as the original 'A'
       System.out.println("Are both 'A' characters the same instance? " + (charA == charA2));
   }
}
Explanation of Code
Flyweight Class (Character): The Character class represents the intrinsic state, which is shared across all instances. In this case, it only contains the character symbol (char). Each Character object is created once and can be reused across different contexts.

Flyweight Factory Class (CharacterFactory): This class is responsible for managing the creation and reuse of the Character objects. It keeps a cache (characterPool) of already created Character objects and returns the same object if one with the same symbol already exists. If not, it creates a new object, adds it to the cache, and returns it.

Contextual State Class (TextStyle): The TextStyle class represents the extrinsic state (context-specific data). This includes formatting details such as whether the text should be bold or italic. This information is passed at runtime and is not part of the Flyweight object.

Client Code (FlyweightPatternDemo): The client code demonstrates how Flyweights are shared. Characters A, B, and C are created and styled. The system reuses the A character object when needed. The client can apply different styles (extrinsic state) without creating new instances for each unique combination.
    */
}
//1. Flyweight Class (Character)

// Flyweight class
class Character {
   private java.lang.Character symbol;

   public Character(char symbol) {
       this.symbol = symbol;
   }

   public char getSymbol() {
       return symbol;
   }

   // The Flyweight class has the intrinsic state (symbol) that is shared.
}
//2. Flyweight Factory Class (CharacterFactory)


// Flyweight Factory class
class CharacterFactory {
   private static final Map<Character, Character> characterPool = new HashMap<>();

   // Retrieves the Flyweight (reusing if already created)
   public static Character getCharacter(char symbol) {
       Character character = characterPool.get(symbol);
       if (character == null) {
           character = new Character(symbol);
         //  characterPool.put(symbol, character);
           System.out.println("Creating new character: " + symbol);
       }
       return character;
   }
}

//3. Contextual State Class (TextStyle)

// Contextual state class
class TextStyle {
   private boolean bold;
   private boolean italic;

   public TextStyle(boolean bold, boolean italic) {
       this.bold = bold;
       this.italic = italic;
   }

   public void applyStyle(Character character) {
       System.out.print("Character " + character.getSymbol());
       if (bold) {
           System.out.print(" in Bold");
       }
       if (italic) {
           System.out.print(" in Italic");
       }
       System.out.println();
   }
}


/*
Use Case for Flyweight Pattern
The Flyweight Pattern is a structural design pattern that is used to minimize memory usage by sharing as much data as possible between objects. The Flyweight pattern is particularly useful when there are many objects that are similar in nature and require similar resources, but where maintaining a large number of individual objects would be inefficient.

The Flyweight Pattern is most useful when:

You have a large number of objects that share common properties or behaviors.
You want to reduce the memory consumption of an application by reusing shared objects instead of creating new ones each time.
You can break down the object’s state into two parts: intrinsic (shared) and extrinsic (contextual) states.
*/
                         BridgePattern
package com.niteshsynergy.designPattern.StructuralDesign;

 

public class Demo06BridgePattern {

   //Scenario: Shape and Color
   /*
   Consider a scenario where you are designing a drawing application that deals with different shapes (e.g., Circle, Rectangle) and colors (e.g., Red, Blue). Without the Bridge Pattern, you'd end up with a large number of shape-color combinations like RedCircle, BlueCircle, RedRectangle, BlueRectangle, and so on. The Bridge Pattern allows you to separate the shape and color functionalities into different classes, avoiding this explosion of classes.
    */
   public static void main(String[] args) {
       Shape redCircle = new Circle(new Red(), 10);
       Shape blueCircle = new Circle(new Blue(), 15);

       redCircle.draw();
       blueCircle.draw();
   }
}
//1. Abstraction Class (Shape)

// Abstraction class
abstract class Shape {
   protected Color color; // Reference to the Implementor class

   // Constructor to initialize the Implementor (Color)
   protected Shape(Color color) {
       this.color = color;
   }

   // Abstract method for drawing shapes, to be implemented by subclasses
   public abstract void draw();
}

//2. Refined Abstraction Class (Circle)
// Refined abstraction class
class Circle extends Shape {

   private int radius;

   public Circle(Color color, int radius) {
       super(color); // Passing the implementor (Color)
       this.radius = radius;
   }

   @Override
   public void draw() {
       System.out.print("Drawing Circle with radius " + radius + " in ");
       color.applyColor(); // Delegating the color functionality to the Implementor
   }
}

//3. Implementor Interface (Color)
// Implementor interface
interface Color {
   void applyColor();
}

//4. Concrete Implementor Classes (Red and Blue)
// Concrete Implementor classes
class Red implements Color {
   @Override
   public void applyColor() {
       System.out.println("Red color");
   }
}

class Blue implements Color {
   @Override
   public void applyColor() {
       System.out.println("Blue color");
   }
}


/*
Use Case for Bridge Pattern
The Bridge Pattern is a structural design pattern that is used to separate an abstraction from its implementation so that both can be varied independently. The main goal of the Bridge Pattern is to decouple abstraction and implementation, allowing both to evolve without affecting each other.

The Bridge Pattern is useful when:

You want to avoid creating a large number of classes due to combinations of different abstraction and implementation classes.
You need to modify an abstraction and its implementation independently.
You have several variants of a class, and the differences lie mainly in the details of the implementation, not in the abstraction itself.
*/

/*
Explanation of Code
Abstraction (Shape): This class contains a reference to the Color interface (implementor). It defines the core behavior (draw) that can be refined by subclasses. It allows the Shape class to remain independent of the color implementation.

Refined Abstraction (Circle): The Circle class extends Shape and implements the draw() method. It delegates the color-specific logic to the Color object, which implements the actual color functionality.

Implementor Interface (Color): The Color interface defines the applyColor() method, which is implemented by concrete color classes like Red and Blue.

Concrete Implementors (Red and Blue): The concrete Red and Blue classes implement the applyColor() method, providing the color-specific functionality.

Client Code (BridgePatternDemo): The client code creates instances of Shape (e.g., Circle) and assigns different Color implementations (e.g., Red, Blue) to them. The draw() method is called, which delegates the color application to the respective color object.
*/
                        DecoratorPattern
package com.niteshsynergy.designPattern.StructuralDesign;

public class Demo07DecoratorPattern {

   //Scenario: Coffee Shop
   /*
   Consider a coffee shop where customers can order a basic coffee (Coffee), and depending on their choice, additional toppings (like milk, sugar, chocolate) can be added. Instead of creating a new subclass for every possible combination of coffee and toppings, the Decorator Pattern allows you to decorate a basic Coffee object with various toppings.
    */
   public static void main(String[] args) {
       Coffee coffee = new BasicCoffee();
       System.out.println("Cost of Basic Coffee: " + coffee.cost());

       Coffee milkCoffee = new MilkDecorator(coffee);
       System.out.println("Cost of Coffee with Milk: " + milkCoffee.cost());

       Coffee milkSugarCoffee = new SugarDecorator(milkCoffee);
       System.out.println("Cost of Coffee with Milk and Sugar: " + milkSugarCoffee.cost());
   }
}
//1. Component Interface (Coffee)

// Component interface
interface Coffee {
   double cost();
}
//2. Concrete Component Class (BasicCoffee)
// Concrete Component class
class BasicCoffee implements Coffee {
   @Override
   public double cost() {
       return 5.0; // Basic coffee price
   }
}

//3. Decorator Class (CoffeeDecorator)
// Decorator class
abstract class CoffeeDecorator implements Coffee {
   protected Coffee decoratedCoffee; // The coffee object to be decorated

   public CoffeeDecorator(Coffee coffee) {
       this.decoratedCoffee = coffee;
   }

   public double cost() {
       return decoratedCoffee.cost();
   }
}
//4. Concrete Decorators (MilkDecorator, SugarDecorator)
// Concrete Decorator for Milk
class MilkDecorator extends CoffeeDecorator {
   public MilkDecorator(Coffee coffee) {
       super(coffee);
   }

   @Override
   public double cost() {
       return decoratedCoffee.cost() + 1.5; // Adding milk cost
   }
}

// Concrete Decorator for Sugar
class SugarDecorator extends CoffeeDecorator {
   public SugarDecorator(Coffee coffee) {
       super(coffee);
   }

   @Override
   public double cost() {
       return decoratedCoffee.cost() + 0.5; // Adding sugar cost
   }
}


/*
Use Case for Decorator Pattern
The Decorator Pattern is a structural design pattern that allows you to add new functionality to an object dynamically without altering its structure. It provides a flexible alternative to subclassing for extending functionality.

The Decorator Pattern is useful when:

You want to add responsibilities to objects without affecting other objects of the same class.
You need to extend an object's behavior in a flexible and reusable way, rather than using inheritance.
You want to allow behaviors to be added at runtime (as opposed to at compile-time).
*/

/*
Explanation of Code
Component Interface (Coffee): This is the interface that defines the behavior (cost()) that all concrete and decorator classes should implement.

Concrete Component (BasicCoffee): This is the basic coffee object that implements the Coffee interface and defines the base cost.

Decorator Class (CoffeeDecorator): This is an abstract decorator class that implements the Coffee interface and holds a reference to a Coffee object. It delegates the cost() method to the wrapped Coffee object, allowing additional behavior to be added by the concrete decorators.

Concrete Decorators (MilkDecorator, SugarDecorator): These concrete decorators add additional functionality (costs) to the basic coffee object. They override the cost() method to include their specific cost.

Client Code (DecoratorPatternDemo): The client code demonstrates how the decorator pattern can be used to dynamically add functionality to a Coffee object, such as adding milk and sugar. The base BasicCoffee object is decorated with various decorators at runtime.


*/
                             Behavioral Design Pattern
                          TemplateMethodPattern
package com.niteshsynergy.designPattern.BehavioralDesign;

public class Demo01TemplateMethodPattern {
   //Scenario: Preparing a Beverage
   /*
   Consider a scenario where we are designing a system for preparing beverages like tea and coffee. Both tea and coffee require some common steps, such as boiling water and pouring it into a cup, but they also require different steps (e.g., tea requires steeping, while coffee requires brewing). Using the Template Method Pattern, we can define a general template for preparing a beverage, while allowing each subclass to implement the specific steps for tea or coffee.
    */
   public static void main(String[] args) {
       Beverage tea = new Tea();
       Beverage coffee = new Coffee();

       System.out.println("Preparing tea...");
       tea.prepareRecipe();

       System.out.println("\nPreparing coffee...");
       coffee.prepareRecipe();
   }
   /*
   Explanation of Code
Abstract Class (Beverage): This is the class that defines the template method (prepareRecipe()). The template method contains the steps of the algorithm. Some of these steps are common to all beverages, such as boiling water and pouring into a cup, while others (e.g., brewing and adding condiments) are abstract and are meant to be implemented by subclasses.

Concrete Class for Tea (Tea): This class provides the specific implementation of the abstract methods for making tea, such as steeping the tea and adding lemon.

Concrete Class for Coffee (Coffee): This class provides the specific implementation of the abstract methods for making coffee, such as dripping coffee through a filter and adding sugar and milk.

Client Code (TemplateMethodPatternDemo): The client code creates instances of Tea and Coffee, and calls the prepareRecipe() method on both objects. Despite the different beverage types, the overall algorithm remains the same, while the specific steps (e.g., brewing and adding condiments) differ.
    */
}
//1. Abstract Class (Beverage)
// Abstract class defining the template method and common behavior
abstract class Beverage {

   // Template Method
   public final void prepareRecipe() {
       boilWater();
       brew();
       pourInCup();
       addCondiments();
   }

   // Common step to boil water
   private void boilWater() {
       System.out.println("Boiling water...");
   }

   // Abstract method for brewing the beverage, to be implemented by subclasses
   protected abstract void brew();

   // Common step to pour beverage into the cup
   private void pourInCup() {
       System.out.println("Pouring into cup...");
   }

   // Abstract method for adding condiments, to be implemented by subclasses
   protected abstract void addCondiments();
}

//2. Concrete Class for Tea (Tea)
// Concrete class for tea
class Tea extends Beverage {

   @Override
   protected void brew() {
       System.out.println("Steeping the tea...");
   }

   @Override
   protected void addCondiments() {
       System.out.println("Adding lemon...");
   }
}
//3. Concrete Class for Coffee (Coffee)
// Concrete class for coffee
class Coffee extends Beverage {

   @Override
   protected void brew() {
       System.out.println("Dripping coffee through filter...");
   }

   @Override
   protected void addCondiments() {
       System.out.println("Adding sugar and milk...");
   }
}

/*
The Template Method Pattern is useful when:

You have an algorithm that must be implemented in multiple steps.
Some steps of the algorithm may vary across subclasses, but the overall algorithm should remain the same.
You want to allow subclasses to redefine certain parts of the algorithm while keeping the overall process intact.
*/

                         MediatorPattern
package com.niteshsynergy.designPattern.BehavioralDesign;

import java.util.ArrayList;
import java.util.List;

public class Demo02MediatorPattern {
   public static void main(String[] args) {
       ChatMediator mediator = new ChatRoom();

       User user1 = new User("Alice", mediator);
       User user2 = new User("Bob", mediator);

       mediator.addUser(user1);
       mediator.addUser(user2);

       user1.sendMessage("Hello, Bob!");
   }
}
// Mediator interface
interface ChatMediator {
   void sendMessage(String message, User user);
   void addUser(User user);
}

// Concrete Mediator
class ChatRoom implements ChatMediator {
   private List<User> users = new ArrayList<>();

   @Override
   public void sendMessage(String message, User user) {
       for (User u : users) {
           if (u != user) {
               u.receiveMessage(message);
           }
       }
   }

   @Override
   public void addUser(User user) {
       users.add(user);
   }
}

// Colleague class
class User {
   private String name;
   private ChatMediator mediator;

   public User(String name, ChatMediator mediator) {
       this.name = name;
       this.mediator = mediator;
   }

   public void sendMessage(String message) {
       mediator.sendMessage(message, this);
   }

   public void receiveMessage(String message) {
       System.out.println(name + " received: " + message);
   }
}


/*
Example Use Case: In a chat application, instead of users directly communicating with each other, a Mediator (like a ChatRoom) handles all interactions, ensuring proper message routing.
*/

                     ObserverPattern
package com.niteshsynergy.designPattern.BehavioralDesign;

import java.util.ArrayList;
import java.util.List;

public class Demo04ObserverPattern {
   public static void main(String[] args) {
       WeatherStation station = new WeatherStation();
       TemperatureDisplay display = new TemperatureDisplay();

       station.addObserver(display);
       station.setTemperature(25);
   }
}
// Subject
class WeatherStation {
   private List<Observer> observers = new ArrayList<>();
   private int temperature;

   public void addObserver(Observer observer) {
       observers.add(observer);
   }

   public void removeObserver(Observer observer) {
       observers.remove(observer);
   }

   public void setTemperature(int temperature) {
       this.temperature = temperature;
       notifyObservers();
   }

   private void notifyObservers() {
       for (Observer observer : observers) {
           observer.update(temperature);
       }
   }
}

// Observer
interface Observer {
   void update(int temperature);
}

// Concrete Observer
class TemperatureDisplay implements Observer {
   private int temperature;

   @Override
   public void update(int temperature) {
       this.temperature = temperature;
       System.out.println("Temperature updated to: " + temperature);
   }
}


/*
Example Use Case: A weather monitoring system where multiple devices (observers) need to be updated whenever the weather data (subject) changes.

*/

                    StrategyPattern
package com.niteshsynergy.designPattern.BehavioralDesign;

public class Demo05StrategyPattern {
   public static void main(String[] args) {
       PaymentContext context = new PaymentContext(new CreditCardPayment());
       context.executePayment(100);

       context = new PaymentContext(new PayPalPayment());
       context.executePayment(200);
   }
}

// Strategy interface
interface PaymentStrategy {
   void pay(int amount);
}

// Concrete Strategies
class CreditCardPayment implements PaymentStrategy {
   @Override
   public void pay(int amount) {
       System.out.println("Paid " + amount + " using Credit Card");
   }
}

class PayPalPayment implements PaymentStrategy {
   @Override
   public void pay(int amount) {
       System.out.println("Paid " + amount + " using PayPal");
   }
}

// Context
class PaymentContext {
   private PaymentStrategy paymentStrategy;

   public PaymentContext(PaymentStrategy paymentStrategy) {
       this.paymentStrategy = paymentStrategy;
   }

   public void executePayment(int amount) {
       paymentStrategy.pay(amount);
   }
}


/*
Example Use Case: In a payment system where customers can pay via different methods (credit card, PayPal, etc.), and the client selects the appropriate payment method at runtime.
*/

                                  CommandPattern
package com.niteshsynergy.designPattern.BehavioralDesign;

public class Demo06CommandPattern {
   public static void main(String[] args) {
       Light light = new Light();
       Command lightOn = new LightOnCommand(light);

       RemoteControl remote = new RemoteControl();
       remote.setCommand(lightOn);
       remote.pressButton();
   }
}
// Command interface
interface Command {
   void execute();
}

// Concrete Command
class LightOnCommand implements Command {
   private Light light;

   public LightOnCommand(Light light) {
       this.light = light;
   }

   @Override
   public void execute() {
       light.turnOn();
   }
}

// Receiver class
class Light {
   public void turnOn() {
       System.out.println("Light is ON");
   }

   public void turnOff() {
       System.out.println("Light is OFF");
   }
}

// Invoker class
class RemoteControl {
   private Command command;

   public void setCommand(Command command) {
       this.command = command;
   }

   public void pressButton() {
       command.execute();
   }
}


/*
Example Use Case: In a remote control system where commands for turning devices on or off are encapsulated and executed based on user input.
*/

                     StatePattern
package com.niteshsynergy.designPattern.BehavioralDesign;

public class Demo07StatePattern {
   public static void main(String[] args) {
       VendingMachine vendingMachine = new VendingMachine();

       vendingMachine.insertCoin();
       vendingMachine.dispenseItem();
   }
}
// State interface
interface VendingMachineState {
   void insertCoin();
   void ejectCoin();
   void dispenseItem();
}

// Concrete States
class NoCoinState implements VendingMachineState {
   private VendingMachine vendingMachine;

   public NoCoinState(VendingMachine vendingMachine) {
       this.vendingMachine = vendingMachine;
   }

   @Override
   public void insertCoin() {
       System.out.println("Coin inserted.");
       vendingMachine.setState(vendingMachine.getHasCoinState());
   }

   @Override
   public void ejectCoin() {
       System.out.println("No coin to eject.");
   }

   @Override
   public void dispenseItem() {
       System.out.println("Insert coin first.");
   }
}

class HasCoinState implements VendingMachineState {
   private VendingMachine vendingMachine;

   public HasCoinState(VendingMachine vendingMachine) {
       this.vendingMachine = vendingMachine;
   }

   @Override
   public void insertCoin() {
       System.out.println("Coin already inserted.");
   }

   @Override
   public void ejectCoin() {
       System.out.println("Coin ejected.");
       vendingMachine.setState(vendingMachine.getNoCoinState());
   }

   @Override
   public void dispenseItem() {
       System.out.println("Dispensing item.");
       vendingMachine.setState(vendingMachine.getNoCoinState());
   }
}

// Context (VendingMachine)
class VendingMachine {
   private VendingMachineState noCoinState;
   private VendingMachineState hasCoinState;
   private VendingMachineState currentState;

   public VendingMachine() {
       noCoinState = new NoCoinState(this);
       hasCoinState = new HasCoinState(this);
       currentState = noCoinState;
   }

   public void setState(VendingMachineState state) {
       currentState = state;
   }

   public void insertCoin() {
       currentState.insertCoin();
   }

   public void ejectCoin() {
       currentState.ejectCoin();
   }

   public void dispenseItem() {
       currentState.dispenseItem();
   }

   public VendingMachineState getNoCoinState() {
       return noCoinState;
   }

   public VendingMachineState getHasCoinState() {
       return hasCoinState;
   }
}

 

/*
Example Use Case: In a vending machine, the behavior (e.g., accepting money, dispensing item) changes based on the current state (e.g., idle, money inserted, item dispensed).
*/
VisitorPattern
package com.niteshsynergy.designPattern.BehavioralDesign;

public class Demo08VisitorPattern {
}
/*
Example Use Case: In a shopping cart system where various types of items (e.g., books, electronics) need to be processed differently, but the processing behavior can change dynamically.
*/
InterpreterPattern
package com.niteshsynergy.designPattern.BehavioralDesign;

public class Demo09InterpreterPattern {
}

/*
Use Case:
The Interpreter Pattern is used when you need to evaluate sentences or expressions in a language, especially useful for designing interpreters or compilers.


*/

IteratorPattern
package com.niteshsynergy.designPattern.BehavioralDesign;

public class Demo10IteratorPattern {
}

/*
Use Case:
The Iterator Pattern is used to provide a way to access elements of a collection sequentially without exposing its underlying representation.
*/
ChainofResponsibilityPattern
package com.niteshsynergy.designPattern.BehavioralDesign;

public class Demp03ChainofResponsibilityPattern {
   public static void main(String[] args) {
       SupportHandler handler1 = new LevelOneSupport();
       SupportHandler handler2 = new LevelTwoSupport();

       handler1.setNextHandler(handler2);

       handler1.handleRequest("simple issue");
       handler1.handleRequest("complex issue");
   }
}

// Abstract Handler
abstract class SupportHandler {
   protected SupportHandler nextHandler;

   public void setNextHandler(SupportHandler nextHandler) {
       this.nextHandler = nextHandler;
   }

   public abstract void handleRequest(String request);
}

// Concrete Handlers
class LevelOneSupport extends SupportHandler {
   @Override
   public void handleRequest(String request) {
       if (request.equals("simple issue")) {
           System.out.println("Level One Support handling the request.");
       } else {
           System.out.println("Passing to Level Two Support.");
           nextHandler.handleRequest(request);
       }
   }
}

class LevelTwoSupport extends SupportHandler {
   @Override
   public void handleRequest(String request) {
       if (request.equals("complex issue")) {
           System.out.println("Level Two Support handling the request.");
       } else {
           System.out.println("No one can handle this request.");
       }
   }
}

 

/*
Example Use Case: In a support system where a request is passed through multiple levels (e.g., Level 1 support, Level 2 support) until it is resolved.
*/
 


Thanks for reading complete design pattern training from Nitesh Synergy….
FOr more details reach out say@niteshsynergy.com to get live session on ddesign pattern & get high paying job. We also offer HLD & LLD which is paid.
 

68 min read
नव. 19, 2024
By Nitesh Synergy
Share