SlideShare a Scribd company logo
DAYANANDA SAGAR ACADEMY OF TECHNOLOGY & MANAGEMENT
Department of Computer Science and Design
OOPS DESIGN ( 23CSD33)
Module 3: DESIGN PATTERNS
What are design patterns ?
• Design patterns are essential building blocks in software development, offering time-tested solutions to
recurring design problems.
• By providing standardized ways to solve common design challenges, they promote efficient,
maintainable, and scalable code, which is critical in building robust software systems.
• Design patterns encapsulate best practices and offer a way to communicate complex ideas in a shared
language, making them valuable tools for software engineers.
History of Design Patterns
The concept of design patterns in software engineering originated from the field of architecture and
gradually evolved to become an essential part of programming practices.
In general, a pattern has four essential elements.
• Pattern Name: A label for a design problem, its solution, and consequences, allowing us to discuss design at
a higher level of abstraction. A shared vocabulary simplifies communication and thinking about design
choices.
• Problem: Describes when to use the pattern, detailing the design issue and context. It may include specific
challenges (like representing algorithms as objects) or conditions that suggest the pattern is appropriate.
• Solution: Outlines the key elements, relationships, and collaborations in the design. Rather than a specific
implementation, it serves as a reusable template for solving similar design problems in various contexts.
• Consequences: Explores the results and trade-offs of using the pattern, including space, time, flexibility, and
extensibility impacts. Explicitly listing consequences helps in evaluating design options and understanding
the pattern's effects.
Design patterns play a crucial role in software development, offering a range of benefits that enhance the
quality, scalability, and maintainability of code.
Promote Reusability and Reduce Redundancy: Design patterns provide tried-and-tested solutions to
common design challenges, so developers don’t need to reinvent solutions for recurring problems. This
promotes code reuse and reduces development time.
Enhance Code Maintainability: Patterns organize code in consistent, predictable structures, making it easier
to understand, modify, and extend. This simplifies maintenance as developers can recognize and follow
established patterns in the codebase.
Facilitate Communication and Collaboration: Design patterns create a shared vocabulary for developers.
Names like “Factory” or “Observer” instantly communicate the intent and structure of the design, making it
easier to discuss ideas and design choices with teammates.
Improve Code Flexibility and Scalability: Patterns like "Strategy" or "Decorator" enable flexible, modular
designs, making it easier to add new features or modify existing ones without major rewrites. They promote design
principles such as Open/Closed Principle (open for extension, closed for modification), which is key to scalable
software.
Enhance Code Maintainability: Patterns organize code in consistent, predictable structures, making it easier to
understand, modify, and extend. This simplifies maintenance as developers can recognize and follow established
patterns in the codebase.
Encourage Best Practices: Patterns encapsulate object-oriented best practices, encouraging developers to follow
principles like Single Responsibility, Encapsulation, and Separation of Concerns. This leads to robust, well-
structured code.
Assist in Managing Complexity: Design patterns provide blueprints for managing complex software architectures.
They break down complicated structures into understandable and manageable components, as seen in architectural
patterns like MVC (Model-View-Controller) or Microservices.
Categories of design patterns
1. Creational Design patterns
2. Structural Design patterns
3. Behavioral Design patterns
Creational Design Patterns: Creational Design Pattern abstract the instantiation process. They help in
making a system independent of how its objects are created, composed and represented.
Creational design patterns focus on the process of object instantiation, leveraging inheritance to vary object
creation. These patterns share two key themes:
1. Encapsulation: They encapsulate knowledge about which concrete classes are used.
2. Abstraction: They hide the details of how objects are created and assembled, exposing only their interfaces
through abstract classes.
This approach provides flexibility in determining what, who, how, and when objects are created.
There are five creational design patterns:
1. Singleton Pattern 2. Factory Pattern 3. Abstract Factory Pattern 4. Builder Pattern 5. Prototype Pattern
Singleton Pattern: Ensures a class has only one instance and provides a global access point to it.
Key Features include/ it contains:
• Private constructor.
• Static instance variable.
• Public static method to access the instance.
Example: The Singleton pattern ensures that only one instance of a class is created throughout the lifetime of
an application and provides a global access point to it.
Fig: Class diagram for singleton pattern
The principles of the Singleton Pattern:
• Single Instance: Singleton ensures that only one instance of the class exists throughout the application.
• Global Access: Provide a global point of access to that instance.
• Lazy or Eager Initialization: Support creating the instance either when needed (lazy) or when the class is
loaded (eager).
• Thread Safety: Implement mechanisms to prevent multiple threads from creating separate instances
simultaneously.
• Private Constructor: Restrict direct instantiation by making the constructor private, forcing the use of the
access point
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
• The instance variable is static, meaning it belongs to the class and is shared across all instances.
• It stores the single instance of the Singleton class
• The constructor is private, so no other class can create an instance of the Singleton class using the new
keyword.
• This restriction ensures controlled instance creation through the class itself.
• The method getInstance is static, that means it can be accessed directly using the class name
(Singleton.getInstance()).
• It checks if the instance is null:
• If null: Creates a new instance of the class and assigns it to instance.
• If not null: Returns the already created instance.
• This ensures that only one instance of the class is created.
Singleton class can be instantiated by two methods:
• Early initialization : In this method, class is initialized whether it is to be used or not. The main advantage of
this method is its simplicity. You initiate the class at the time of class loading. Its drawback is that class is
always initialized whether it is being used or not.
• Lazy initialization : In this method, class in initialized only when it is required. It can save you from
instantiating the class when you don’t need it. Generally, lazy initialization is used when we create a
singleton class.
Factory Method Pattern: The Factory Method Design Pattern is a creational design pattern that provides an interface for creating
objects in a superclass, allowing subclasses to alter the type of objects that will be created.
Instead of instantiating objects directly, the Factory Pattern defines an interface or abstract class for creating objects,
allowing subclasses or methods to decide which object to create.
1. Product Interface: // Product interface representing a vehicle
public abstract class Vehicle {
public abstract void printVehicle();
}
2.Concrete Products: // Concrete product classes representing different types of vehicles
public class TwoWheeler extends Vehicle {
public void printVehicle() {
System.out.println("I am two wheeler");
}
}
public class FourWheeler extends Vehicle {
public void printVehicle() {
System.out.println("I am four wheeler");
}
}
3. Creator Interface : (Factory Interface)
// Factory interface defining the factory method
public interface VehicleFactory {
Vehicle createVehicle();
}
4. Concrete Creators (Concrete Factories) :
// Concrete factory class for TwoWheeler
public class TwoWheelerFactory implements VehicleFactory {
public Vehicle createVehicle() {
return new TwoWheeler();
}
}
// Concrete factory class for FourWheeler
public class FourWheelerFactory implements VehicleFactory {
public Vehicle createVehicle() {
return new FourWheeler();
}
}
• Vehicle serves as the Product interface, defining the common method printVehicle() that all concrete
products must implement.
• TwoWheeler and FourWheeler are concrete product classes representing different types of vehicles,
implementing the printVehicle() method.
• VehicleFactory acts as the Creator interface (Factory Interface) with a method createVehicle() representing
the factory method.
• TwoWheelerFactory and FourWheelerFactory are concrete creator classes (Concrete Factories)
implementing the VehicleFactory interface to create instances of specific types of vehicles.
Let’s implement a simple factory that creates different types of shapes (Circle, Rectangle, Square).
Step 1: Define an interface
// Shape.java
public interface Shape {
void draw();
}
Step 2: Create concrete implementations
// Circle.java
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Drawing a Circle");
}
}
// Rectangle.java
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Drawing a Rectangle");
}
}
// Square.java
public class Square implements Shape {
@Override
public void draw() {
System.out.println("Drawing a Square");
}
}
Step 3: Create the Factory
// ShapeFactory.java
public class ShapeFactory {
// Factory method
public Shape getShape(String shapeType) {
if (shapeType == null) {
return null;
}
if (shapeType.equalsIgnoreCase("CIRCLE")) {
return new Circle();
} else if (shapeType.equalsIgnoreCase("RECTANGLE")) {
return new Rectangle();
} else if (shapeType.equalsIgnoreCase("SQUARE")) {
return new Square();
}
return null;
}
}
Step 4: Use the Factory in the Client Code
// FactoryPatternDemo.java
public class FactoryPatternDemo {
public static void main(String[] args) {
ShapeFactory shapeFactory = new ShapeFactory();
// Get a Circle object and call its draw method
Shape shape1 = shapeFactory.getShape("CIRCLE");
shape1.draw();
// Get a Rectangle object and call its draw method
Shape shape2 = shapeFactory.getShape("RECTANGLE");
shape2.draw();
// Get a Square object and call its draw method
Shape shape3 = shapeFactory.getShape("SQUARE");
shape3.draw();
}
}
The Abstract Factory Pattern: It is a creational design pattern that provides an interface for creating families
of related or dependent objects without specifying their concrete classes. It is often used when there are
multiple related object types, and the client code needs to be decoupled from their specific implementations.
Key Concepts
Abstract Factory: Declares methods to create abstract product objects.
Concrete Factory: Implements the methods of the Abstract Factory to create specific products.
Abstract Product: Declares an interface for a type of product.
Concrete Product: Implements the Abstract Product interface.
Client: Uses only the interfaces provided by the Abstract Factory and Abstract Product.
Example: GUI Themes (Windows vs Mac) :
Suppose you are designing a GUI toolkit that supports multiple operating systems (Windows and Mac). Each
OS has its own style for UI components like buttons and checkboxes.
Step 1: Define Abstract Products
// Button.java
public interface Button {
void render();
}
// Checkbox.java
public interface Checkbox {
void render();
}
Step 2: Create Concrete Products
// WindowsButton.java
public class WindowsButton implements Button {
@Override
public void render() {
System.out.println("Rendering Windows Button");
}
}
// MacButton.java
public class MacButton implements Button {
@Override
public void render() {
System.out.println("Rendering Mac Button");
}
}
// WindowsCheckbox.java
public class WindowsCheckbox implements Checkbox {
@Override
public void render() {
System.out.println("Rendering Windows Checkbox");
}
}
// MacCheckbox.java
public class MacCheckbox implements Checkbox {
@Override
public void render() {
System.out.println("Rendering Mac Checkbox");
}
}
Step 3: Define the Abstract Factory
// GUIFactory.java
public interface GUIFactory {
Button createButton();
Checkbox createCheckbox();
}
Step 4: Implement Concrete Factories
// WindowsFactory.java
public class WindowsFactory implements GUIFactory {
@Override
public Button createButton() {
return new WindowsButton();
}
@Override
public Checkbox createCheckbox() {
return new WindowsCheckbox();
}
}
// MacFactory.java
public class MacFactory implements GUIFactory {
@Override
public Button createButton() {
return new MacButton();
}
@Override
public Checkbox createCheckbox() {
return new MacCheckbox();
}
}
Step 5: Client Code
The client code uses the GUIFactory interface to create UI components, making it independent of the specific implementations.
// Application.java
public class Application {
private Button button;
private Checkbox checkbox;
public Application(GUIFactory factory) {
// Use the factory to create components
button = factory.createButton();
checkbox = factory.createCheckbox();
}
public void render() {
button.render();
checkbox.render();
}
}
// Main.java
public class Main {
public static void main(String[] args) {
// Choose a factory based on the OS
GUIFactory factory;
String os = "Windows"; // Change to "Mac" to test MacFactory
if (os.equalsIgnoreCase("Windows")) {
factory = new WindowsFactory();
} else {
factory = new MacFactory();
}
// Pass the factory to the application
Application app = new Application(factory);
app.render();
}
}
Let us consider an example of vehicle manufacturing company. It produces 2 types of vehicles, that is
Bikes and car. And each type can be either Electric or Petrol.
The factory should ensure:
• Electric components (like motors) are used only for electric vehicles.
• Petrol components (like engines) are used only for petrol vehicles.
Step 1: Define Abstract Products
// Car.java
public interface Car {
void assemble();
}
// Bike.java
public interface Bike {
void assemble();
}
Step 2: Create Concrete Products
// ElectricCar.java
public class ElectricCar implements Car {
@Override
public void assemble() {
System.out.println("Assembling Electric Car with motor and battery.");
}
}
// PetrolCar.java
public class PetrolCar implements Car {
@Override
public void assemble() {
System.out.println("Assembling Petrol Car with engine and fuel tank.");
}
}
// ElectricBike.java
public class ElectricBike implements Bike {
@Override
public void assemble() {
System.out.println("Assembling Electric Bike with motor and battery.");
}
}
// PetrolBike.java
public class PetrolBike implements Bike {
@Override
public void assemble() {
System.out.println("Assembling Petrol Bike with engine and fuel tank.");
}
}
Step 3: Define the Abstract Factory
// VehicleFactory.java
public interface VehicleFactory {
Car createCar();
Bike createBike();
}
Step 4: Create Concrete Factories
// ElectricVehicleFactory.java
public class ElectricVehicleFactory implements VehicleFactory {
@Override
public Car createCar() {
return new ElectricCar();
}
@Override
public Bike createBike() {
return new ElectricBike();
}
}
// PetrolVehicleFactory.java
public class PetrolVehicleFactory implements VehicleFactory {
@Override
public Car createCar() {
return new PetrolCar();
}
@Override
public Bike createBike() {
return new PetrolBike();
}
}
Step 5: Client Code: The client will decide which type of factory to use at runtime, based on the market
requirement (electric or petrol).
// ManufacturingPlant.java
public class ManufacturingPlant {
public static void main(String[] args) {
VehicleFactory factory;
// Switch between factories dynamically
String market = "Electric"; // Change to "Petrol" to test PetrolVehicleFactory
if (market.equalsIgnoreCase("Electric")) {
factory = new ElectricVehicleFactory();
} else {
factory = new PetrolVehicleFactory();
}
// Use the factory to create vehicles
Car car = factory.createCar();
Bike bike = factory.createBike();
// Assemble vehicles
car.assemble();
bike.assemble();
}
}
Structural Design Patterns
• Structural Design Patterns are solutions in software design that focus on how classes and objects are organized to
form larger, functional structures.
• These patterns help developers simplify relationships between objects, making code more efficient, flexible, and
easy to maintain.
• By using structural patterns, you can better manage complex class hierarchies, reuse existing code, and create
scalable architectures.
• It is helpful when you want to combine features from different classes that were created separately.
• Instead of rewriting or modifying these classes, you can use this pattern to make them work together smoothly.
• Structural design patterns focus on how objects are combined to create new features.
• Instead of relying on inheritance (which is fixed at compile-time), these patterns compose objects dynamically
to add or modify functionality.
• The real advantage of this pattern is its flexibility. You can add or change features while the program is running,
which isn’t possible with traditional inheritance because inheritance locks the structure when you write the code.
Types of Structural Design Patterns:
Adapter Method Design Pattern:
• Adapter Method or Adapter Design Pattern also knows as wrapper.
• It converts the interface of a class into another interface which clients expect.
• Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.
When to Use the Adapter Pattern:
• Mismatch Between Interfaces:
You have a class that you want to use, but its methods or structure (interface) don’t match what your
program expects. The adapter acts like a "translator" to make them compatible.
• Making Unrelated Classes Work Together:
You want to create a class that can work with other classes that are unrelated or not designed to work
together. The adapter helps connect them by making their interfaces compatible.
• Reusing Many Classes Without Sub classing:
If you need to work with several existing subclasses, it would be tedious to modify or subclass each one just
to adapt their interface. Instead, you can create a single object adapter that wraps around these classes and
adapts them to what you need.
Components of Adapter Design Pattern
• Target Interface: Defines the interface expected by the client. It represents the set of operations that the
client code can use. It’s the common interface that the client code interacts with.
• Adaptee: The existing class or system with an incompatible interface that needs to be integrated into the
new system. It’s the class or system that the client code cannot directly use due to interface mismatches.
• Adapter: A class that implements the target interface and internally uses an instance of the adaptee to make
it compatible with the target interface. It acts as a bridge, adapting the interface of the adaptee to match the
target interface.
• Client: The code that uses the target interface to interact with objects. It remains unaware of the specific
implementation details of the adaptee and the adapter. It’s the code that benefits from the integration of the
adaptee into the system through the adapter.
How Adapter Design Pattern works?
• Step 1: The client initiates a request by calling a method on the adapter via the target interface.
• Step 2: The adapter maps or transforms the client’s request into a format that the adaptee can understand
using the adaptee’s interface.
• Step 3: The adaptee does the actual job based on the translated request from the adapter.
• Step 4: The client receives the results of the call, remaining unaware of the adapter’s presence or the
specific details of the adaptee.
1. Target Interface: MediaPlayer
• The MediaPlayer interface defines a standard method play() that the client (AudioPlayer) will use.
• It acts as the abstraction the client depends on.
• Every media player, whether it supports .mp3 or advanced formats, must implement this interface.
2. Adaptee Classes: Mp4Player and VlcPlayer
• These classes represent the existing functionality for playing .mp4 and .vlc files.
• They implement the AdvancedMediaPlayer interface, which has methods specific to each format.
• Mp4Player can play .mp4 files but does nothing for .vlc files.
• VlcPlayer can play .vlc files but does nothing for .mp4 files.
• These classes are incompatible with the MediaPlayer interface because they do not implement play().
3. Adapter Class: MediaAdapter
• The MediaAdapter bridges the gap between the MediaPlayer interface and the AdvancedMediaPlayer
classes (Mp4Player and VlcPlayer).
• The MediaAdapter implements the MediaPlayer interface.
• it internally uses AdvancedMediaPlayer objects (Mp4Player or VlcPlayer) to provide the required
functionality.
• When the play() method is called, the adapter determines the media type and delegates the task to the
appropriate AdvancedMediaPlayer.
4. Client Class: AudioPlayer
• The AudioPlayer is the client class that uses the MediaPlayer interface.
• AudioPlayer can directly play .mp3 files.
• For .mp4 and .vlc files, it uses the MediaAdapter to handle playback.
• The MediaAdapter dynamically maps the request to the appropriate AdvancedMediaPlayer.
5. Main Class: Testing the Adapter
The main class demonstrates how the client (AudioPlayer) interacts with the MediaPlayer interface to play
different types of files.
Example: Media Player
1. Target Interface:The interface that the client expects.
interface MediaPlayer {
void play(String audioType, String fileName);
}
2. Existing Adaptee Classes: Classes with the functionality we want to adapt.
interface AdvancedMediaPlayer {
void playMp4(String fileName);
void playVlc(String fileName);
}
class Mp4Player implements AdvancedMediaPlayer {
@Override
public void playMp4(String fileName) {
System.out.println("Playing mp4 file. Name: " + fileName);
}
@Override
public void playVlc(String fileName) {
// Do nothing
}
}
class VlcPlayer implements AdvancedMediaPlayer {
@Override
public void playMp4(String fileName) {
// Do nothing
}
@Override
public void playVlc(String fileName) {
System.out.println("Playing vlc file. Name: " + fileName);
}
}
3. Adapter Class: Bridges the gap between MediaPlayer and AdvancedMediaPlayer.
class MediaAdapter implements MediaPlayer {
private AdvancedMediaPlayer advancedMediaPlayer;
public MediaAdapter(String audioType) {
if (audioType.equalsIgnoreCase("mp4")) {
advancedMediaPlayer = new Mp4Player();
} else if (audioType.equalsIgnoreCase("vlc")) {
advancedMediaPlayer = new VlcPlayer();
}
}
@Override
public void play(String audioType, String fileName) {
if (audioType.equalsIgnoreCase("mp4")) {
advancedMediaPlayer.playMp4(fileName);
} else if (audioType.equalsIgnoreCase("vlc")) {
advancedMediaPlayer.playVlc(fileName);
}
}
}
4. Client Class: The main class using the adapter to play different formats.
class AudioPlayer implements MediaPlayer {
@Override
public void play(String audioType, String fileName) {
if (audioType.equalsIgnoreCase("mp3")) {
System.out.println("Playing mp3 file. Name: " + fileName);
} else if (audioType.equalsIgnoreCase("mp4") || audioType.equalsIgnoreCase("vlc")) {
MediaPlayer adapter = new MediaAdapter(audioType);
adapter.play(audioType, fileName);
} else {
System.out.println("Invalid media type. Only mp3, mp4, and vlc formats supported.");
}
}
}
5. Main Class to Test the Adapter:
public class MediaPlayerApp {
public static void main(String[] args) {
MediaPlayer player = new AudioPlayer();
// Playing mp3 file
player.play("mp3", "song.mp3");
// Playing mp4 file
player.play("mp4", "video.mp4");
// Playing vlc file
player.play("vlc", "movie.vlc");
// Unsupported format
player.play("avi", "file.avi");
}
}
Output: Playing mp3 file. Name: song.mp3
Playing mp4 file. Name: video.mp4
Playing vlc file. Name: movie.vlc
Invalid media type. Only mp3, mp4, and vlc formats supported.
The Target Interface (MediaPlayer) defines the standard for the client.
The Adapter Class (MediaAdapter) converts the requests into a format understandable by the Adaptee
(Mp4Player, VlcPlayer).
The Client (AudioPlayer) uses the adapter seamlessly without worrying about format compatibility.
What is the Decorator Method Design Pattern?
• The Decorator design pattern is a structural pattern used in object-oriented programming to add new
functionality to objects dynamically without altering their structure.
• In Java, this pattern is often employed to extend the behavior of objects in a flexible and reusable way.
Characteristics of the Decorator Method Design Pattern
• This pattern promotes flexibility and extensibility in software systems by allowing developers to compose
objects with different combinations of functionalities at runtime.
• It follows the open/closed principle, as new decorators can be added without modifying existing code,
making it a powerful tool for building modular and customizable software components.
• The Decorator Pattern is commonly used in scenarios where a variety of optional features or behaviors need
to be added to objects in a flexible and reusable manner, such as in text formatting, graphical user interfaces,
or customization of products like coffee or ice cream.
How to Implement Decorator Design Pattern?
• Define the Component Interface: Create a common interface that both concrete components and
decorators will implement.
• Create Concrete Components: Implement the concrete classes that will be decorated, ensuring they adhere
to the component interface.
• Create the Base Decorator: Develop an abstract decorator class that implements the component interface
and holds a reference to a component object.
• Implement Concrete Decorators: Create specific decorator classes that extend the base decorator, adding
their own behavior while delegating to the component object.
• Use the Decorators: Instantiate the concrete component and wrap it with decorators as needed to enhance
functionality dynamically.
Step1:- Create an interface
Shape.java
public interface Shape {
void draw();
}
Step2:- 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() {
System.out.println("Shape: Circle");
}
}
Step 3: Create abstract decorator class implementing the Shape interface.
ShapeDecorator.java
public abstract class ShapeDecorator implements Shape {
protected Shape decoratedShape;
public ShapeDecorator(Shape decoratedShape){
this.decoratedShape = decoratedShape;
}
public void draw(){
decoratedShape.draw();
}
}
Step 4: Create concrete decorator class extending the ShapeDecorator class.
RedShapeDecorator.java
public class RedShapeDecorator extends ShapeDecorator {
public RedShapeDecorator(Shape decoratedShape) {
super(decoratedShape);
}
@Override
public void draw() {
decoratedShape.draw();
setRedBorder(decoratedShape);
}
private void setRedBorder(Shape decoratedShape){
System.out.println("Border Color: Red");
}
}
Step 5: Use the RedShapeDecorator to decorate Shape objects.
DecoratorPatternDemo.java
public class DecoratorPatternDemo {
public static void main(String[] args) {
Shape circle = new Circle();
Shape redCircle = new RedShapeDecorator(new Circle());
Shape redRectangle = new RedShapeDecorator(new Rectangle());
System.out.println("Circle with normal border");
circle.draw();
System.out.println("nCircle of red border");
redCircle.draw();
System.out.println("nRectangle of red border");
redRectangle.draw();
}
}
• The Bridge Pattern is a structural design pattern that decouples an abstraction from its implementation,
allowing them to vary independently.
• This pattern is often used when a class has multiple dimensions of variability (e.g., types and platforms), and
you want to avoid a combinatorial explosion of subclasses.
• Instead of tightly coupling the abstraction to a specific implementation, the pattern introduces a "bridge"
between them, enabling flexibility and scalability.
Components of bridge pattern:
1. Abstraction
• This is the high-level interface or abstract class that defines the core functionality.
• It contains a reference to the Implementer, which acts as a "bridge" to the actual implementation.
• The Abstraction delegates the implementation-specific tasks to the Implementer.
2. Refined Abstraction
• This is a specialized or extended version of the Abstraction.
• It may add extra functionalities or fine-tuned behaviors while still delegating implementation-specific tasks
to the Implementer.
3. Implementer
• This is the interface or abstract class that defines the implementation details.
• It doesn't need to match the Abstraction interface and can be entirely different.
• The Abstraction uses the Implementer to perform the actual work.
4. Concrete Implementer
• These are the specific implementations of the Implementer interface.
• They provide the concrete behavior that the Abstraction depends on.
How the Bridge Pattern Works (Flow):
• Abstraction interacts with the client and provides high-level functionality.
• It delegates the low-level implementation details to the Implementer.
• Refined Abstraction extends the Abstraction to add more specific behaviors.
• Concrete Implementer defines how the implementation is actually performed.
Implementer Interface
interface Sender {
void send(String message);
}
2. Concrete Implementers
class EmailSender implements Sender {
@Override
public void send(String message) {
System.out.println("Sending Email: " + message);
}
}
class SMSSender implements Sender {
@Override
public void send(String message) {
System.out.println("Sending SMS: " + message);
}
}
3. Abstraction
abstract class NotificationSender {
protected Sender sender;
protected NotificationSender(Sender sender) {
this.sender = sender;
}
public abstract void sendNotification(String message);
}
4. Refined Abstraction
class UrgentNotificationSender extends NotificationSender {
public UrgentNotificationSender(Sender sender) {
super(sender);
}
@Override
public void sendNotification(String message) {
System.out.println("Urgent Notification: ");
sender.send(message);
}
}
class RegularNotificationSender extends NotificationSender {
public RegularNotificationSender(Sender sender) {
super(sender);
}
@Override
public void sendNotification(String message) {
System.out.println("Regular Notification: ");
sender.send(message);
}
}
5. Client Code
public class BridgePattern {
public static void main(String[] args) {
Sender emailSender = new EmailSender();
Sender smsSender = new SMSSender();
NotificationSender urgentEmailNotification = new UrgentNotificationSender(emailSender);
urgentEmailNotification.sendNotification("This is an urgent email notification!");
NotificationSender regularSMSNotification = new RegularNotificationSender(smsSender);
regularSMSNotification.sendNotification("This is a regular SMS notification.");
}
}
Behavioral Design Patterns
Behavioral design patterns are a category of design patterns that focus on the interactions and communication
between objects. They help define how objects collaborate and distribute responsibility among them, making it
easier to manage complex control flow and communication in a system.
Iterator Pattern
• The Iterator pattern is a widely used design pattern in software development that provides a way to access
the elements of an aggregate object (such as a list or collection) sequentially without exposing its underlying
representation.
• The Iterator design pattern is a behavioral design pattern that provides a way to access the elements of an
aggregate object (like a list) sequentially without exposing its underlying representation.
• It defines a separate object, called an iterator, which encapsulates the details of traversing the elements of the
aggregate, allowing the aggregate to change its internal structure without affecting the way its elements are
accessed.
Components of iterator pattern
1. Iterator Interface
Defines the methods required to traverse the elements of the collection. Common methods include:
hasNext(): Checks if there are more elements in the collection.
next(): Returns the next element in the collection.
(Optional) remove(): Removes the current element (in some implementations).
2. Concrete Iterator
This is the implementation of the Iterator interface for a specific collection. It maintains the state of the
iteration.
3. Aggregate Interface (Collection)
• Defines the interface for the collection that will provide an iterator. This typically includes a method like:
createIterator(): Returns an iterator object for the collection
4. Concrete Aggregate (Concrete Collection)
Implements the Aggregate interface and provides the specific implementation of the iterator creation. The
collection itself may store data in various structures (e.g., arrays, lists, trees).
5. Client
• The client uses the iterator to access elements of the collection without needing to understand the internal
structure of the collection.
1. Iterator Interface
Defines the methods required to traverse the elements of the collection. Common methods include:
hasNext(): Checks if there are more elements in the collection.
next(): Returns the next element in the collection.
(Optional) remove(): Removes the current element (in some implementations).
createIterator(): Returns an iterator object for the collection
// Iterator Interface
interface Iterator<T> {
boolean hasNext();
T next();
}
// Concrete Iterator
class ListIterator<T> implements Iterator<T> {
private List<T> list;
private int position = 0;
public ListIterator(List<T> list) {
this.list = list;
}
public boolean hasNext() {
return position < list.size();
}
public T next() {
return list.get(position++);
}
}
// Aggregate Interface
interface Aggregate<T> {
Iterator<T> createIterator();
}
// Concrete Aggregate
class CustomList<T> implements Aggregate<T> {
private List<T> items = new ArrayList<>();
public void add(T item) {
items.add(item);
}
MVC Design Pattern
The MVC design pattern is a software architecture pattern that separates an application into three main
components:
Model, View, and Controller, making it easier to manage and maintain the codebase. It also allows for the
reusability of components and promotes a more modular approach to software development.
• The Model View Controller (MVC) design pattern specifies that an application consists of a data model,
presentation information, and control information. The pattern requires that each of these be separated into
different objects.
• The MVC pattern separates the concerns of an application into three distinct components, each responsible
for a specific aspect of the application’s functionality.
• This separation of concerns makes the application easier to maintain and extend, as changes to one
component do not require changes to the other components.
Components of the MVC Design Pattern
1. Model:The Model component in the MVC (Model-View-Controller) design pattern demonstrates the data
and business logic of an application. It is responsible for managing the application’s data, processing business
rules, and responding to requests for information from other components, such as the View and the Controller.
• View: Displays the data from the Model to the user and sends user inputs to the Controller. It is passive and
does not directly interact with the Model. Instead, it receives data from the Model and sends user inputs to
the Controller for processing.
3. Controller: Controller acts as an intermediary between the Model and the View. It handles user input and
updates the Model accordingly and updates the View to reflect changes in the Model. It contains application
logic, such as input validation and data transformation.
Communication between the Components
• User Interaction with View: The user interacts with the View, such as clicking a button or entering text into
a form.
• View Receives User Input: The View receives the user input and forwards it to the Controller.
• Controller Processes User Input: The Controller receives the user input from the View. It interprets the
input, performs any necessary operations (such as updating the Model), and decides how to respond.
• Controller Updates Model: The Controller updates the Model based on the user input or application logic.
• Model Notifies View of Changes: If the Model changes, it notifies the View.
• View Requests Data from Model: The View requests data from the Model to update its display.
• Controller Updates View: The Controller updates the View based on the changes in the Model or in
response to user input.
• View Renders Updated UI: The View renders the updated UI based on the changes made by the Controller.

More Related Content

PPTX
JS Design patterns in Web technologies including oop techniques.pptx
PPTX
Software Architecture and Design Patterns Notes.pptx
PPTX
Creational Design Patterns.pptx
PPT
Introduction To Design Patterns
PPTX
Introduction to Design Patterns
PPTX
Lecture-7.pptx software design and Arthitechure
PPT
Design pattern in android
PPTX
design pattern is the computer scicence subject
JS Design patterns in Web technologies including oop techniques.pptx
Software Architecture and Design Patterns Notes.pptx
Creational Design Patterns.pptx
Introduction To Design Patterns
Introduction to Design Patterns
Lecture-7.pptx software design and Arthitechure
Design pattern in android
design pattern is the computer scicence subject

Similar to OOPSDesign PPT ( introduction to opps and design ( (20)

PPTX
UNIT IV DESIGN PATTERNS.pptx
PPT
Design patterns
PPT
Object Oriented Analysis and Design
PPTX
PPT
Design Patterns
PDF
Module 2 design patterns-2
PPTX
Software Patterns
PPT
Software Design Patterns
PPT
Software Design Patterns
PPTX
Design pattern-presentation
PDF
Java Design Patterns Interview Questions PDF By ScholarHat
PPTX
Design pattern of software words computer .pptx
PPT
Oops design pattern intro
PPTX
Typescript design patterns applied to sharepoint framework - Sharepoint Satur...
PPTX
Design patterns
PPT
P Training Presentation
PPT
6 Design Pattern.ppt design pattern in softeare engineering
PPT
Design pattern
PDF
Cse 6007 fall2012
PPTX
Design Patterns - General Introduction
UNIT IV DESIGN PATTERNS.pptx
Design patterns
Object Oriented Analysis and Design
Design Patterns
Module 2 design patterns-2
Software Patterns
Software Design Patterns
Software Design Patterns
Design pattern-presentation
Java Design Patterns Interview Questions PDF By ScholarHat
Design pattern of software words computer .pptx
Oops design pattern intro
Typescript design patterns applied to sharepoint framework - Sharepoint Satur...
Design patterns
P Training Presentation
6 Design Pattern.ppt design pattern in softeare engineering
Design pattern
Cse 6007 fall2012
Design Patterns - General Introduction
Ad

Recently uploaded (20)

PDF
Chalkpiece Annual Report from 2019 To 2025
PDF
Facade & Landscape Lighting Techniques and Trends.pptx.pdf
PDF
Interior Structure and Construction A1 NGYANQI
PPT
Machine printing techniques and plangi dyeing
PPTX
mahatma gandhi bus terminal in india Case Study.pptx
PDF
Trusted Executive Protection Services in Ontario — Discreet & Professional.pdf
PDF
Urban Design Final Project-Site Analysis
PPTX
Complete Guide to Microsoft PowerPoint 2019 – Features, Tools, and Tips"
PPTX
rapid fire quiz in your house is your india.pptx
PPTX
BSCS lesson 3.pptxnbbjbb mnbkjbkbbkbbkjb
PPT
WHY_R12 Uaafafafpgradeaffafafafaffff.ppt
PPTX
YV PROFILE PROJECTS PROFILE PRES. DESIGN
PDF
SEVA- Fashion designing-Presentation.pdf
PPTX
Causes of Flooding by Slidesgo sdnl;asnjdl;asj.pptx
PPTX
CLASSIFICATION OF YARN- process, explanation
PPTX
Implications Existing phase plan and its feasibility.pptx
PPTX
Special finishes, classification and types, explanation
PPTX
DOC-20250430-WA0014._20250714_235747_0000.pptx
PPTX
AC-Unit1.pptx CRYPTOGRAPHIC NNNNFOR ALL
PPTX
joggers park landscape assignment bandra
Chalkpiece Annual Report from 2019 To 2025
Facade & Landscape Lighting Techniques and Trends.pptx.pdf
Interior Structure and Construction A1 NGYANQI
Machine printing techniques and plangi dyeing
mahatma gandhi bus terminal in india Case Study.pptx
Trusted Executive Protection Services in Ontario — Discreet & Professional.pdf
Urban Design Final Project-Site Analysis
Complete Guide to Microsoft PowerPoint 2019 – Features, Tools, and Tips"
rapid fire quiz in your house is your india.pptx
BSCS lesson 3.pptxnbbjbb mnbkjbkbbkbbkjb
WHY_R12 Uaafafafpgradeaffafafafaffff.ppt
YV PROFILE PROJECTS PROFILE PRES. DESIGN
SEVA- Fashion designing-Presentation.pdf
Causes of Flooding by Slidesgo sdnl;asnjdl;asj.pptx
CLASSIFICATION OF YARN- process, explanation
Implications Existing phase plan and its feasibility.pptx
Special finishes, classification and types, explanation
DOC-20250430-WA0014._20250714_235747_0000.pptx
AC-Unit1.pptx CRYPTOGRAPHIC NNNNFOR ALL
joggers park landscape assignment bandra
Ad

OOPSDesign PPT ( introduction to opps and design (

  • 1. DAYANANDA SAGAR ACADEMY OF TECHNOLOGY & MANAGEMENT Department of Computer Science and Design OOPS DESIGN ( 23CSD33)
  • 2. Module 3: DESIGN PATTERNS What are design patterns ? • Design patterns are essential building blocks in software development, offering time-tested solutions to recurring design problems. • By providing standardized ways to solve common design challenges, they promote efficient, maintainable, and scalable code, which is critical in building robust software systems. • Design patterns encapsulate best practices and offer a way to communicate complex ideas in a shared language, making them valuable tools for software engineers. History of Design Patterns The concept of design patterns in software engineering originated from the field of architecture and gradually evolved to become an essential part of programming practices.
  • 3. In general, a pattern has four essential elements. • Pattern Name: A label for a design problem, its solution, and consequences, allowing us to discuss design at a higher level of abstraction. A shared vocabulary simplifies communication and thinking about design choices. • Problem: Describes when to use the pattern, detailing the design issue and context. It may include specific challenges (like representing algorithms as objects) or conditions that suggest the pattern is appropriate. • Solution: Outlines the key elements, relationships, and collaborations in the design. Rather than a specific implementation, it serves as a reusable template for solving similar design problems in various contexts. • Consequences: Explores the results and trade-offs of using the pattern, including space, time, flexibility, and extensibility impacts. Explicitly listing consequences helps in evaluating design options and understanding the pattern's effects.
  • 4. Design patterns play a crucial role in software development, offering a range of benefits that enhance the quality, scalability, and maintainability of code. Promote Reusability and Reduce Redundancy: Design patterns provide tried-and-tested solutions to common design challenges, so developers don’t need to reinvent solutions for recurring problems. This promotes code reuse and reduces development time. Enhance Code Maintainability: Patterns organize code in consistent, predictable structures, making it easier to understand, modify, and extend. This simplifies maintenance as developers can recognize and follow established patterns in the codebase. Facilitate Communication and Collaboration: Design patterns create a shared vocabulary for developers. Names like “Factory” or “Observer” instantly communicate the intent and structure of the design, making it easier to discuss ideas and design choices with teammates.
  • 5. Improve Code Flexibility and Scalability: Patterns like "Strategy" or "Decorator" enable flexible, modular designs, making it easier to add new features or modify existing ones without major rewrites. They promote design principles such as Open/Closed Principle (open for extension, closed for modification), which is key to scalable software. Enhance Code Maintainability: Patterns organize code in consistent, predictable structures, making it easier to understand, modify, and extend. This simplifies maintenance as developers can recognize and follow established patterns in the codebase. Encourage Best Practices: Patterns encapsulate object-oriented best practices, encouraging developers to follow principles like Single Responsibility, Encapsulation, and Separation of Concerns. This leads to robust, well- structured code. Assist in Managing Complexity: Design patterns provide blueprints for managing complex software architectures. They break down complicated structures into understandable and manageable components, as seen in architectural patterns like MVC (Model-View-Controller) or Microservices.
  • 6. Categories of design patterns 1. Creational Design patterns 2. Structural Design patterns 3. Behavioral Design patterns Creational Design Patterns: Creational Design Pattern abstract the instantiation process. They help in making a system independent of how its objects are created, composed and represented. Creational design patterns focus on the process of object instantiation, leveraging inheritance to vary object creation. These patterns share two key themes: 1. Encapsulation: They encapsulate knowledge about which concrete classes are used. 2. Abstraction: They hide the details of how objects are created and assembled, exposing only their interfaces through abstract classes. This approach provides flexibility in determining what, who, how, and when objects are created.
  • 7. There are five creational design patterns: 1. Singleton Pattern 2. Factory Pattern 3. Abstract Factory Pattern 4. Builder Pattern 5. Prototype Pattern Singleton Pattern: Ensures a class has only one instance and provides a global access point to it. Key Features include/ it contains: • Private constructor. • Static instance variable. • Public static method to access the instance. Example: The Singleton pattern ensures that only one instance of a class is created throughout the lifetime of an application and provides a global access point to it. Fig: Class diagram for singleton pattern
  • 8. The principles of the Singleton Pattern: • Single Instance: Singleton ensures that only one instance of the class exists throughout the application. • Global Access: Provide a global point of access to that instance. • Lazy or Eager Initialization: Support creating the instance either when needed (lazy) or when the class is loaded (eager). • Thread Safety: Implement mechanisms to prevent multiple threads from creating separate instances simultaneously. • Private Constructor: Restrict direct instantiation by making the constructor private, forcing the use of the access point
  • 9. public class Singleton { private static Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } • The instance variable is static, meaning it belongs to the class and is shared across all instances. • It stores the single instance of the Singleton class
  • 10. • The constructor is private, so no other class can create an instance of the Singleton class using the new keyword. • This restriction ensures controlled instance creation through the class itself. • The method getInstance is static, that means it can be accessed directly using the class name (Singleton.getInstance()). • It checks if the instance is null: • If null: Creates a new instance of the class and assigns it to instance. • If not null: Returns the already created instance. • This ensures that only one instance of the class is created. Singleton class can be instantiated by two methods: • Early initialization : In this method, class is initialized whether it is to be used or not. The main advantage of this method is its simplicity. You initiate the class at the time of class loading. Its drawback is that class is always initialized whether it is being used or not. • Lazy initialization : In this method, class in initialized only when it is required. It can save you from instantiating the class when you don’t need it. Generally, lazy initialization is used when we create a singleton class.
  • 11. Factory Method Pattern: The Factory Method Design Pattern is a creational design pattern that provides an interface for creating objects in a superclass, allowing subclasses to alter the type of objects that will be created. Instead of instantiating objects directly, the Factory Pattern defines an interface or abstract class for creating objects, allowing subclasses or methods to decide which object to create. 1. Product Interface: // Product interface representing a vehicle public abstract class Vehicle { public abstract void printVehicle(); }
  • 12. 2.Concrete Products: // Concrete product classes representing different types of vehicles public class TwoWheeler extends Vehicle { public void printVehicle() { System.out.println("I am two wheeler"); } } public class FourWheeler extends Vehicle { public void printVehicle() { System.out.println("I am four wheeler"); } }
  • 13. 3. Creator Interface : (Factory Interface) // Factory interface defining the factory method public interface VehicleFactory { Vehicle createVehicle(); } 4. Concrete Creators (Concrete Factories) : // Concrete factory class for TwoWheeler public class TwoWheelerFactory implements VehicleFactory { public Vehicle createVehicle() { return new TwoWheeler(); } }
  • 14. // Concrete factory class for FourWheeler public class FourWheelerFactory implements VehicleFactory { public Vehicle createVehicle() { return new FourWheeler(); } } • Vehicle serves as the Product interface, defining the common method printVehicle() that all concrete products must implement. • TwoWheeler and FourWheeler are concrete product classes representing different types of vehicles, implementing the printVehicle() method. • VehicleFactory acts as the Creator interface (Factory Interface) with a method createVehicle() representing the factory method. • TwoWheelerFactory and FourWheelerFactory are concrete creator classes (Concrete Factories) implementing the VehicleFactory interface to create instances of specific types of vehicles.
  • 15. Let’s implement a simple factory that creates different types of shapes (Circle, Rectangle, Square). Step 1: Define an interface // Shape.java public interface Shape { void draw(); } Step 2: Create concrete implementations // Circle.java public class Circle implements Shape { @Override public void draw() { System.out.println("Drawing a Circle"); } }
  • 16. // Rectangle.java public class Rectangle implements Shape { @Override public void draw() { System.out.println("Drawing a Rectangle"); } } // Square.java public class Square implements Shape { @Override public void draw() { System.out.println("Drawing a Square"); } }
  • 17. Step 3: Create the Factory // ShapeFactory.java public class ShapeFactory { // Factory method public Shape getShape(String shapeType) { if (shapeType == null) { return null; } if (shapeType.equalsIgnoreCase("CIRCLE")) { return new Circle(); } else if (shapeType.equalsIgnoreCase("RECTANGLE")) { return new Rectangle(); } else if (shapeType.equalsIgnoreCase("SQUARE")) { return new Square(); } return null; } }
  • 18. Step 4: Use the Factory in the Client Code // FactoryPatternDemo.java public class FactoryPatternDemo { public static void main(String[] args) { ShapeFactory shapeFactory = new ShapeFactory(); // Get a Circle object and call its draw method Shape shape1 = shapeFactory.getShape("CIRCLE"); shape1.draw(); // Get a Rectangle object and call its draw method Shape shape2 = shapeFactory.getShape("RECTANGLE"); shape2.draw(); // Get a Square object and call its draw method Shape shape3 = shapeFactory.getShape("SQUARE"); shape3.draw(); } }
  • 19. The Abstract Factory Pattern: It is a creational design pattern that provides an interface for creating families of related or dependent objects without specifying their concrete classes. It is often used when there are multiple related object types, and the client code needs to be decoupled from their specific implementations. Key Concepts Abstract Factory: Declares methods to create abstract product objects. Concrete Factory: Implements the methods of the Abstract Factory to create specific products. Abstract Product: Declares an interface for a type of product. Concrete Product: Implements the Abstract Product interface. Client: Uses only the interfaces provided by the Abstract Factory and Abstract Product. Example: GUI Themes (Windows vs Mac) : Suppose you are designing a GUI toolkit that supports multiple operating systems (Windows and Mac). Each OS has its own style for UI components like buttons and checkboxes.
  • 20. Step 1: Define Abstract Products // Button.java public interface Button { void render(); } // Checkbox.java public interface Checkbox { void render(); }
  • 21. Step 2: Create Concrete Products // WindowsButton.java public class WindowsButton implements Button { @Override public void render() { System.out.println("Rendering Windows Button"); } } // MacButton.java public class MacButton implements Button { @Override public void render() { System.out.println("Rendering Mac Button"); } }
  • 22. // WindowsCheckbox.java public class WindowsCheckbox implements Checkbox { @Override public void render() { System.out.println("Rendering Windows Checkbox"); } } // MacCheckbox.java public class MacCheckbox implements Checkbox { @Override public void render() { System.out.println("Rendering Mac Checkbox"); } }
  • 23. Step 3: Define the Abstract Factory // GUIFactory.java public interface GUIFactory { Button createButton(); Checkbox createCheckbox(); } Step 4: Implement Concrete Factories // WindowsFactory.java public class WindowsFactory implements GUIFactory { @Override public Button createButton() { return new WindowsButton(); } @Override public Checkbox createCheckbox() { return new WindowsCheckbox(); } }
  • 24. // MacFactory.java public class MacFactory implements GUIFactory { @Override public Button createButton() { return new MacButton(); } @Override public Checkbox createCheckbox() { return new MacCheckbox(); } }
  • 25. Step 5: Client Code The client code uses the GUIFactory interface to create UI components, making it independent of the specific implementations. // Application.java public class Application { private Button button; private Checkbox checkbox; public Application(GUIFactory factory) { // Use the factory to create components button = factory.createButton(); checkbox = factory.createCheckbox(); } public void render() { button.render(); checkbox.render(); } }
  • 26. // Main.java public class Main { public static void main(String[] args) { // Choose a factory based on the OS GUIFactory factory; String os = "Windows"; // Change to "Mac" to test MacFactory if (os.equalsIgnoreCase("Windows")) { factory = new WindowsFactory(); } else { factory = new MacFactory(); } // Pass the factory to the application Application app = new Application(factory); app.render(); } }
  • 27. Let us consider an example of vehicle manufacturing company. It produces 2 types of vehicles, that is Bikes and car. And each type can be either Electric or Petrol. The factory should ensure: • Electric components (like motors) are used only for electric vehicles. • Petrol components (like engines) are used only for petrol vehicles. Step 1: Define Abstract Products // Car.java public interface Car { void assemble(); } // Bike.java public interface Bike { void assemble(); }
  • 28. Step 2: Create Concrete Products // ElectricCar.java public class ElectricCar implements Car { @Override public void assemble() { System.out.println("Assembling Electric Car with motor and battery."); } } // PetrolCar.java public class PetrolCar implements Car { @Override public void assemble() { System.out.println("Assembling Petrol Car with engine and fuel tank."); } }
  • 29. // ElectricBike.java public class ElectricBike implements Bike { @Override public void assemble() { System.out.println("Assembling Electric Bike with motor and battery."); } } // PetrolBike.java public class PetrolBike implements Bike { @Override public void assemble() { System.out.println("Assembling Petrol Bike with engine and fuel tank."); } }
  • 30. Step 3: Define the Abstract Factory // VehicleFactory.java public interface VehicleFactory { Car createCar(); Bike createBike(); } Step 4: Create Concrete Factories // ElectricVehicleFactory.java public class ElectricVehicleFactory implements VehicleFactory { @Override public Car createCar() { return new ElectricCar(); } @Override public Bike createBike() { return new ElectricBike(); } }
  • 31. // PetrolVehicleFactory.java public class PetrolVehicleFactory implements VehicleFactory { @Override public Car createCar() { return new PetrolCar(); } @Override public Bike createBike() { return new PetrolBike(); } }
  • 32. Step 5: Client Code: The client will decide which type of factory to use at runtime, based on the market requirement (electric or petrol). // ManufacturingPlant.java public class ManufacturingPlant { public static void main(String[] args) { VehicleFactory factory; // Switch between factories dynamically String market = "Electric"; // Change to "Petrol" to test PetrolVehicleFactory if (market.equalsIgnoreCase("Electric")) { factory = new ElectricVehicleFactory(); } else { factory = new PetrolVehicleFactory(); }
  • 33. // Use the factory to create vehicles Car car = factory.createCar(); Bike bike = factory.createBike(); // Assemble vehicles car.assemble(); bike.assemble(); } }
  • 34. Structural Design Patterns • Structural Design Patterns are solutions in software design that focus on how classes and objects are organized to form larger, functional structures. • These patterns help developers simplify relationships between objects, making code more efficient, flexible, and easy to maintain. • By using structural patterns, you can better manage complex class hierarchies, reuse existing code, and create scalable architectures. • It is helpful when you want to combine features from different classes that were created separately. • Instead of rewriting or modifying these classes, you can use this pattern to make them work together smoothly. • Structural design patterns focus on how objects are combined to create new features. • Instead of relying on inheritance (which is fixed at compile-time), these patterns compose objects dynamically to add or modify functionality. • The real advantage of this pattern is its flexibility. You can add or change features while the program is running, which isn’t possible with traditional inheritance because inheritance locks the structure when you write the code.
  • 35. Types of Structural Design Patterns: Adapter Method Design Pattern: • Adapter Method or Adapter Design Pattern also knows as wrapper. • It converts the interface of a class into another interface which clients expect. • Adapter lets classes work together that couldn't otherwise because of incompatible interfaces. When to Use the Adapter Pattern: • Mismatch Between Interfaces: You have a class that you want to use, but its methods or structure (interface) don’t match what your program expects. The adapter acts like a "translator" to make them compatible. • Making Unrelated Classes Work Together: You want to create a class that can work with other classes that are unrelated or not designed to work together. The adapter helps connect them by making their interfaces compatible. • Reusing Many Classes Without Sub classing: If you need to work with several existing subclasses, it would be tedious to modify or subclass each one just to adapt their interface. Instead, you can create a single object adapter that wraps around these classes and adapts them to what you need.
  • 36. Components of Adapter Design Pattern • Target Interface: Defines the interface expected by the client. It represents the set of operations that the client code can use. It’s the common interface that the client code interacts with. • Adaptee: The existing class or system with an incompatible interface that needs to be integrated into the new system. It’s the class or system that the client code cannot directly use due to interface mismatches. • Adapter: A class that implements the target interface and internally uses an instance of the adaptee to make it compatible with the target interface. It acts as a bridge, adapting the interface of the adaptee to match the target interface. • Client: The code that uses the target interface to interact with objects. It remains unaware of the specific implementation details of the adaptee and the adapter. It’s the code that benefits from the integration of the adaptee into the system through the adapter.
  • 37. How Adapter Design Pattern works? • Step 1: The client initiates a request by calling a method on the adapter via the target interface. • Step 2: The adapter maps or transforms the client’s request into a format that the adaptee can understand using the adaptee’s interface. • Step 3: The adaptee does the actual job based on the translated request from the adapter. • Step 4: The client receives the results of the call, remaining unaware of the adapter’s presence or the specific details of the adaptee.
  • 38. 1. Target Interface: MediaPlayer • The MediaPlayer interface defines a standard method play() that the client (AudioPlayer) will use. • It acts as the abstraction the client depends on. • Every media player, whether it supports .mp3 or advanced formats, must implement this interface. 2. Adaptee Classes: Mp4Player and VlcPlayer • These classes represent the existing functionality for playing .mp4 and .vlc files. • They implement the AdvancedMediaPlayer interface, which has methods specific to each format. • Mp4Player can play .mp4 files but does nothing for .vlc files. • VlcPlayer can play .vlc files but does nothing for .mp4 files. • These classes are incompatible with the MediaPlayer interface because they do not implement play(). 3. Adapter Class: MediaAdapter • The MediaAdapter bridges the gap between the MediaPlayer interface and the AdvancedMediaPlayer classes (Mp4Player and VlcPlayer). • The MediaAdapter implements the MediaPlayer interface.
  • 39. • it internally uses AdvancedMediaPlayer objects (Mp4Player or VlcPlayer) to provide the required functionality. • When the play() method is called, the adapter determines the media type and delegates the task to the appropriate AdvancedMediaPlayer. 4. Client Class: AudioPlayer • The AudioPlayer is the client class that uses the MediaPlayer interface. • AudioPlayer can directly play .mp3 files. • For .mp4 and .vlc files, it uses the MediaAdapter to handle playback. • The MediaAdapter dynamically maps the request to the appropriate AdvancedMediaPlayer. 5. Main Class: Testing the Adapter The main class demonstrates how the client (AudioPlayer) interacts with the MediaPlayer interface to play different types of files.
  • 40. Example: Media Player 1. Target Interface:The interface that the client expects. interface MediaPlayer { void play(String audioType, String fileName); } 2. Existing Adaptee Classes: Classes with the functionality we want to adapt. interface AdvancedMediaPlayer { void playMp4(String fileName); void playVlc(String fileName); } class Mp4Player implements AdvancedMediaPlayer { @Override public void playMp4(String fileName) { System.out.println("Playing mp4 file. Name: " + fileName); }
  • 41. @Override public void playVlc(String fileName) { // Do nothing } } class VlcPlayer implements AdvancedMediaPlayer { @Override public void playMp4(String fileName) { // Do nothing } @Override public void playVlc(String fileName) { System.out.println("Playing vlc file. Name: " + fileName); } }
  • 42. 3. Adapter Class: Bridges the gap between MediaPlayer and AdvancedMediaPlayer. class MediaAdapter implements MediaPlayer { private AdvancedMediaPlayer advancedMediaPlayer; public MediaAdapter(String audioType) { if (audioType.equalsIgnoreCase("mp4")) { advancedMediaPlayer = new Mp4Player(); } else if (audioType.equalsIgnoreCase("vlc")) { advancedMediaPlayer = new VlcPlayer(); } }
  • 43. @Override public void play(String audioType, String fileName) { if (audioType.equalsIgnoreCase("mp4")) { advancedMediaPlayer.playMp4(fileName); } else if (audioType.equalsIgnoreCase("vlc")) { advancedMediaPlayer.playVlc(fileName); } } } 4. Client Class: The main class using the adapter to play different formats. class AudioPlayer implements MediaPlayer { @Override public void play(String audioType, String fileName) {
  • 44. if (audioType.equalsIgnoreCase("mp3")) { System.out.println("Playing mp3 file. Name: " + fileName); } else if (audioType.equalsIgnoreCase("mp4") || audioType.equalsIgnoreCase("vlc")) { MediaPlayer adapter = new MediaAdapter(audioType); adapter.play(audioType, fileName); } else { System.out.println("Invalid media type. Only mp3, mp4, and vlc formats supported."); } } } 5. Main Class to Test the Adapter: public class MediaPlayerApp { public static void main(String[] args) { MediaPlayer player = new AudioPlayer();
  • 45. // Playing mp3 file player.play("mp3", "song.mp3"); // Playing mp4 file player.play("mp4", "video.mp4"); // Playing vlc file player.play("vlc", "movie.vlc"); // Unsupported format player.play("avi", "file.avi"); } }
  • 46. Output: Playing mp3 file. Name: song.mp3 Playing mp4 file. Name: video.mp4 Playing vlc file. Name: movie.vlc Invalid media type. Only mp3, mp4, and vlc formats supported. The Target Interface (MediaPlayer) defines the standard for the client. The Adapter Class (MediaAdapter) converts the requests into a format understandable by the Adaptee (Mp4Player, VlcPlayer). The Client (AudioPlayer) uses the adapter seamlessly without worrying about format compatibility.
  • 47. What is the Decorator Method Design Pattern? • The Decorator design pattern is a structural pattern used in object-oriented programming to add new functionality to objects dynamically without altering their structure. • In Java, this pattern is often employed to extend the behavior of objects in a flexible and reusable way. Characteristics of the Decorator Method Design Pattern • This pattern promotes flexibility and extensibility in software systems by allowing developers to compose objects with different combinations of functionalities at runtime. • It follows the open/closed principle, as new decorators can be added without modifying existing code, making it a powerful tool for building modular and customizable software components. • The Decorator Pattern is commonly used in scenarios where a variety of optional features or behaviors need to be added to objects in a flexible and reusable manner, such as in text formatting, graphical user interfaces, or customization of products like coffee or ice cream.
  • 48. How to Implement Decorator Design Pattern? • Define the Component Interface: Create a common interface that both concrete components and decorators will implement. • Create Concrete Components: Implement the concrete classes that will be decorated, ensuring they adhere to the component interface. • Create the Base Decorator: Develop an abstract decorator class that implements the component interface and holds a reference to a component object. • Implement Concrete Decorators: Create specific decorator classes that extend the base decorator, adding their own behavior while delegating to the component object. • Use the Decorators: Instantiate the concrete component and wrap it with decorators as needed to enhance functionality dynamically.
  • 49. Step1:- Create an interface Shape.java public interface Shape { void draw(); } Step2:- Create concrete classes implementing the same interface. Rectangle.java public class Rectangle implements Shape { @Override public void draw() { System.out.println("Shape: Rectangle"); } }
  • 50. Circle.java public class Circle implements Shape { @Override public void draw() { System.out.println("Shape: Circle"); } } Step 3: Create abstract decorator class implementing the Shape interface. ShapeDecorator.java public abstract class ShapeDecorator implements Shape { protected Shape decoratedShape; public ShapeDecorator(Shape decoratedShape){ this.decoratedShape = decoratedShape; }
  • 51. public void draw(){ decoratedShape.draw(); } } Step 4: Create concrete decorator class extending the ShapeDecorator class. RedShapeDecorator.java public class RedShapeDecorator extends ShapeDecorator { public RedShapeDecorator(Shape decoratedShape) { super(decoratedShape); } @Override public void draw() { decoratedShape.draw(); setRedBorder(decoratedShape); }
  • 52. private void setRedBorder(Shape decoratedShape){ System.out.println("Border Color: Red"); } } Step 5: Use the RedShapeDecorator to decorate Shape objects. DecoratorPatternDemo.java public class DecoratorPatternDemo { public static void main(String[] args) { Shape circle = new Circle(); Shape redCircle = new RedShapeDecorator(new Circle());
  • 53. Shape redRectangle = new RedShapeDecorator(new Rectangle()); System.out.println("Circle with normal border"); circle.draw(); System.out.println("nCircle of red border"); redCircle.draw(); System.out.println("nRectangle of red border"); redRectangle.draw(); } }
  • 54. • The Bridge Pattern is a structural design pattern that decouples an abstraction from its implementation, allowing them to vary independently. • This pattern is often used when a class has multiple dimensions of variability (e.g., types and platforms), and you want to avoid a combinatorial explosion of subclasses. • Instead of tightly coupling the abstraction to a specific implementation, the pattern introduces a "bridge" between them, enabling flexibility and scalability. Components of bridge pattern: 1. Abstraction • This is the high-level interface or abstract class that defines the core functionality. • It contains a reference to the Implementer, which acts as a "bridge" to the actual implementation. • The Abstraction delegates the implementation-specific tasks to the Implementer. 2. Refined Abstraction • This is a specialized or extended version of the Abstraction. • It may add extra functionalities or fine-tuned behaviors while still delegating implementation-specific tasks to the Implementer.
  • 55. 3. Implementer • This is the interface or abstract class that defines the implementation details. • It doesn't need to match the Abstraction interface and can be entirely different. • The Abstraction uses the Implementer to perform the actual work. 4. Concrete Implementer • These are the specific implementations of the Implementer interface. • They provide the concrete behavior that the Abstraction depends on. How the Bridge Pattern Works (Flow): • Abstraction interacts with the client and provides high-level functionality. • It delegates the low-level implementation details to the Implementer. • Refined Abstraction extends the Abstraction to add more specific behaviors. • Concrete Implementer defines how the implementation is actually performed.
  • 56. Implementer Interface interface Sender { void send(String message); } 2. Concrete Implementers class EmailSender implements Sender { @Override public void send(String message) { System.out.println("Sending Email: " + message); } }
  • 57. class SMSSender implements Sender { @Override public void send(String message) { System.out.println("Sending SMS: " + message); } } 3. Abstraction abstract class NotificationSender { protected Sender sender; protected NotificationSender(Sender sender) { this.sender = sender; } public abstract void sendNotification(String message); }
  • 58. 4. Refined Abstraction class UrgentNotificationSender extends NotificationSender { public UrgentNotificationSender(Sender sender) { super(sender); } @Override public void sendNotification(String message) { System.out.println("Urgent Notification: "); sender.send(message); } }
  • 59. class RegularNotificationSender extends NotificationSender { public RegularNotificationSender(Sender sender) { super(sender); } @Override public void sendNotification(String message) { System.out.println("Regular Notification: "); sender.send(message); } }
  • 60. 5. Client Code public class BridgePattern { public static void main(String[] args) { Sender emailSender = new EmailSender(); Sender smsSender = new SMSSender(); NotificationSender urgentEmailNotification = new UrgentNotificationSender(emailSender); urgentEmailNotification.sendNotification("This is an urgent email notification!"); NotificationSender regularSMSNotification = new RegularNotificationSender(smsSender); regularSMSNotification.sendNotification("This is a regular SMS notification."); } }
  • 61. Behavioral Design Patterns Behavioral design patterns are a category of design patterns that focus on the interactions and communication between objects. They help define how objects collaborate and distribute responsibility among them, making it easier to manage complex control flow and communication in a system.
  • 62. Iterator Pattern • The Iterator pattern is a widely used design pattern in software development that provides a way to access the elements of an aggregate object (such as a list or collection) sequentially without exposing its underlying representation. • The Iterator design pattern is a behavioral design pattern that provides a way to access the elements of an aggregate object (like a list) sequentially without exposing its underlying representation. • It defines a separate object, called an iterator, which encapsulates the details of traversing the elements of the aggregate, allowing the aggregate to change its internal structure without affecting the way its elements are accessed. Components of iterator pattern 1. Iterator Interface Defines the methods required to traverse the elements of the collection. Common methods include: hasNext(): Checks if there are more elements in the collection. next(): Returns the next element in the collection. (Optional) remove(): Removes the current element (in some implementations).
  • 63. 2. Concrete Iterator This is the implementation of the Iterator interface for a specific collection. It maintains the state of the iteration. 3. Aggregate Interface (Collection) • Defines the interface for the collection that will provide an iterator. This typically includes a method like: createIterator(): Returns an iterator object for the collection 4. Concrete Aggregate (Concrete Collection) Implements the Aggregate interface and provides the specific implementation of the iterator creation. The collection itself may store data in various structures (e.g., arrays, lists, trees). 5. Client • The client uses the iterator to access elements of the collection without needing to understand the internal structure of the collection.
  • 64. 1. Iterator Interface Defines the methods required to traverse the elements of the collection. Common methods include: hasNext(): Checks if there are more elements in the collection. next(): Returns the next element in the collection. (Optional) remove(): Removes the current element (in some implementations). createIterator(): Returns an iterator object for the collection // Iterator Interface interface Iterator<T> { boolean hasNext(); T next(); }
  • 65. // Concrete Iterator class ListIterator<T> implements Iterator<T> { private List<T> list; private int position = 0; public ListIterator(List<T> list) { this.list = list; } public boolean hasNext() { return position < list.size(); } public T next() { return list.get(position++); } }
  • 66. // Aggregate Interface interface Aggregate<T> { Iterator<T> createIterator(); } // Concrete Aggregate class CustomList<T> implements Aggregate<T> { private List<T> items = new ArrayList<>(); public void add(T item) { items.add(item); }
  • 67. MVC Design Pattern The MVC design pattern is a software architecture pattern that separates an application into three main components: Model, View, and Controller, making it easier to manage and maintain the codebase. It also allows for the reusability of components and promotes a more modular approach to software development. • The Model View Controller (MVC) design pattern specifies that an application consists of a data model, presentation information, and control information. The pattern requires that each of these be separated into different objects. • The MVC pattern separates the concerns of an application into three distinct components, each responsible for a specific aspect of the application’s functionality. • This separation of concerns makes the application easier to maintain and extend, as changes to one component do not require changes to the other components. Components of the MVC Design Pattern 1. Model:The Model component in the MVC (Model-View-Controller) design pattern demonstrates the data and business logic of an application. It is responsible for managing the application’s data, processing business rules, and responding to requests for information from other components, such as the View and the Controller.
  • 68. • View: Displays the data from the Model to the user and sends user inputs to the Controller. It is passive and does not directly interact with the Model. Instead, it receives data from the Model and sends user inputs to the Controller for processing.
  • 69. 3. Controller: Controller acts as an intermediary between the Model and the View. It handles user input and updates the Model accordingly and updates the View to reflect changes in the Model. It contains application logic, such as input validation and data transformation. Communication between the Components • User Interaction with View: The user interacts with the View, such as clicking a button or entering text into a form. • View Receives User Input: The View receives the user input and forwards it to the Controller. • Controller Processes User Input: The Controller receives the user input from the View. It interprets the input, performs any necessary operations (such as updating the Model), and decides how to respond. • Controller Updates Model: The Controller updates the Model based on the user input or application logic. • Model Notifies View of Changes: If the Model changes, it notifies the View. • View Requests Data from Model: The View requests data from the Model to update its display. • Controller Updates View: The Controller updates the View based on the changes in the Model or in response to user input. • View Renders Updated UI: The View renders the updated UI based on the changes made by the Controller.