Lab 10
Lab 10
CS F213
LAB-10 [Design Patterns]
DATE: 26/04/2021 TIME: 02 Hours
1. Factory Pattern
2. Adapter Pattern
3. Composite Pattern
4. Decorator Pattern
1. Factory Pattern:
Factory pattern is one of the most used design patterns in Java. This type of design pattern comes under
creational pattern as this pattern provides one of the best ways to create an object.
In Factory pattern, we create object without exposing the creation logic to the client and refer to
newly created object using a common interface.
Implementation
Factory Pattern
Shape Demo
<<Interface>>
+draw() : void +main() : void
implements implements
implements
Step 2
Create concrete classes implementing the same interface.
Rectangle.java
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
Square.java
public class Square implements Shape {
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
Circle.java
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
Step 3
Create a Factory to generate object of concrete class based on given information.
ShapeFactory.java
public class ShapeFactory {
//use getShape method to get object of type shape
public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
Step 4
Use the Factory to get object of concrete class by passing an information such as type.
FactoryPatternDemo.java
public class FactoryPatternDemo {
public static void main(String[] args) {
ShapeFactory shapeFactory = new ShapeFactory();
//get an object of Circle and call its draw method.
Shape shape1 = shapeFactory.getShape("CIRCLE");
//call draw method of Circle
shape1.draw();
//get an object of Rectangle and call its draw method.
Shape shape2 = shapeFactory.getShape("RECTANGLE");
//call draw method of Rectangle
shape2.draw();
//get an object of Square and call its draw method.
Shape shape3 = shapeFactory.getShape("SQUARE");
//call draw method of circle
shape3.draw();
}
}
2. Adapter Pattern:
Adapter pattern works as a bridge between two incompatible interfaces. This type of design pattern comes
under structural pattern as this pattern combines the capability of two independent interfaces.
This pattern involves a single class which is responsible to join functionalities of independent or
incompatible interfaces. A real life example could be a case of card reader which acts as an adapter
between memory card and a laptop. You plugin the memory card into card reader and card reader into the
laptop so that memory card can be read via laptop.
Adapter pattern is demonstrated via following example in which an audio player device can play mp3 files
only and wants to use an advanced audio player capable of playing vlc and mp4 files.
Implementation
We have a MediaPlayer interface and a concrete class AudioPlayer implementing the MediaPlayer
interface. AudioPlayer can play mp3 format audio files by default.
We are having another interface AdvancedMediaPlayer and concrete classes implementing the
AdvancedMediaPlayer interface. These classes can play vlc and mp4 format files.
<Interface>>
+playVLC() : void
+playMp4() : void
MediaAdapter AudioPlayer
VLC Player Mp4Player
-advancedMusicPlayer: -mediaAdapter:
AdvancedMediaPlayer MediaAdapter
+playVLC() : void +playVLC() : void
+playMp4() : void +playMp4() : void +MediaAdapter() : void +play() : void
+play() : void
Step 1
Create interfaces for Media Player and Advanced Media Player.
MediaPlayer.java
public interface MediaPlayer {
public void play(String audioType, String fileName);
}
AdvancedMediaPlayer.java
public interface AdvancedMediaPlayer {
public void playVlc(String fileName);
public void playMp4(String fileName);
}
Step 2
Create concrete classes implementing the AdvancedMediaPlayer interface.
VlcPlayer.java
public class VlcPlayer implements AdvancedMediaPlayer{
@Override
public void playVlc(String fileName) {
System.out.println("Playing vlc file. Name: "+ fileName);
}
@Override
public void playMp4(String fileName) {
//do nothing
Mp4Player.java
public class Mp4Player implements AdvancedMediaPlayer{
@Override
public void playVlc(String fileName) {
//do nothing
}
@Override
public void playMp4(String fileName) {
System.out.println("Playing mp4 file. Name: "+ fileName);
}
}
Step 3
Create adapter class implementing the MediaPlayer interface.
MediaAdapter.java
public class MediaAdapter implements MediaPlayer {
AdvancedMediaPlayer advancedMusicPlayer;
public MediaAdapter(String audioType){
if(audioType.equalsIgnoreCase("vlc") ){
advancedMusicPlayer = new VlcPlayer();
}else if (audioType.equalsIgnoreCase("mp4")){
advancedMusicPlayer = new Mp4Player();
}
}
@Override
public void play(String audioType, String fileName) {
if(audioType.equalsIgnoreCase("vlc")){
advancedMusicPlayer.playVlc(fileName);
}
else if(audioType.equalsIgnoreCase("mp4")){
advancedMusicPlayer.playMp4(fileName);
}
}
}
Step 4
Create concrete class implementing the MediaPlayer interface.
AudioPlayer.java
public class AudioPlayer implements MediaPlayer {
MediaAdapter mediaAdapter;
@Override
else{
System.out.println("Invalid media. " + audioType + " format not supported");
}
}
}
Step 5
Use the AudioPlayer to play different types of audio formats.
AdapterPatternDemo.java
public class AdapterPatternDemo {
public static void main(String[] args) {
AudioPlayer audioPlayer = new AudioPlayer();
3. Composite Pattern:
Composite pattern is used where we need to treat a group of objects in similar way as a single object.
Composite pattern composes objects in term of a tree structure to represent part as well as whole
hierarchy. This type of design pattern comes under structural pattern as this pattern creates a tree structure
of group of objects.
This pattern creates a class that contains group of its own objects. This class provides ways to modify its
group of same objects.
Composite pattern is demonstrated via following example in which we will show employees hierarchy of
an organization.
Implementation
We have a class Employee which acts as composite pattern actor class. CompositePatternDemo, our demo
class will use Employee class to add department level hierarchy and print all employees.
+main():void
Employees
-name:String
-dept:String
-salary:int
-subordinates:List<Employee>
-Employee()
+add():void
+remove():void
+getSubordinates:List<Employee>
+toString():String
Step 1
Create Employee class having list of Employee objects.
Employee.java
import java.util.ArrayList;
import java.util.List;
public class Employee {
private String name;
private String dept;
private int salary;
private List<Employee> subordinates;
// constructor
public Employee(String name,String dept, int sal) {
this.name = name;
this.dept = dept;
this.salary = sal;
subordinates = new ArrayList<Employee>();
}
public void add(Employee e) {
subordinates.add(e);
}
public void remove(Employee e) {
subordinates.remove(e);
}
public List<Employee> getSubordinates(){
return subordinates;
}
public String toString(){
return ("Employee :[ Name : " + name + ", dept : " + dept + ", salary :" + salary+"
]");
Step 2
Use the Employee class to create and print employee hierarchy.
CompositePatternDemo.java
public class CompositePatternDemo {
public static void main(String[] args) {
Employee CEO = new Employee("John","CEO", 30000);
Employee headSales = new Employee("Robert","Head Sales", 20000);
Employee headMarketing = new Employee("Michel","Head Marketing", 20000);
Employee clerk1 = new Employee("Laura","Marketing", 10000);
Employee clerk2 = new Employee("Bob","Marketing", 10000);
Employee salesExecutive1 = new Employee("Richard","Sales", 10000);
Employee salesExecutive2 = new Employee("Rob","Sales", 10000);
CEO.add(headSales);
CEO.add(headMarketing);
headSales.add(salesExecutive1);
headSales.add(salesExecutive2);
headMarketing.add(clerk1);
headMarketing.add(clerk2);
4. Decorator Pattern:
Decorator pattern allows a user to add new functionality to an existing object without altering its structure.
This type of design pattern comes under structural pattern as this pattern acts as a wrapper to existing class.
This pattern creates a decorator class which wraps the original class and provides additional
functionality keeping class methods signature intact.
We are demonstrating the use of decorator pattern via following example in which we will decorate a
shape with some color without alter shape class.
Implementation
Shape DecoratorPatternDemo
<<Interface>>
ShapeDecorator
+draw():void +main() : void
+shape:Shape
+ShapeDecorator()
implements
Circle Rectangle +draw():void
+draw():void +draw():void
RedShapeDecorator
+shape:Shape
+RedShapeDecorator()
+draw():void
-setRedBorder():void
Step 1
Create an interface.
Shape.java
public interface Shape {
void draw();
}
Step 2
Create concrete classes implementing the same interface.
Rectangle.java
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Shape: Rectangle");
}
}
Circle.java
public class Circle implements Shape {
@Override
public void draw() {
Step 3
Create abstract decorator class implementing the Shape interface.
ShapeDecorator.java
public abstract class ShapeDecorator implements Shape {
protected Shape decoratedShape;
Step 4
Create concrete decorator class extending the ShapeDecorator class.
RedShapeDecorator.java
public class RedShapeDecorator extends ShapeDecorator {
@Override
public void draw() {
decoratedShape.draw();
setRedBorder(decoratedShape);
}
Step 5
Use the RedShapeDecorator to decorate Shape objects.
DecoratorPatternDemo.java
public class DecoratorPatternDemo {
public static void main(String[] args) {
Case Study
1.
Let’s say, we need Currency objects based on country. Now you define an interface called Currency, and
specify your functionality in the form of method. Now implement this interface for different countries, and
each method implementation will be specific to the country.
Now how can we create objects? Create a factory class and provide a static method to get required object.
This method should take input, here input can be country name. Based on the country value, create specific
country object, and return it.
@Override
public String getCurrency() {
return "Rupee";
}
@Override
public String getSymbol() {
return "Rs";
}
@Override
public String getCurrency() {
return "Dollar";
}
@Override
public String getSymbol() {
return "$";
}
}
Factory class to create objects
public class CurrencyFactory {
2.
Let's say, you want to book a flight from Delhi to Toronto. There are 4 types of seats available Economy
class, Premium Economy class, Business class and First class each having an estimated price of $2500,
$3500, $4500 and $5500 respectively.
The fares given above are just the base fares without any extra amenities. There are few available amenities
like WiFi, Live TV and Wine which will be provided only if we opt for them. They cost $10, $50 and $30
respectively.
Once, you opt for any of the available amenities the cost will be added correspondingly and the final cost
will be shown which is to be paid.