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.
- 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.
- 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 Developer101 Vinay Sharma Developer200 Kushagra Garg SEO Manager201 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 :100FarmHouse, 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
· 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
Post a Comment