Structural Design Pattern

 

Kathiravan  R (21USC010)

Selvabala T(21USC021)

       III B.Sc Computer Science

          SRMV CAS

 

Introduction

Structural design patterns are concerned with how classes and objects can be composed, to form larger structures.The structural design patterns simplifies the structure by identifying the relationships.These patterns focus on, how the classes inherit from each other and how they are composed from other classes.

Types of structural design patterns

There are following 7 types of structural design patterns.

  1. Adapter Pattern

·       An Adapter Pattern says that just "converts the interface of a class into another interface that a client wants".

·       In other words, to provide the interface according to client requirement while using the services of a class with a different interface.

·       The Adapter Pattern is also known as Wrapper.

Advantage of Adapter Pattern:

Ø  It allows two or more previously incompatible objects to interact.

Ø  It allows reusability of existing functionality.

UML for Adapter Pattern:

 


Example For Adapter Pattern :

Let's consider an example where we have two incompatible interfaces, OldSystem and NewSystem, and we want to adapt the OldSystem to work with the NewSystem interface using the Adapter pattern in Java.

// OldSystem interface (the interface we want to adapt)

public interface OldSystem {

voidoldMethod();

}

 

// NewSystem interface (the target interface we want to use)

public interface NewSystem {

voidnewMethod();

}

 

// OldSystemImplementation (the existing class that implements OldSystem)

public class OldSystemImplementation implements OldSystem {

public void oldMethod() {

System.out.println("Calling oldMethod from OldSystemImplementation.");

}

}

 

// Adapter class that adapts OldSystem to NewSystem

public class Adapter implements NewSystem {

privateOldSystemoldSystem;

 

public Adapter(OldSystemoldSystem) {

this.oldSystem = oldSystem;

}

 

public void newMethod() {

// Use the oldMethod of OldSystem to implement newMethod of NewSystem

oldSystem.oldMethod();

}

}

public class Client {

public static void main(String[] args) {

OldSystemoldSystem = new OldSystemImplementation();

NewSystemnewSystem = new Adapter(oldSystem);

 

// Now we can use the newSystem interface to call oldMethod from OldSystem

newSystem.newMethod();

}

}

In this example:

Ø  We have two interfaces, OldSystem and NewSystem.

Ø  We have an implementation of OldSystem called OldSystemImplementation.

Ø  We create an Adapter class that implements the NewSystem interface. This adapter class takes an instance of OldSystem as a parameter in its constructor.

Ø  In the newMethod of the Adapter class, we delegate the call to newMethod to the oldMethod of the OldSystem instance that it holds. This way, we make the OldSystem class compatible with the NewSystem interface.

 

1.     Bridge Pattern

·       A Bridge Pattern says that just "decouple the functional abstraction from the implementation so that the two can vary independently".

·       The Bridge Pattern is also known as Handle or Body.

Advantage of Bridge Pattern:

Ø  It enables the separation of implementation from the interface.

Ø  It improves the extensibility.

Ø  It allows the hiding of implementation details from the client.

UML for Bridge Pattern:



Lets see an Example of Bridge Design Pattern Using Java:

// Java code to demonstrate

// bridge design pattern

 

// abstraction in bridge pattern

abstractclassVehicle {

protectedWorkshop workShop1;

protectedWorkshop workShop2;

 

protectedVehicle(Workshop workShop1, Workshop workShop2)

{

this.workShop1 = workShop1;

this.workShop2 = workShop2;

}

 

abstractpublicvoidmanufacture();

}

 

// Refine abstraction 1 in bridge pattern

classCar extendsVehicle {

publicCar(Workshop workShop1, Workshop workShop2)

{

super(workShop1, workShop2);

}

 

@Override

publicvoidmanufacture()

{

System.out.print("Car ");

workShop1.work();

workShop2.work();

}

}

 

// Refine abstraction 2 in bridge pattern

classBike extendsVehicle {

publicBike(Workshop workShop1, Workshop workShop2)

{

super(workShop1, workShop2);

}

 

@Override

publicvoidmanufacture()

{

System.out.print("Bike ");

workShop1.work();

workShop2.work();

}

}

 

// Implementer for bridge pattern

interfaceWorkshop

{

abstractpublicvoidwork();

}

 

// Concrete implementation 1 for bridge pattern

classProduce implementsWorkshop {

@Override

publicvoidwork()

{

System.out.print("Produced");

}

}

 

// Concrete implementation 2 for bridge pattern

classAssemble implementsWorkshop {

@Override

publicvoidwork()

{

System.out.print(" And");

System.out.println(" Assembled.");

}

}

 

// Demonstration of bridge design pattern

classBridgePattern {

publicstaticvoidmain(String[] args)

{

Vehicle vehicle1 = newCar(newProduce(), newAssemble());

vehicle1.manufacture();

Vehicle vehicle2 = newBike(newProduce(), newAssemble());

vehicle2.manufacture();

}

}

Output :

Car Produced And Assembled.

Bike Produced And Assembled.

ü  Here we’re producing and assembling the two different vehicles using Bridge design pattern.

 

  1. Composite Pattern

·       A Composite Pattern says that just "allow clients to operate in generic manner on objects that may or may not represent a hierarchy of objects".

Advantage of Composite Design Pattern:

Ø  It defines class hierarchies that contain primitive and complex objects.

Ø  It makes easier to you to add new kinds of components.

Ø  It provides flexibility of structure with manageable class or interface.

UML for Composite Pattern:




Example For Composite Design Pattern:

As you can see, there can be many children to a single parent i.e. Composite, but only one parent per child.

// A Java program to demonstrate working of

// Composite Design Pattern with example

// of a company with different

// employee details

importjava.util.ArrayList;

importjava.util.List;

// A common interface for all employee

interface Employee

{

public void showEmployeeDetails();

}

class Developer implements Employee

{

private String name;

private long empId;

private String position;

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

{

// Assign the Employee id,

// name and the position

this.empId = empId;

this.name = name;

this.position = position;

}

@Override

public void showEmployeeDetails()

{

System.out.println(empId+" " +name+ " " + position );

}

}

class Manager implements Employee

{

private String name;

private long empId;

private String position;

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

{

this.empId = empId;

this.name = name;

this.position = position;

}

@Override

public void showEmployeeDetails()

{

System.out.println(empId+" " +name+ " " + position );

}

}

 

// Class used to get Employee List

// and do the opertions like

// add or remove Employee

classCompanyDirectory implements Employee

{

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

@Override

public void showEmployeeDetails()

{

for(Employee emp:employeeList)

{

emp.showEmployeeDetails();

}

}

public void addEmployee(Employee emp)

{

employeeList.add(emp);

}

public void removeEmployee(Employee emp)

{

employeeList.remove(emp);

}

}

// Driver class

public class Company

{

public static void main (String[] args)

{

Developer dev1 = new Developer(100, "Lokesh Sharma", "Pro Developer");

Developer dev2 = new Developer(101, "Vinay Sharma", "Developer");

CompanyDirectoryengDirectory = new CompanyDirectory();

engDirectory.addEmployee(dev1);

engDirectory.addEmployee(dev2);

Manager man1 = new Manager(200, "Kushagra Garg", "SEO Manager");

Manager man2 = new Manager(201, "Vikram Sharma ", "Kushagra's Manager");

CompanyDirectoryaccDirectory = new CompanyDirectory();

accDirectory.addEmployee(man1);

accDirectory.addEmployee(man2);

CompanyDirectory directory = new CompanyDirectory();

directory.addEmployee(engDirectory);

directory.addEmployee(accDirectory);

directory.showEmployeeDetails();

}

}

Output:

100 Lokesh Sharma Pro Developer
101 Vinay Sharma Developer
200 Kushagra Garg SEO Manager
201 VikramSharma  Kushagra's Manager

 

 

1.     Decorator Pattern

·       A Decorator Pattern says that just "attach a flexible additional responsibilities to an object dynamically".

·       The Decorator Pattern is also known as Wrapper.

Advantage of Decorator Pattern:

Ø  It provides greater flexibility than static inheritance.

Ø  It enhances the extensibility of the object, because changes are made by coding new classes.

Ø  It simplifies the coding by allowing you to develop a series of functionality from targeted classes instead of coding all of the behavior into the object.

UML for Decorator Pattern:


Example of  Decorator Design Pattern:

 

// Java program to demonstrate Decorator

// pattern

 

// Abstract Pizza class (All classes extend

// from this)

 

abstract class Pizzai

{

// it is an abstract pizza

String description = "Unkknown Pizza";

 

public String getDescription()

{

return description;

}

 

public abstract intgetCost();

}

 

// The decorator class : It extends Pizza to be

// interchangeable with it toppings decorator can

// also be implemented as an interface

abstract class ToppingsDecorator extends Pizza

{

public abstract String getDescription();

}

 

// Concrete pizza classes

classPeppyPaneer extends Pizza

{

publicPeppyPaneer() { description = "PeppyPaneer"; }

publicintgetCost() { return 100; }

}

classFarmHouse extends Pizza

{

publicFarmHouse() { description = "FarmHouse"; }

publicintgetCost() { return 200; }

}

class Margherita extends Pizza

{

public Margherita() { description = "Margherita"; }

publicintgetCost() { return 100; }

}

classChickenFiesta extends Pizza

{

publicChickenFiesta() { description = "ChickenFiesta";}

publicintgetCost() { return 200; }

}

classSimplePizza extends Pizza

{

publicSimplePizza() { description = "SimplePizza"; }

publicintgetCost() { return 50; }

}

 

// Concrete toppings classes

classFreshTomato extends ToppingsDecorator

{

// we need a reference to obj we are decorating

Pizza pizza;

 

publicFreshTomato(Pizza pizza) { this.pizza = pizza; }

public String getDescription() {

returnpizza.getDescription() + ", Fresh Tomato ";

}

publicintgetCost() { return 40 + pizza.getCost(); }

}

class Barbeque extends ToppingsDecorator

{

Pizza pizza;

public Barbeque(Pizza pizza) { this.pizza = pizza; }

public String getDescription() {

returnpizza.getDescription() + ", Barbeque ";

}

publicintgetCost() { return 90 + pizza.getCost(); }

}

class Paneer extends ToppingsDecorator

{

Pizza pizza;

public Paneer(Pizza pizza) { this.pizza = pizza; }

public String getDescription() {

returnpizza.getDescription() + ", Paneer ";

}

publicintgetCost() { return 70 + pizza.getCost(); }

}

 

// Other toppings can be coded in a similar way

 

// Driver class and method

classPizzaStore

{

public static void main(String args[])

{

// create new margherita pizza

Pizza pizza = new Margherita();

System.out.println(pizza.getDescription() +

" Cost :" + pizza.getCost());

 

// create new FarmHouse pizza

Pizza pizza2 = new FarmHouse();

 

// decorate it with freshtomato topping

pizza2 = new FreshTomato(pizza2);

 

//decorate it with paneer topping

pizza2 = new Paneer(pizza2);

 

System.out.println( pizza2.getDescription() +

" Cost :" + pizza2.getCost());

Pizza pizza3 = new Barbeque(null); //no specific pizza

System.out.println( pizza3.getDescription() + " Cost :" + pizza3.getCost());

}

}

Output:

Margherita Cost :100
FarmHouse, Fresh Tomato , Paneer Cost :310
 

Ø  Notice how we can add/remove new pizzas and toppings with no alteration in previously tested code and toppings and pizzas are decoupled.

 

1.     Facade Pattern

·       A Facade Pattern says that just "just provide a unified and simplified interface to a set of interfaces in a subsystem, therefore it hides the complexities of the subsystem from the client".

·       Practically, every Abstract Factory is a type of Facade.

Advantage of Facade Pattern:

Ø  It shields the clients from the complexities of the sub-system components.

Ø  It promotes loose coupling between subsystems and its clients.

UML for Facade Pattern:


Example of Façade Design Pattern:

Now Let’s try and understand the facade pattern better using a simple example. Let’s consider a hotel. This hotel has a hotel keeper. There are a lot of restaurants inside the hotel e.g. Veg restaurants, Non-Veg restaurants, and Veg/Non Both restaurants. You, as a client want access to different menus of different restaurants. Here, the hotel keeper acts as the facade, as he hides the complexities of the system hotel. Let’s see how it works:

Interface of Hotel

packagestructural.facade;

 

publicinterfaceHotel {

publicMenus getMenus();

}

The hotel interface only returns Menus. Similarly, the Restaurant are of three types and can implement the hotel interface. Let’s have a look at the code for one of the Restaurants.

NonVegRestaurant.java

packagestructural.facade;

 

publicclassNonVegRestaurantimplementsHotel {

 

publicMenus getMenus()

{

NonVegMenunv = newNonVegMenu();

returnnv;

}

}

VegRestaurant.java

packagestructural.facade;

 

publicclassVegRestaurantimplementsHotel {

 

publicMenus getMenus()

{

VegMenu v = newVegMenu();

returnv;

}

}

VegNonBothRestaurant.java

packagestructural.facade;

 

publicclassVegNonBothRestaurantimplementsHotel {

 

publicMenus getMenus()

{

Both b = newBoth();

returnb;

}

}

Now let’s consider the facade,

HotelKeeper.java

/*package whatever //do not write package name here */

 

packagestructural.facade;

 

publicinterfaceHotelKeeper {

 

 

publicVegMenugetVegMenu();

publicNonVegMenugetNonVegMenu();

publicBoth getVegNonMenu();

 

}

HotelKeeperImplementation.java

packagestructural.facade;

 

publicclassHotelKeeperImplementationimplementsHotelKeeper {

 

publicVegMenugetVegMenu()

{

VegRestaurant v = newVegRestaurant();

VegMenuvegMenu = (VegMenu)v.getMenus();

returnvegMenu;

}

 

publicNonVegMenugetNonVegMenu()

{

NonVegRestaurant v = newNonVegRestaurant();

NonVegMenuNonvegMenu = (NonVegMenu)v.getMenus();

returnNonvegMenu;

}

 

publicBoth getVegNonMenu()

{

VegNonBothRestaurant v = newVegNonBothRestaurant();

Both bothMenu = (Both)v.getMenus();

returnbothMenu;

}

}

From this, It is clear that the complex implementation will be done by HotelKeeper himself. The client will just access the HotelKeeper and ask for either Veg, NonVeg or VegNon Both Restaurant menu.

How will the client program access this façade?

packagestructural.facade;

 

publicclassClient

{

publicstaticvoidmain (String[] args)

{

HotelKeeper keeper = newHotelKeeperImplementation();

 

VegMenu v = keeper.getVegMenu();

NonVegMenunv = keeper.getNonVegMenu();

Both = keeper.getVegNonMenu();

 

}

}

In this way, the implementation is sent to the façade. The client is given just one interface and can access only that. This hides all the complexities

1.     Flyweight Pattern

·       A Flyweight Pattern says that just "to reuse already existing similar kind of objects by storing them and create new object when no matching object is found".

Advantage of Flyweight Pattern:

Ø  It reduces the number of objects.

Ø  It reduces the amount of memory and storage devices required if the objects are persisted.

UML for Flyweight Pattern:

 


Here's a Java code example to illustrate the Flyweight design pattern:

Java Code

importjava.util.HashMap;

importjava.util.Map;

// Flyweight interface

interface Shape {

void draw();

}

// Concrete Flyweight

class Circle implements Shape {

private String color;

privateint x;

privateint y;

privateint radius;

public Circle(String color) {

this.color = color;

}

public void setCoordinates(int x, int y) {

this.x = x;

this.y = y;

}

public void setRadius(int radius) {

this.radius = radius;

}

@Override

public void draw() {

System.out.println("Circle: Color " + color + ", Radius " + radius +

", X " + x + ", Y " + y);

}

}

// Flyweight Factory

classShapeFactory {

private static final Map<String, Shape>circleMap = new HashMap<>();

public static Shape getCircle(String color) {

Circle circle = (Circle) circleMap.get(color);

if (circle == null) {

circle = new Circle(color);

circleMap.put(color, circle);

System.out.println("Creating circle of color: " + color);

}

return circle;

}

}

public class FlyweightPatternExample {

private static final String[] colors = {"Red", "Green", "Blue"};

private static final int NUM_CIRCLES = 20;

public static void main(String[] args) {

for (inti = 0; i< NUM_CIRCLES; i++) {

Circle circle = (Circle) ShapeFactory.getCircle(getRandomColor());

circle.setCoordinates(getRandomX(), getRandomY());

circle.setRadius(10);

circle.draw();

}

}

private static String getRandomColor() {

returncolors[(int) (Math.random() * colors.length)];

}

private static intgetRandomX() {

return (int) (Math.random() * 100);

}

private static intgetRandomY() {

return (int) (Math.random() * 100);

}

}

In this example, we have a Shape interface representing the flyweight, and a concrete flyweight class Circle. The ShapeFactory class is responsible for creating and managing flyweight objects (circles) and reusing them when needed to minimize memory usage. The FlyweightPatternExample demonstrates how to use the Flyweight pattern to create and draw circles with different colors and coordinates while reusing existing circle objects whenever possible.

1.     Proxy Pattern

·       Proxy means an object representing another object.

·       A Proxy Pattern provides the control for accessing the original object.

·       So, we can perform many operations like hiding the information of original object, on demand loading etc.

·       Proxy pattern is also known as Surrogate or Placeholder.

Advantage of Proxy Pattern:

Ø  It provides the protection to the original object from the outside world.

UML for Proxy Pattern:

 


Examples

A very simple real life scenario is our college internet, which restricts few site access. The proxy first checks the host you are connecting to, if it is not part of restricted site list, then it connects to the real internet. This example is based on Protection proxies.

Lets see how it works :

Interface of Internet

packagecom.saket.demo.proxy;

 

publicinterfaceInternet

{

publicvoidconnectTo(String serverhost) throwsException;

}

RealInternet.java

packagecom.saket.demo.proxy;

 

publicclassRealInternetimplementsInternet

{

@Override

publicvoidconnectTo(String serverhost)

{

System.out.println("Connecting to "+ serverhost);

}

}

ProxyInternet.java

packagecom.saket.demo.proxy;

 

importjava.util.ArrayList;

importjava.util.List;

 

 

publicclassProxyInternetimplementsInternet

{

privateInternet internet = newRealInternet();

privatestaticList<String>bannedSites;

 

static

{

bannedSites = newArrayList<String>();

bannedSites.add("abc.com");

bannedSites.add("def.com");

bannedSites.add("ijk.com");

bannedSites.add("lnm.com");

}

 

@Override

publicvoidconnectTo(String serverhost) throwsException

{

if(bannedSites.contains(serverhost.toLowerCase()))

{

thrownewException("Access Denied");

}

 

internet.connectTo(serverhost);

}

 

}

Client.java

packagecom.saket.demo.proxy;

 

publicclassClient

{

publicstaticvoidmain (String[] args)

{

Internet internet = newProxyInternet();

try

{

internet.connectTo("geeksforgeeks.org");

internet.connectTo("abc.com");

}

catch(Exception e)

{

System.out.println(e.getMessage());

}

}

}

As one of the site is mentioned in the banned sites, So
Running the program will give the

Output :

Connecting to geeksforgeeks.org

Access Denied








Comments

Popular posts from this blog

DISPLAY CONTROLLER

multiple inheritance

Constructors and Destructors in c++