0% found this document useful (0 votes)
34 views24 pages

Adapter Design Pattern Explained

The document outlines several design patterns including Adapter, Bridge, Composite, Decorator, Façade, and Flyweight. Each pattern is described with its intent, applicability, structure, participants, collaborations, and implementation examples. The patterns aim to solve common software design problems by promoting code reusability, flexibility, and simplification of complex systems.

Uploaded by

Rahul gupta
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
34 views24 pages

Adapter Design Pattern Explained

The document outlines several design patterns including Adapter, Bridge, Composite, Decorator, Façade, and Flyweight. Each pattern is described with its intent, applicability, structure, participants, collaborations, and implementation examples. The patterns aim to solve common software design problems by promoting code reusability, flexibility, and simplification of complex systems.

Uploaded by

Rahul gupta
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Adapter Design Pattern

Intent
- Converts the interface of a class into another interface clients expect.
- Adapter lets classes work together that could not otherwise because of
incompatible interfaces.
Applicability
Use the adapter pattern when
- You want to use an existing class, and its interface does not match the one
you need.

- When you need to integrate a third-party library or legacy code with your
application.
- When you need to integrate a third-party library or legacy code with your
application.
Structure

Participants
1. Target (e.g., PaymentProcessor)
• Defines the domain-specific interface that the client uses.
2. Client
• The class that interacts with objects that implement the target interface.
3. Adaptee (e.g., OldPaymentSystem)
• The existing class with an incompatible interface that needs to be
adapted.
4. Adapter (e.g., PaymentAdapter)
• A class that implements the target interface and translates the calls to
the adaptee.
Collaborations
- Client calls operations on an Adapter instance. In turn, the adapter calls
Adaptee operations that carry out the request.
Implementation
Example
OldPaymentSystem (Adaptee)
This is the existing class with an incompatible interface.
public class OldPaymentSystem {
public void processOldPayment(double amount) {
[Link]("Processing payment of $" + amount + " using Old Payment
System.");
}
}

PaymentProcessor (Target Interface)


This is the interface that your new application expects.
public interface PaymentProcessor {
void processPayment(double amount);
}

PaymentAdapter (Adapter)
The adapter class implements the PaymentProcessor interface and translates calls to the
OldPaymentSystem.
public class PaymentAdapter implements PaymentProcessor {
private OldPaymentSystem oldPaymentSystem;

public PaymentAdapter(OldPaymentSystem oldPaymentSystem) {


[Link] = oldPaymentSystem;
}

@Override
public void processPayment(double amount) {
// Translate the call to the old system's method
[Link](amount);
}
}
Client
public class Main {
public static void main(String[] args) {
// Existing system
OldPaymentSystem oldPaymentSystem = new OldPaymentSystem();

// Adapter
PaymentProcessor paymentProcessor = new PaymentAdapter(oldPaymentSystem);

// Client uses the adapter as if it's using a new system


[Link](100.0); // Output: Processing payment of $100.0
using Old Payment System.
}
}
Bridge Design Pattern
Intent
- Decouples and abstraction from its implementation so that the two can vary
independently.

- particularly useful when you want to avoid a permanent binding between an


abstraction and its implementation.
Applicability
Use the Bridge pattern when
- you want to avoid a permanent binding between an abstraction and its
implementation. This might be the case, for example, when implementation
must be selected or switched at run-time.
- Changes in the implementations of an abstraction should have no impact on
clients; that is, their code should not have to be recompiled.

- When both the abstraction and the implementation might evolve


independently.

Structure

Participants
1. Abstraction (e.g., Shape)
• Defines abstraction’s interface
• Maintains a reference to an object of type Implementor
2. RefinedAbstraction (e.g., Circle or Square)
• Extends the interface defined by abstraction to provide additional or
specialized behaviour.
3. Implementor (e.g., Color)
• Defines the interface for implementation classes. This doesn’t need to
correspond directly to the abstraction's interface, as the abstraction can
define higher-level operations (e.g., draw()) that are based on primitives
(e.g., applyColor()) provided by the implementer.
4. ConcreteImplementor (e.g., RedColor or BlueColor)
• Implements the Implementer interface and defines the concrete
implementation of the abstraction.
Collaborations
- Abstraction forwards client requests to its Implementor object.
Implementation
Example
Implementor
public interface Color {
void applyColor();
}

ConcreteImplementors
public class RedColor implements Color {
@Override
public void applyColor() {
[Link]("Applying red color");
}
}

public class BlueColor implements Color {


@Override
public void applyColor() {
[Link]("Applying blue color");
}
}

Abstraction
public abstract class Shape {
// This defines the abstract interface for shapes and maintains a reference to a Color
implementer.
protected Color color;

public Shape(Color color) {


[Link] = color;
}

public abstract void draw();


}

RefinedAbstraction
public class Circle extends Shape {
public Circle(Color color) {
super(color);
}

@Override
public void draw() {
[Link]("Drawing Circle with ");
[Link]();
}
}

public class Square extends Shape {


public Square(Color color) {
super(color);
}

@Override
public void draw() {
[Link]("Drawing Square with ");
[Link]();
}
}

Client
public class Main {
public static void main(String[] args) {
Shape redCircle = new Circle(new RedColor());
Shape blueSquare = new Square(new BlueColor());

[Link](); // Output: Drawing Circle with Applying red color


[Link](); // Output: Drawing Square with Applying blue color
}
}
Composite Design Pattern
Intent
- Composes objects into tree structure to represent part-whole hierarchies.
- Composite lets clients treat individual objects and compositions of objects
uniformly.
Applicability
Use the Composite pattern when
- You want to represent part-whole hierarchies of objects.
- You want clients to be able to ignore the difference between composition of
objects and individual objects. Clients will treat all objects in the composite
structure uniformly.

- you want to represent hierarchies of objects, like files and directories in a file
system.
- you want to treat a group of objects as a single object.
Structure

Participants
1. Component (e.g., Employee)
• Declares the interface for objects in the composition
• Implements default behaviour for the interface common to all classes, as
appropriate.
• Declares an interface for accessing and managing its child components.
2. Leaf (e.g., Developer and Designer)
• Represents leaf objects in the composition. A leaf has no children.
3. Composite (e.g., Manager)
• Defines behaviour for components having children.
• Stores child components.
• Implements child-related operations in the Component interface.
4. Client
• Manipulates objects in the composition through the component interface.
Collaboration
- Clients use the Component class interface to interact with objects in the
composite structure.
- If the recipient is a Leaf, then the request is handled directly. If the recipient is
Composite, then it usually forwards requests to its child components, possibly
performing additional operations before and/or after forwarding. (e.g.,
showEmployeeDetails() method)
Implementation
Example
Component Interface
public interface Employee {
void showEmployeeDetails();
}

Leaf
public class Developer implements Employee {
private String name;
private long empId;
private String position;

public Developer(long empId, String name, String position) {


[Link] = empId;
[Link] = name;
[Link] = position;
}

@Override
public void showEmployeeDetails() {
[Link](empId + " " + name + " " + position);
}
}

public class Designer implements Employee {


private String name;
private long empId;
private String position;

public Designer(long empId, String name, String position) {


[Link] = empId;
[Link] = name;
[Link] = position;
}

@Override
public void showEmployeeDetails() {
[Link](empId + " " + name + " " + position);
}
}

Composite
import [Link];
import [Link];

public class Manager implements Employee {


private List<Employee> employees = new ArrayList<>();

private String name;


private long empId;
private String position;

public Manager(long empId, String name, String position) {


[Link] = empId;
[Link] = name;
[Link] = position;
}

public void addEmployee(Employee employee) {


[Link](employee);
}

public void removeEmployee(Employee employee) {


[Link](employee);
}

@Override
public void showEmployeeDetails() {
[Link](empId + " " + name + " " + position);
for (Employee employee : employees) {
[Link]();
}
}
}
Client
public class Main {
public static void main(String[] args) {
Employee dev1 = new Developer(101, "John Doe", "Senior Developer");
Employee dev2 = new Developer(102, "Jane Smith", "Junior Developer");
Employee des1 = new Designer(103, "Emily Johnson", "UI/UX Designer");

Manager manager1 = new Manager(201, "Michael Scott", "Team Manager");


[Link](dev1);
[Link](dev2);

Manager generalManager = new Manager(301, "Pam Beesly", "General


Manager");
[Link](manager1);
[Link](des1);

[Link]();
}
}
Decorator Design Pattern
Intent
- Attaches additional responsibilities to an object dynamically.
- Decorators provide a flexible alternative to subclassing for extending
functionality.

- allows you to dynamically add behavior or responsibilities to individual objects


without affecting the behavior of other objects from the same class.
- provides a flexible alternative to subclassing for extending [Link].
Applicability
Use Decorator
- to add responsibilities to individual objects dynamically and transparently, that
is, without affecting other objects.
- For responsibilities that can be withdrawn.
- When extension by subclassing is impractical. Sometimes many independent
extensions are possible and would produce an explosion of subclasses to
support every combination. Or a class definition may be hidden or otherwise
unavailable for subclassing.
Structure

Participants
1. Component (e.g., Coffee)
• Defines the interface for objects that can have responsibilities added to
them dynamically.
2. ConcreteComponent (e.g., SimpleCoffee)
• Defines an object to which additional responsibilities can be attached.

• class that implements the Component interface. It represents the core


object that can be dynamically wrapped by decorators.
3. Decorator (e.g., CoffeeDecorator)
• Maintains a reference to a components object and defines an interface
that conforms (implements) to Component’s interface.
• It delegates the operations to the Component object and can add
additional behavior.
4. ConcreteDecorator (e.g., MilkDecorator and SugarDecorator)
• Adds responsibilities to the component.

• They override the operations to add behavior before or after delegating


the call to the wrapped Component object.

Collaborations
- Decorator forwards requests to its Component Object. It may perform
additional operations before and after forwarding the request.
Implementation
Example
Component
public interface Coffee {
String getDescription();
double getCost();
}

Concrete Component
public class SimpleCoffee implements Coffee {
@Override
public String getDescription() {
return "Simple Coffee";
}

@Override
public double getCost() {
return 5.0;
}
}
Decorator
public abstract class CoffeeDecorator implements Coffee {
protected Coffee decoratedCoffee;

public CoffeeDecorator(Coffee coffee) {


[Link] = coffee;
}

@Override
public String getDescription() {
return [Link]();
}

@Override
public double getCost() {
return [Link]();
}
}

ConcreteDecorator
public class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}

@Override
public String getDescription() {
return [Link]() + ", Milk";
}

@Override
public double getCost() {
return [Link]() + 1.5;
}
}

public class SugarDecorator extends CoffeeDecorator {


public SugarDecorator(Coffee coffee) {
super(coffee);
}

@Override
public String getDescription() {
return [Link]() + ", Sugar";
}

@Override
public double getCost() {
return [Link]() + 0.5;
}
}

Client
public class Main {
public static void main(String[] args) {
Coffee coffee = new SimpleCoffee();
[Link]([Link]() + " $" +
[Link]());

coffee = new MilkDecorator(coffee);


[Link]([Link]() + " $" +
[Link]());

coffee = new SugarDecorator(coffee);


[Link]([Link]() + " $" +
[Link]());
}
}
Façade Design Pattern
Intent
- Provides a unified interface to a set of interfaces in a subsystem.
- Defines a higher-level interface that makes the subsystem easier to use.

- aims to simplify the interaction with a complex system by providing a unified


interface.
- It helps clients use the system without understanding its complexity.
Applicability
Use Façade
- When you want to provide a simple interface to complex subsystem.
A façade can provide a simple default view of the subsystem that is good
enough for most clients. Only clients needing more customizability will need to
look beyond the façade.
- To decouple the subsystem from clients and other subsystems, thereby
promoting subsystem independence and portability.
- When you want to layer your subsystems.
Use a façade to define an entry point to each subsystem level. If subsystems
are dependent, you can make them communicate with each other solely
through their facades.
Structures

Participants
1. Façade (e.g., HomeTheatreFacade)
• Knows which subsystem classes are responsible for a request.
• Delegates client requests to appropriate subsystem objects.
• Provides a simple interface to interact with subsystems
2. Subsystem Classes (e.g., TV, SoundSystem, Lights)
• Implement subsystem functionality.
• Handle work assigned by the Façade object
• Have no knowledge of the façade; that is, they keep no reference to it.
3. Client
• Uses the Facade to interact with the system. It is unaware of the complexity
of the subsystems.
Collaborations
- Clients communicate with the subsystem by sending requests to façade, which
forwards them to the appropriate subsystem objects. Although the subsystem
objects perform the actual work, the façade may have to do work of its own to
translate its interface to subsystems interface.
- Clients that use the façade don’t have to access its subsystem objects directly.
Implementation
Example:
Subsystem Classes
// Subsystem 1: TV
class TV {
public void turnOn() {
[Link]("Turning on the TV...");
}

public void turnOff() {


[Link]("Turning off the TV...");
}
}
// Subsystem 2: Sound System
class SoundSystem {
public void turnOn() {
[Link]("Turning on the Sound System...");
}

public void turnOff() {


[Link]("Turning off the Sound System...");
}
}
// Subsystem 3: Lights
class Lights {
public void dim() {
[Link]("Dimming the lights...");
}

public void brighten() {


[Link]("Brightening the lights...");
}
}

Façade Class
// Facade: HomeTheaterFacade
class HomeTheaterFacade {
private TV tv;
private SoundSystem soundSystem;
private Lights lights;

public HomeTheaterFacade(TV tv, SoundSystem soundSystem, Lights lights) {


[Link] = tv;
[Link] = soundSystem;
[Link] = lights;
}

public void startMovie() {


[Link]("Getting ready to watch a movie...");
[Link]();
[Link]();
[Link]();
}

public void endMovie() {


[Link]("Shutting down the movie theater...");
[Link]();
[Link]();
[Link]();
}
}

Client
// Client code
public class FacadeExample {
public static void main(String[] args) {
// Create instances of the subsystem classes
TV tv = new TV();
SoundSystem soundSystem = new SoundSystem();
Lights lights = new Lights();

// Create the facade


HomeTheaterFacade homeTheater = new HomeTheaterFacade(tv, soundSystem, lights);

// Using the facade to start/end a movie


[Link](); // Simplified interaction
[Link](); // Simplified interaction
}
}
Flyweight Design Pattern
Intent
- to reuse common parts of an object, rather than creating new instances of
objects with the same data.
- reduces memory consumption when dealing with a large number of objects.
Applicability
Use Flyweight Pattern when
- You need to reduce memory usage by sharing common data across multiple
objects.
- system has a large number of similar objects, and their creation costs are
high.
- objects can be broken down into intrinsic (shared) and extrinsic (unique)
states, where intrinsic states can be shared across many objects.
Structure

Participants
1. Flyweight (e.g., BookFlyweight)
• interface for flyweight objects, defining a method for receiving the extrinsic
state.
2. ConcreteFlyweight (e.g., Book)
• class that implements the Flyweight interface and stores intrinsic state (data
that can be shared)
3. UnsharedConcreteFlyweight (e.g., SpecialEditionBook)
• Represents objects that cannot be shared.
4. FlyweightFactory (e.g., BookFactory)
• Manages and provides shared flyweight objects.
Collaboration
- Client uses the FlyweightFactory to obtain flyweight instances.
- FlyweightFactory ensures that shared objects are reused, avoiding the
creation of duplicate objects.
- The Flyweight instances use intrinsic states (Shared Pre-existing Data)
stored in themselves and extrinsic states (Unique new Data) passed by the
client.
Implementation
Example:
Flyweight
interface BookFlyweight {
void display(int shelfNumber, String bookID); // extrinsic state: shelf number and
book ID
}

ConcreteFlyweight
class Book implements BookFlyweight {
private String title; // intrinsic state
private String author;
private String genre;

public Book(String title, String author, String genre) {


[Link] = title;
[Link] = author;
[Link] = genre;
}

@Override
public void display(int shelfNumber, String bookID) {
[Link]("Displaying book: " + title + " by " + author + " (Genre: " +
genre + ") on Shelf: " + shelfNumber + ", Book ID: " + bookID);
}
}

UnsharedConcreteFlyweight
class SpecialEditionBook implements BookFlyweight {
private String title;
private String author;
private String editionDetails; // Unique to this specific copy

public SpecialEditionBook(String title, String author, String editionDetails) {


[Link] = title;
[Link] = author;
[Link] = editionDetails;
}

@Override
public void display(int shelfNumber, String bookID) {
[Link]("Displaying Special Edition of " + title + " by " + author + "
(" + editionDetails + ") on Shelf: " + shelfNumber + ", Book ID: " + bookID);
}
}

Flyweight Factory
import [Link];
import [Link];

class BookFactory {
private static Map<String, Book> bookMap = new HashMap<>();

public static Book getBook(String title, String author, String genre) {


String key = title + author + genre;
Book book = [Link](key);

// Create a new shared book object if not already present


if (book == null) {
book = new Book(title, author, genre);
[Link](key, book);
}
return book;
}
}

Client
public class Client {
public static void main(String[] args) {
BookFactory bookFactory = new BookFactory();

// Shared books (multiple copies of the same book with different locations/IDs)
BookFlyweight book1 = [Link]("1984", "George Orwell", "Dystopian");
BookFlyweight book2 = [Link]("The Hobbit", "J.R.R. Tolkien",
"Fantasy");
BookFlyweight book3 = [Link]("1984", "George Orwell",
"Dystopian"); // Reusing the same "1984" book

// Unshared book (Special Edition, cannot be shared)


BookFlyweight specialEditionBook = new SpecialEditionBook("The Hobbit", "J.R.R.
Tolkien", "Illustrated by Alan Lee");

// Display books with extrinsic state (shelf location and book ID)
[Link](1, "ID001");
[Link](2, "ID002");
[Link](3, "ID003"); // Reuses the shared "1984" book
[Link](4, "ID004"); // Special edition book, not shared
}
}
Proxy Design Pattern
Intent
- Provide a surrogate or placeholder for another object to control access to it.

- aims to provide a substitute or placeholder for an object, giving control over its
operations.
- controls access to the original object, allowing for additional functionalities like
lazy initialization, logging, access control, or remote object interaction.
Applicability
Proxy pattern is applicable when
- you want to control access to a resource.
- You need defer (delay) expensive object creation until it is needed (lazy
loading).
- You want to add functionality, like caching or logging, to an object without
changing its interface.
- You need to use remote objects (remote proxy), security checks (protection
proxy), or to cache results (caching proxy).
Structure

Participants
1. Proxy (e.g., ProxyInternet)
• Maintains a reference that lets proxy access the real subject.
• Controls access to the real subject and may be responsible for creating
and deleting it.
2. Subject (e.g., Internet)
• Defines the common interface for RealSubject and Proxy so that a Proxy
can be used anywhere a RealSubject is expected.
3. RealSubject (e.g., RealInternet)
• Defines the real object that the proxy represents.
Collaboration
- Proxy forwards requests to RealSubject when appropriate, depending on the
kind of proxy.
Implementation
Example
Subject
interface Internet {
void connectTo(String serverHost) throws Exception;
}

RealSubject
class RealInternet implements Internet {
@Override
public void connectTo(String serverHost) {
[Link]("Connecting to " + serverHost);
}
}

ProxyInternet
class ProxyInternet implements Internet {
private Internet realInternet = new RealInternet();
private static List<String> blockedSites;

// Static block to initialize the blocked sites


static {
blockedSites = new ArrayList<>();
[Link]("[Link]");
[Link]("[Link]");
[Link]("[Link]");
}

@Override
public void connectTo(String serverHost) throws Exception {
if ([Link]([Link]())) {
throw new Exception("Access Denied to " + serverHost);
}
[Link](serverHost);
}
}

Client
public class ProxyPatternExample {
public static void main(String[] args) {
Internet internet = new ProxyInternet();

try {
[Link]("[Link]"); // Allowed
[Link]("[Link]"); // Blocked
} catch (Exception e) {
[Link]([Link]());
}
}
}

You might also like