0% found this document useful (0 votes)
9 views70 pages

Design Pattern All

The document outlines several creational design patterns including Singleton, Factory, Abstract Factory, Builder, Prototype, Adapter, and Bridge. Each pattern is described with its characteristics, advantages, disadvantages, and best use cases, providing examples in Java code. These patterns help manage object creation and relationships in software design, enhancing maintainability and flexibility.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
9 views70 pages

Design Pattern All

The document outlines several creational design patterns including Singleton, Factory, Abstract Factory, Builder, Prototype, Adapter, and Bridge. Each pattern is described with its characteristics, advantages, disadvantages, and best use cases, providing examples in Java code. These patterns help manage object creation and relationships in software design, enhancing maintainability and flexibility.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Singleton Pattern (Creational Design Pattern)

Singleton is a creational design pattern (object-creation focused, and it ensures


single object creation.

■​ Characteristics
a.​ Only one instance of the class exists in the whole application (single
object creation)
b.​ Global access point: the instance is accessed through one common
method (typically getInstance()).
c.​ Private constructor: prevents new from outside the class, so no one
can create extra objects.
■​ Advantages
a.​ Controlled single instance: avoids multiple objects for the same
“shared resource”.
b.​ Easy access: one global point to retrieve the instance.
c.​ Saves memory/resources when creating objects is expensive (e.g.,
heavy managers).
■​ Disadvantages
a.​ Global state risk: can make debugging harder because many classes
depend on it.
b.​ Harder to test: mocking/replacing it in unit tests is tricky.
c.​ Hidden dependencies: classes may silently rely on the Singleton
instead of receiving dependencies cleanly.
■​ Use cases

Use Singleton when exactly one instance should coordinate an


application-wide service, for example:

●​ Configuration manager (read settings once, use everywhere)


●​ Logging service (single logger for the app)
●​ Connection/Resource manager (shared pool/manager)
●​ Caching service (one cache to keep consistent data)
Factory Pattern (Creational Design Pattern)
Factory is an object creation pattern that answers: “Who decides which concrete
class to instantiate?”. It defines a common type/interface, and the factory method
decides which specific class object to create.

1) Key characteristics (high-importance) — 3–4


points
●​ Hides object creation logic: client doesn’t use new everywhere; creation
is centralized inside the factory.
●​ Returns a common type (interface/superclass), but internally chooses
the concrete class based on input/config/environment.
●​ Loosely coupled client code: client depends on the interface, not the
concrete class—easy to change later.
●​ Rules can evolve: if selection logic changes (new types added), you
update the factory instead of many client files.

2) Very simple Java code (Simple Factory style)


// 1) Common interface
interface Shape {
void draw();
}

// 2) Concrete classes
class Circle implements Shape {
public void draw() { [Link]("Drawing Circle"); }
}
class Square implements Shape {
public void draw() { [Link]("Drawing Square"); }
}

// 3) Factory
class ShapeFactory {
public Shape createShape(String type) {
if ([Link]("circle")) return new Circle();
if ([Link]("square")) return new Square();
throw new IllegalArgumentException("Unknown shape: " + type);
}
}
// 4) Client
public class Main {
public static void main(String[] args) {
ShapeFactory factory = new ShapeFactory();
Shape s1 = [Link]("circle");
[Link]();
}
}

3) Advantages and disadvantages


Advantages

●​ Decouples client from concrete classes (client only knows the interface)
●​ Centralizes creation logic (no “construction logic all over the code”)
●​ Easier to extend/change which class gets created when rules change

Disadvantages

●​ Factory can become a “big if-else” when many types exist (harder to
maintain if not structured well).
●​ Extra class/indirection: more files and one more layer to understand.
●​ Adding new product types may require modifying the factory (unless
you design it more flexibly).

4) Where to apply (best use cases)


Use Factory when:

●​ You know you need a general type (e.g., Viewer, Shape, Enemy) but want
to hide which subclass is created
●​ Choice depends on configuration/environment (e.g., file extension .mp3
→ MusicPlayer, .jpg → PhotoViewer)
●​ Your app has changing “selection rules” (today: dessert → cake,
tomorrow: dessert → ice cream) like the restaurant example
Abstract Factory Pattern (Creational)
Abstract Factory is used when you must create families of related objects that
should work together, and you want to avoid mixing mismatched families (e.g.,
Modern vs Victorian furniture set). It provides an interface to create these
families without specifying concrete classes.

1) Key characteristics (high-importance) — 3–4


points
●​ Creates a family of related products (a matched set), not just one product
type (e.g., Button + Checkbox together).
●​ Guarantees compatibility: choosing one factory (e.g., Windows factory)
ensures all created UI elements match and work together.
●​ Client code stays decoupled from concrete classes: client uses only
abstract interfaces (factory + products), concrete classes stay hidden.
●​ Easy to switch entire families: change the factory (Windows ↔ Mac, Dark
↔ Light), and the whole set changes consistently.

2) Very simple Java code (GUI family example)


// Products
interface Button { void paint(); }
interface Checkbox { void paint(); }

// Concrete products (Windows family)


class WinButton implements Button { public void paint(){
[Link]("WinButton"); } }
class WinCheckbox implements Checkbox { public void paint(){
[Link]("WinCheckbox"); } }

// Concrete products (Mac family)


class MacButton implements Button { public void paint(){
[Link]("MacButton"); } }
class MacCheckbox implements Checkbox { public void paint(){
[Link]("MacCheckbox"); } }

// Abstract Factory
interface GUIFactory {
Button createButton();
Checkbox createCheckbox();
}

// Concrete Factories
class WindowsGUIFactory implements GUIFactory {
public Button createButton() { return new WinButton(); }
public Checkbox createCheckbox() { return new WinCheckbox(); }
}

class MacGUIFactory implements GUIFactory {


public Button createButton() { return new MacButton(); }
public Checkbox createCheckbox() { return new MacCheckbox(); }
}

// Client
class Main {
public static void main(String[] args) {
GUIFactory factory = new WindowsGUIFactory(); // swap to MacGUIFactory
anytime
Button b = [Link]();
Checkbox c = [Link]();
[Link]();
[Link]();
}
}

This matches the idea: pick a factory (Windows/Mac), then all created products
are compatible.

3) Advantages and disadvantages


Advantages

●​ Prevents mixing mismatched products (ensures “same style / same


family”).
●​ Switching families is easy: change only the factory, not the client logic.
●​ Strong decoupling: client doesn’t depend on concrete product classes.
Disadvantages

●​ More classes/interfaces: factory interface + multiple product interfaces +


multiple concrete families.
●​ Harder to add a new product type (e.g., add createSlider()): you must
update all factories and families.
●​ Can feel overkill if you don’t truly need families (then normal Factory may
be enough).

4) Where to apply (best use cases)


Use Abstract Factory when:

●​ You need multiple families of products like Windows UI vs Mac UI or Dark


theme vs Light theme, and want to switch the whole set easily.
●​ Your system must create a coherent set of related objects (not just one
object type).
●​ Real examples:
i.​ Cross-platform UI toolkits (Buttons, Menus, Scrollbars must match)
ii.​ Game themes/levels where a “theme” means multiple things
together (enemy + music + background)
iii.​ “CuisineFactory” concept: ItalianFactory makes a matching set
(Pizza + Pasta), BengaliFactory makes a matching set (Biryani +
Borhani).
Builder Pattern (Creational)
Builder is used for step-by-step construction of a complex object, especially
when many parts are optional and constructors become messy. The intent is to
separate construction from representation, so the same process can create
different representations.

1) Key characteristics (high-importance) — 3–4


points
●​ Builds complex objects step-by-step (multi-step creation, often fluent
chaining).
●​ Solves the telescoping constructor problem (too many constructor
parameters becomes unreadable and error-prone).
●​ Readable + flexible: you only set what you need; no need to pass lots of
null/0 for optional values.
●​ Final build() returns the finished object after configuring parts.

2) Very simple Java code (Burger example style)


class Burger {
String size;
boolean cheese;
boolean lettuce;

Burger(String size, boolean cheese, boolean lettuce) {


[Link] = size;
[Link] = cheese;
[Link] = lettuce;
}
}

class BurgerBuilder {
private String size = "medium";
private boolean cheese = false;
private boolean lettuce = false;

public BurgerBuilder setSize(String size) {


[Link] = size;
return this;
}

public BurgerBuilder addCheese() {


[Link] = true;
return this;
}

public BurgerBuilder addLettuce() {


[Link] = true;
return this;
}

public Burger build() {


return new Burger(size, cheese, lettuce);
}
}

public class Main {


public static void main(String[] args) {
Burger b = new BurgerBuilder()
.addCheese()
.addLettuce()
.setSize("large")
.build();
}
}

This matches the “step-by-step + final build()” idea shown in your slides.

3) Advantages and disadvantages


Advantages

●​ Much more readable than long constructors with many parameters.


●​ Avoids wrong parameter order mistakes (common in telescoping
constructors).
●​ Great for optional parts: configure only what you need.
Disadvantages

●​ More code / more classes (Builder class + product class).


●​ If the object is simple (2–3 fields), Builder can be overkill.
●​ You must ensure build() validates required fields (otherwise invalid
objects may be created).

4) Where to apply (best use cases)


Use Builder when:

●​ The object has many optional/configurable parts (RAM/SSD/GPU laptop


configuration).
●​ You want a readable construction style instead of huge constructors.
●​ You may have different ways/builders to create similar objects (e.g.,
GamingLaptopBuilder vs OfficeLaptopBuilder).
●​ Anytime you see the “too many constructor params” pain in code.

Prototype Pattern (Creational)


Prototype creates new objects by copying (cloning) an existing object instead of
creating from scratch using new every time. It’s useful when object creation is
costly, and you want fast duplication.

1) Key characteristics (high-importance) — 3–4


points
●​ Creates objects by cloning an existing “prototype” instance (copy-based
creation).
●​ Avoids expensive initialization: build one fully configured object once,
then clone it quickly.
●​ Supports runtime creation: you can add/register prototypes dynamically
and clone as needed.
●​ Needs copying strategy: you must decide shallow copy vs deep copy
(very important in real use).
2) Very simple Java code (using Cloneable)
class Student implements Cloneable {
String name;
int age;

Student(String name, int age) {


[Link] = name;
[Link] = age;
}

@Override
public Student clone() {
try {
return (Student) [Link](); // shallow copy
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
}

public class Main {


public static void main(String[] args) {
Student s1 = new Student("Ashik", 22);
Student s2 = [Link](); // new object by copying
[Link] = "Rahman";

[Link]([Link]); // Ashik
[Link]([Link]); // Rahman
}
}

3) Advantages and disadvantages


Advantages

●​ Fast object creation when constructors/setup are expensive.


●​ Reduces subclassing/complex creation logic (no need for many “new +
setup” blocks).
●​ Easy to create many similar objects with small changes.
Disadvantages

●​ Cloning can be tricky (especially with nested objects / references).


●​ Shallow vs deep copy problems: shallow copy can cause shared references
(bugs).
●​ In Java, Cloneable and clone() are often considered awkward; many
teams prefer copy constructors or custom copy() methods.

4) Where to apply (best use cases)


Use Prototype when:

●​ You need to create many objects with similar configuration (templates).


●​ Object creation is expensive (DB setup, heavy parsing, large initialization).
●​ You want to keep a registry of ready-made objects and clone them on
demand.
●​ Examples:
○​ Game: cloning enemies/characters with preset stats
○​ UI: copying configured components
○​ Document editors: duplicating shapes/widgets with same styling
Adapter Pattern (Structural) — again, cleaner +
aligned with your slide
Adapter: Convert one interface into another the client expects.​
Key idea from your lecture: Adapter changes the interface but not the behavior.

1) Key characteristics (high-importance) — 3–4


points
●​ Interface conversion: makes an existing class/library usable when its
interface doesn’t match your code.
●​ Wraps an incompatible class (adaptee) inside an Adapter, so you don’t
modify the library code.
●​ Client depends on a Target interface (clean + stable client), adapter plugs
in behind it.
●​ Supports Open–Closed idea: you can add new implementations without
changing the stable client code (avoids big if-else chains).

2) Very simple Java code (same as your Payment


Gateway example)
// Target interface expected by client (Checkout)
interface PaymentGateway {
void pay(float amount);
}

// Existing incompatible library class (cannot change)


class NewGateway {
void makePayment(float total, String currency) {
[Link]("Paid " + total + " " + currency + " via NewGateway");
}
}

// Adapter: makes NewGateway look like PaymentGateway


class NewGatewayAdapter implements PaymentGateway {
private NewGateway gateway = new NewGateway();
public void pay(float amount) {
[Link](amount, "BDT"); // adaptation happens here
}
}

// Client stays clean


class Checkout {
private PaymentGateway gateway;

public Checkout(PaymentGateway gateway) {


[Link] = gateway;
}

void pay(float amount) {


[Link](amount);
}
}

public class Main {


public static void main(String[] args) {
Checkout checkout = new Checkout(new NewGatewayAdapter());
[Link](500);
}
}

This is exactly the “refactored design” concept shown in the slides.

3) Advantages and disadvantages


Advantages

●​ Reuse working existing/3rd-party code with different API (no need to


rewrite it)
●​ Client stays stable/clean (no big if-else chain inside client class like
Checkout)
●​ Better maintainability + testability (selection logic is separated; client is
simpler)

Disadvantages
●​ Extra layer / extra class (more code & indirection).
●​ If you have many incompatible services, you may end up with many
adapters.
●​ Adapter must be updated if the library API changes.

4) Where to apply (best use cases)


Use Adapter when you have an existing class/library that:

●​ Already works
●​ Cannot be changed (closed source / too many dependencies)
●​ But your client expects a different interface

Typical examples from your lecture style:

●​ Payment gateway migration: old code expects pay(amount) but new SDK
gives makePayment(total, currency)
●​ New SMS provider with a different API than the old one → Adapter

Bridge Pattern (Structural)


Bridge separates an abstraction from its implementation so both can vary
independently—this avoids the “class explosion” when two dimensions (like
Shape × Platform) keep changing.

1) Key characteristics (high-importance) — 3–4


points
●​ Two independent dimensions change separately (e.g., Shapes and
Platforms), so inheritance creates a class explosion.
●​ Bridge splits into two hierarchies:
○​ Abstraction side (e.g., Shape)
○​ Implementation side (e.g., Renderer)
●​ Abstraction holds a reference to Implementation (the “bridge”) and
delegates work to it.
●​ Result: you can add new abstractions OR new implementations without
touching the other side.

2) Very simple Java code (Shape + Renderer)


// Implementation hierarchy
interface Renderer {
void drawCircle(int radius);
}

class WindowsRenderer implements Renderer {


public void drawCircle(int radius) {
[Link]("Windows: draw circle r=" + radius);
}
}

class LinuxRenderer implements Renderer {


public void drawCircle(int radius) {
[Link]("Linux: draw circle r=" + radius);
}
}

// Abstraction hierarchy
abstract class Shape {
protected Renderer renderer; // <-- the Bridge
Shape(Renderer renderer) { [Link] = renderer; }
abstract void draw();
}

class Circle extends Shape {


private int radius;
Circle(Renderer renderer, int radius) {
super(renderer);
[Link] = radius;
}
void draw() {
[Link](radius); // delegate to platform
}
}
// Client
public class Main {
public static void main(String[] args) {
Shape c1 = new Circle(new WindowsRenderer(), 10);
Shape c2 = new Circle(new LinuxRenderer(), 10);
[Link]();
[Link]();
}
}

This matches the lecture idea: separate Shape from Renderer and delegate
drawing to the renderer.

3) Advantages and disadvantages


Advantages

●​ Prevents class explosion: classes become #Shapes + #Renderers instead


of #Shapes × #Renderers.
●​ Easier extension: add new shapes without changing renderer code, and
add new renderers without modifying shapes.
●​ Runtime flexibility: you can combine shape + platform dynamically.

Disadvantages

●​ Adds extra abstraction layers (more interfaces/classes).


●​ Slightly more complex than a simple inheritance solution for small
projects.
●​ You must design the “implementation interface” carefully (e.g., renderer
methods) or it gets messy.

4) Where to apply (best use cases)


Use Bridge when:

●​ Your system has two dimensions that vary independently (the exact
problem described in the slides) like Shapes vs Platforms.
●​ You’re building cross-platform or multi-backend features, e.g. multiple
OS-specific renderers for chart drawing (your lecture explicitly maps this
scenario to Bridge).
●​ You want to avoid subclassing combinations like WindowsCircle,
LinuxCircle, WebGLCircle, etc..

Composite Pattern (Structural)


Composite lets you compose objects into tree structures and allows clients to
treat single objects (leaf) and groups (composites) the same way.

1) Key characteristics (highly important) — 3–4


points
●​ Tree / hierarchy support (part–whole): designed for hierarchical
structures where items can contain other items.
●​ Uniform treatment: client uses the same interface for both a single object
and a group of objects.
●​ Recursive behavior: calling a method on a group recursively calls the
same method on children (e.g., draw() or totalPrice()).
●​ Simplifies client code: client doesn’t need special cases like “single vs
group” (avoids scattered grouping logic).

2) Very simple Java code


import [Link].*;

// Common interface

interface Graphic {

void draw();

}
// Leaf

class Dot implements Graphic {

public void draw() {

[Link]("Drawing Dot");

// Composite (can contain leaves or other composites)

class GraphicGroup implements Graphic {

private List<Graphic> children = new ArrayList<>();

public void add(Graphic g) { [Link](g); }

public void draw() {

for (Graphic g : children) {

[Link](); // recursion / uniform call

// Client

public class Main {

public static void main(String[] args) {


Graphic dot1 = new Dot();

Graphic dot2 = new Dot();

GraphicGroup group = new GraphicGroup();

[Link](dot1);

[Link](dot2);

// Client calls draw() once — works for single or group

[Link]();

This matches the “Graphic / Dot / Group / Canvas” style from the slides.

3) Advantages and disadvantages


Advantages

●​ Client becomes simpler: it just thinks in terms of the common type (e.g.,
Graphic), not “Dot vs Group”.
●​ Great for representing structure (part–whole tree) and applying
operations recursively.
●​ Easy to add new leaf types (e.g., Rectangle, Line) as long as they
implement the common interface.

Disadvantages

●​ If you keep adding many new operations (size/search/export/print…), you


may need to modify every node class repeatedly, which breaks
Open–Closed Principle—this is exactly why Visitor is introduced later.
●​ Can be harder to enforce strict rules like “only folders can contain
children” unless you design carefully.

4) Where to apply (best use cases)


Use Composite when you have recursive part–whole structures and want the
same method call to work for both single items and groups, such as:

●​ File system: folders containing files and subfolders


●​ UI components: panels containing buttons/labels/other panels
●​ Game objects: nested regions/rooms/objects, groups of enemies/particles

Decorator Pattern (Structural)


Decorator lets you add new responsibilities (features) to an object at runtime
by wrapping it—without changing the original class (and without changing the
original method signature / interface).

1) Key characteristics (highly important) — 3–4


points
●​ Wraps an object to add behavior (composition: “has-a”) instead of
modifying the class directly.
●​ Same interface: decorators implement the same interface as the
component, so the client can use them exactly like the original object.
●​ Runtime flexibility: you can stack multiple decorators to combine features
dynamically.
●​ Avoids subclass explosion: no need to create many subclasses for every
feature combination.

2) Very simple Java code (Coffee + Add-ons)


// Component
interface Coffee {

String getDescription();

int cost();

// Concrete component

class SimpleCoffee implements Coffee {

public String getDescription() { return "Simple Coffee"; }

public int cost() { return 50; }

// Base Decorator

abstract class CoffeeDecorator implements Coffee {

protected Coffee coffee;

CoffeeDecorator(Coffee coffee) { [Link] = coffee; }

// Concrete Decorators

class Milk extends CoffeeDecorator {

Milk(Coffee coffee) { super(coffee); }

public String getDescription() {

return [Link]() + ", Milk";

}
public int cost() {

return [Link]() + 10;

class Sugar extends CoffeeDecorator {

Sugar(Coffee coffee) { super(coffee); }

public String getDescription() {

return [Link]() + ", Sugar";

public int cost() {

return [Link]() + 5;

// Client

public class Main {

public static void main(String[] args) {

Coffee c = new SimpleCoffee();

c = new Milk(c); // add milk

c = new Sugar(c); // add sugar

[Link]([Link]()); // Simple Coffee, Milk, Sugar


[Link]([Link]()); // 65

3) Advantages and disadvantages


Advantages

●​ Add features without modifying existing code (safe for legacy / stable
classes).
●​ Mix-and-match features by stacking decorators (flexible combinations).
●​ Follows Single Responsibility: each decorator adds one feature.

Disadvantages

●​ Many small classes (one decorator per feature).


●​ Can be harder to debug because behavior is spread across multiple
wrappers.
●​ Order matters sometimes (Milk then Sugar vs Sugar then Milk) — can be
confusing if not designed carefully.

4) Where to apply (best use cases)


Use Decorator when:

●​ You want to add optional features to objects dynamically (runtime), like


add-ons.
●​ You’d otherwise create too many subclasses for combinations:
○​ CoffeeWithMilk, CoffeeWithSugar, CoffeeWithMilkAndSugar, etc.
●​ Typical examples:
○​ UI components: add scrollbars/borders/shadows without changing
core component.
○​ Logging / encryption / compression wrappers around streams
(classic Java use case).
○​ Pricing add-ons: base product + extras (coffee, pizza toppings,
subscription features).
Facade Pattern (Structural)
Facade = hides the complexities of a system and provides a simple interface.​
In your Lecture 20 words: it simplifies a complex subsystem with one entry
point.

1) Key characteristics (highly important) — 3–4


points
●​ Single simple entry point to a complex subsystem (client calls 1–2
methods instead of talking to many classes).
●​ Hides internal complexity: the client doesn’t need to know
decoding/resizing/mixing etc. (ATM analogy: you press “Withdraw 500”,
internal banking is hidden).
●​ Decouples high-level code from low-level classes: clients depend on the
facade, not on many subsystem classes.
●​ Subsystem classes remain unchanged: you wrap them; you don’t rewrite
them.

2) Very simple Java code (video conversion


example style)
// Subsystem (complex classes)

class VideoDecoder {

String decode(String file) { return "raw(" + file + ")"; }

class FrameResizer {

String resize(String raw, int w, int h) { return raw + "->resized(" + w + "x" + h + ")";
}

class VideoCompressor {
String compress(String resized, String format) { return resized +
"->compressed(" + format + ")"; }

class AudioMixer {

String attachAudio(String video, String file) { return video + "+audio(" + file + ")";
}

// Facade (simple interface)

class VideoConverterFacade {

String convertToMp4(String file) {

VideoDecoder dec = new VideoDecoder();

String raw = [Link](file);

FrameResizer res = new FrameResizer();

String resized = [Link](raw, 1920, 1080);

VideoCompressor comp = new VideoCompressor();

String compressed = [Link](resized, "mp4");

AudioMixer mix = new AudioMixer();

return [Link](compressed, file);

}
// Client

public class Main {

public static void main(String[] args) {

String out = new VideoConverterFacade().convertToMp4("[Link]");

[Link](out);

This matches the lecture idea: client no longer orchestrates many steps; the
facade does it.

3) Advantages and disadvantages


Advantages

●​ Very easy for clients to use (one call hides many internal calls).
●​ Less duplication: many clients won’t re-implement the same low-level
sequence again and again.
●​ Loose coupling: high-level code becomes independent from low-level
implementation details.

Disadvantages

●​ Facade can become a “god class” if it keeps absorbing too many


responsibilities.
●​ If clients need lots of advanced features, they may still need to access
subsystem classes directly (so you must design facade boundaries well).
●​ Adds one more layer (small overhead in structure/maintenance).

4) Where to apply (best use cases)


Use Facade when:
●​ You want a simple entry point to a subsystem with many classes/methods.
●​ You want to separate What the client needs from How it’s done (e.g.,
convertToMp4() vs decoding/resizing/mixing).
●​ A module is “too complicated” and clients only need common workflows
(ATM-style interaction).
●​ Example from the exercise: “A report generation module talks to 10 different
services directly” → Facade.

Proxy Pattern (Structural)


Proxy = “provide a surrogate/placeholder for another object to control access to
it”.​
Your earlier list also phrases it as: a class representing the behavior of another
class.

1) Key characteristics (highly important) — 3–4


points
●​ Controls access to a real object (the real one might be remote, expensive, or
sensitive).
●​ Same interface: Proxy and RealObject typically implement the same
interface, so the client can use Proxy like the real thing.
●​ Lazy / conditional creation: proxy can delay creating the real object until
it’s truly needed (virtual proxy).
●​ Delegation: proxy may check something first
(permissions/caching/logging) and then delegate the call to the real object.

2) Very simple Java code (Virtual Proxy:


lazy-loading image)
// Common interface

interface Image {

void displayFull();
}

// Real object (expensive)

class RealImage implements Image {

private String file;

RealImage(String file) {

[Link] = file;

[Link]("Loading from disk: " + file); // expensive work

public void displayFull() {

[Link]("Showing FULL image: " + file);

// Proxy (placeholder)

class ImageProxy implements Image {

private String file;

private RealImage real; // null at first

ImageProxy(String file) { [Link] = file; }

public void displayFull() {


if (real == null) {

real = new RealImage(file); // load only when needed

[Link]();

public class Main {

public static void main(String[] args) {

Image img = new ImageProxy("photo_hd.png");

// real image not loaded yet

[Link](); // loads + shows

[Link](); // shows only (already loaded)

This matches your slide’s “load only when needed” idea (RealImage expensive,
Proxy holds it null initially).

3) Advantages and disadvantages


Advantages

●​ Performance improvement via lazy loading for heavy objects (startup


faster, less memory)
●​ Security/control: can block/allow requests before reaching the real object
●​ Supports remote access cleanly (remote proxy idea)
Disadvantages

●​ Extra layer / extra classes (more code + indirection).


●​ Can add small runtime overhead because every call passes through proxy.
●​ If overused, the flow becomes harder to trace (client → proxy → real).

4) Where to apply (best use cases)


Use Proxy when you need to control access because the real object is:

●​ Expensive to create → Virtual Proxy (lazy loading images/documents)


●​ In another address space / remote server → Remote Proxy (RPC stub idea)
●​ Sensitive and needs permission checks → Protection Proxy

Example from your exercise: “Loading HD profile pictures makes UI slow → Proxy
(virtual proxy)”.

Flyweight Pattern (Structural)


Flyweight is used to reduce the number of objects and save memory / improve
runtime by sharing common (repeated) data instead of storing it again and again.
In your lecture: it’s best when the system has lots of similar objects.

1) Key characteristics (highly important) — 3–4


points
●​ Object sharing (caching): reuse existing objects instead of creating new
ones repeatedly.
●​ Splits object state into:
○​ Intrinsic state (shared, common, stored inside flyweight)
○​ Extrinsic state (unique per usage, passed from outside at runtime)
●​ Uses a Flyweight Factory to manage cache and return shared objects (if
exists, reuse; else create then store).
●​ Works best when there are many objects with repeated data and you need
to minimize memory usage.
2) Very simple Java code (TreeType shared, many
Trees)
import [Link].*;

// Flyweight (intrinsic/shared state)

class TreeType {

String name; // shared

TreeType(String name) { [Link] = name; }

void draw(int x, int y) { // x,y are extrinsic

[Link]("Draw " + name + " at (" + x + "," + y + ")");

// Flyweight Factory (cache)

class TreeFactory {

private static Map<String, TreeType> cache = new HashMap<>();

public static TreeType getTreeType(String name) {

if (![Link](name)) {

[Link](name, new TreeType(name));

return [Link](name);
}

// Client objects (extrinsic/unique state stored outside)

class Tree {

int x, y; // extrinsic (unique)

TreeType type; // shared flyweight

Tree(int x, int y, TreeType type) {

this.x = x; this.y = y; [Link] = type;

void draw() { [Link](x, y); }

public class Main {

public static void main(String[] args) {

TreeType oakType = [Link]("Oak");

Tree t1 = new Tree(10, 20, oakType);

Tree t2 = new Tree(50, 70, oakType); // same shared type

[Link]();

[Link]();

}
}

Here, "Oak" is created once and reused. Only position (x,y) changes (extrinsic).

3) Advantages and disadvantages


Advantages

●​ Big memory saving when many objects share the same data (like repeated
characters/trees/icons)
●​ Faster performance in large-scale object creation because fewer objects
are allocated
●​ Encourages efficient structure by separating shared vs unique state

Disadvantages

●​ More complexity: you must clearly separate intrinsic vs extrinsic state


●​ If extrinsic state is large or frequently changing, benefit reduces.
●​ Debugging can be harder because many “logical objects” share the same
underlying instance.

4) Where to apply (best use cases)


Use Flyweight when:

●​ Your app creates a huge number of similar objects and memory is a


problem.
●​ Many objects share common properties (color, texture, font, model, icon)
and only a few attributes differ per object (position, ID, etc.).
●​ Typical examples:
○​ Text editor: characters share font/style objects
○​ Games/graphics: trees, bullets, particles, tiles
○​ Maps/UI: repeated icons/markers with same style but different
coordinates

Say next and I’ll continue with Filter (Criteria) or whichever one is next in your
lecture order.
Strategy Pattern (Behavioral)
Strategy lets you swap an algorithm at runtime.​
In your slides: the Context holds a strategy field, and delegates the work to the
current strategy object.

1) Key characteristics (highly important) — 3–4


points
●​ Multiple algorithms, same task: define a family of algorithms (strategies)
and choose one when needed.
●​ Runtime switching: you can change the strategy while the program is
running (like GPS route switching).
●​ Removes big if-else / switch based on type or mode; supports
Open–Closed (add new strategy class, don’t modify context).
●​ Composition-based: the Context has-a Strategy and delegates to it (not
inheritance).

2) Very simple Java code (same structure as your


slides)
// Strategy interface

interface RouteStrategy {

void buildRoute(String from, String to);

// Concrete strategies

class FastestRouteStrategy implements RouteStrategy {

public void buildRoute(String from, String to) {

[Link]("Fastest route from " + from + " to " + to);


}

class EcoRouteStrategy implements RouteStrategy {

public void buildRoute(String from, String to) {

[Link]("Eco route from " + from + " to " + to);

// Context

class Navigator {

private RouteStrategy strategy;

public void setStrategy(RouteStrategy s) {

[Link] = s;

public void navigate(String from, String to) {

[Link](from, to);

// Usage

public class Main {


public static void main(String[] args) {

Navigator nav = new Navigator();

[Link](new FastestRouteStrategy());

[Link]("Dhanmondi", "Airport"); // from your slide


example:contentReference[oaicite:6]{index=6}

[Link](new EcoRouteStrategy()); // switch at runtime

[Link]("Dhanmondi", "Airport");

3) Advantages and disadvantages


Advantages

●​ Easy to add new algorithms: add a new class (e.g., AvoidTollsStrategy)


without touching Navigator.
●​ Cleaner and testable: each algorithm can be tested separately; context
stays simple.
●​ No if-else ladder in the context; follows Open–Closed better.

Disadvantages

●​ More classes (one class per strategy).


●​ Client/config must choose the correct strategy (wrong choice = wrong
behavior).
●​ If strategies need lots of shared data, you must design how to pass it
cleanly.
4) Where to apply (best use cases)
Use Strategy when:

●​ You have multiple ways to do the same job and want to switch based on
user preference/context (GPS fastest vs eco).
●​ Your code has a big if-else/switch deciding “which algorithm to run”
(compression type, payment type, sorting type).
●​ You want to plug in new algorithms later without rewriting the main flow.

Examples from Lecture 19:

●​ Compression: Zip vs Rar vs Gzip strategies instead of if-else chain


●​ Payment: Card vs PayPal vs MobileWallet/bKash as different strategies

Template Method Pattern (Behavioral)


Template Method: define the skeleton (fixed structure) of an algorithm in a base
class, and let subclasses override specific steps without changing the overall
flow.

1) Key characteristics (highly important) — 3–4


points
●​ Fixed algorithm structure lives in the base class as a final template
method (the “recipe”).
●​ Some steps are common/shared (implemented in base class), while others
are variable (declared abstract / overridden by subclasses).
●​ Inheritance-based: you customize behavior by subclassing and overriding
only the required steps.
●​ Best when all variants follow the same overall sequence, but differ in a few
steps.

2) Very simple Java code (Beverage example)


abstract class Beverage {
// Template method: fixed algorithm structure

public final void prepareRecipe() {

boilWater();

brew();

pourInCup();

addCondiments();

// common steps

void boilWater() { [Link]("Boiling water"); }

void pourInCup() { [Link]("Pouring into cup"); }

// variable steps

abstract void brew();

abstract void addCondiments();

class Tea extends Beverage {

void brew() { [Link]("Steeping the tea"); }

void addCondiments() { [Link]("Adding lemon"); }

class Main {

public static void main(String[] args) {


Beverage b = new Tea();

[Link]();

This is the same structure as your slide: prepareRecipe() is the template; brew()
and addCondiments() vary by subclass.

3) Advantages and disadvantages


Advantages

●​ Reuses common code (shared steps in base class) and avoids duplication.
●​ Keeps the workflow consistent (order of steps stays fixed because
template is final).
●​ Easy to add a new variant: just create a subclass and override the variable
steps.

Disadvantages

●​ Inheritance coupling: subclasses are tied to the base class design (less
flexible than composition).
●​ If you need to change the overall algorithm skeleton, you may affect many
subclasses.
●​ More subclasses can appear if you have many variants.

4) Where to apply (best use cases)


Use Template Method when you have fixed high-level steps, but different
implementations for some steps, like:

●​ Data mining pipeline: Open → Parse → Analyze → Close; only parsing


differs (PDF vs CSV).
●​ Beverage maker: Boil → Brew → Pour → Add condiments; tea vs coffee
differ in brew/condiments.
●​ House building: foundation/windows fixed; walls/roof vary by house type
(wood vs glass).

Also: whenever your variants share the same structure, Template Method is
better than Strategy; Strategy is for swapping whole algorithms (composition),
Template is for overriding steps (inheritance).

Chain of Responsibility (Behavioral)


1) Key characteristics (highly important) — 3–4 points

●​ Pass a request along a chain of handlers until one handles it (or the chain
ends).
●​ Decouples sender from receiver: the client sends the request once; it
doesn’t need to know who will handle it.
●​ Each handler has a next reference and either handles or forwards to
[Link](request).
●​ Flexible ordering: you can reorder/insert new checks easily without
rewriting a big if-else block.

2) Very simple Java code

(Exactly the slide-style “ordered checks” example)

class Request {

boolean authenticated, admin, valid;

Request(boolean a, boolean ad, boolean v){ authenticated=a; admin=ad; valid=v;


}

boolean isAuthenticated(){ return authenticated; }

boolean hasAdminRole(){ return admin; }

boolean isValid(){ return valid; }

}
abstract class Handler {

protected Handler next;

public Handler setNext(Handler n){ [Link] = n; return n; }

public final void handle(Request r){

if(!check(r)) return; // stop chain if fails

if(next != null) [Link](r);

protected abstract boolean check(Request r);

class AuthCheck extends Handler {

protected boolean check(Request r){

if(![Link]()){ [Link]("Denied: not logged in"); return


false; }

return true;

class RoleCheck extends Handler {

protected boolean check(Request r){

if(![Link]()){ [Link]("Denied: not admin"); return false; }

return true;

}
}

class ValidationCheck extends Handler {

protected boolean check(Request r){

if(![Link]()){ [Link]("Denied: invalid data"); return false; }

return true;

public class Main {

public static void main(String[] args) {

Handler chain = new AuthCheck()

.setNext(new RoleCheck())

.setNext(new ValidationCheck()); // Auth -> Role ->


Validation:contentReference[oaicite:6]{index=6}

[Link](new Request(true, true, true));

This matches the lecture’s implementation idea: each handler does one check,
then forwards to the next.

3) Advantages and disadvantages

Advantages
●​ Removes big if-else chains; each check is isolated and reusable.
●​ Order is configurable (swap handler order easily).
●​ Open–Closed friendly: add new checks by adding a new handler class, not
by editing existing ones.

Disadvantages

●​ No guarantee a request gets handled (if chain ends without a handler that
can process it).
●​ Debugging can be harder because the request flows through multiple
objects.
●​ If the chain becomes too long, it can add overhead and reduce clarity.

4) Where to apply (best use cases)

Use Chain of Responsibility when you have multiple ordered checks/steps and
want them modular:

●​ Web request pipeline: AuthCheck → RoleCheck → Validation → Controller


(exact slide scenario).
●​ Comment moderation pipeline: SpamFilter → ProfanityFilter →
LengthFilter → Persist (exact slide scenario).
●​ Customer support escalation: bot → human agent → manager (“try A, if
not, try B, then C”).

Mediator Pattern (Behavioral)


1) Key characteristics (highly important) — 3–4 points

●​ Centralizes communication: instead of objects talking to each other


directly (messy web), they communicate through a Mediator.
●​ Reduces communication complexity by removing many-to-many direct
references between objects.
●​ Colleagues notify the mediator: colleagues call something like
[Link](this, event) and the mediator coordinates what
happens next.
●​ Interaction logic lives in one place (the mediator), so UI/widget
interactions become easier to change.
2) Very simple Java code (GUI Form Validation style from your
slides)

// Mediator

interface Mediator {

void notify(Component sender, String event);

// Colleague base

abstract class Component {

protected Mediator dialog;

Component(Mediator dialog) { [Link] = dialog; }

// Concrete colleagues

class TextField extends Component {

private String text = "";

TextField(Mediator dialog) { super(dialog); }

void type(String t) {

text = t;

[Link](this, "typing");

boolean isEmpty() { return [Link](); }

}
class Button extends Component {

private boolean enabled = false;

Button(Mediator dialog) { super(dialog); }

void setEnabled(boolean v) {

enabled = v;

[Link]("Login button enabled? " + enabled);

// Concrete Mediator

class LoginDialog implements Mediator {

TextField user, pass;

Button loginBtn;

LoginDialog() {

user = new TextField(this);

pass = new TextField(this);

loginBtn = new Button(this);

public void notify(Component sender, String event) {

if ("typing".equals(event)) {
boolean ready = ![Link]() && ![Link]();

[Link](ready);

// Usage

public class Main {

public static void main(String[] args) {

LoginDialog dialog = new LoginDialog();

[Link]("ashik");

[Link]("1234");

This matches the lecture idea: notify(sender, event) and enabling button only
when fields are ready.

3) Advantages and disadvantages

Advantages

●​ Less coupling: colleagues don’t need references to each other; they only
know the mediator.
●​ Easier to change interactions: update rules in mediator instead of editing
many components.
●​ Cleaner structure than “messy web” dependencies between UI
components.

Disadvantages
●​ Mediator can become too large/complex (a “god object”) if you keep
adding rules there.
●​ Debugging sometimes shifts from “who called who?” to “what rule inside
mediator fired?”
●​ Adds an extra layer (more classes: mediator + colleagues).

4) Where to apply (best use cases)

Use Mediator when many objects must coordinate their actions, and direct
communication is getting messy, such as:

●​ Chat Room: users send messages to a “Room” (mediator) which broadcasts


to others.
●​ GUI dialog / form validation: checkbox, textfields, submit button
interactions (enable/disable logic) live in the dialog mediator.
●​ Any system where you currently have “Button talks to TextField, TextField
talks to Checkbox…” style dependencies (messy web).

Observer Pattern (Behavioral)


1) Key characteristics (highly important) — 3–4 points

●​ One-to-many dependency: when one object (Subject) changes state, all


dependent objects (Observers) are notified automatically.
●​ Loose coupling: Subject doesn’t know concrete observer classes—only an
Observer interface.
●​ Subscription model: observers can attach/subscribe and
detach/unsubscribe at runtime.
●​ Event-driven updates: change happens in Subject → notification triggers
observers’ update.

2) Very simple Java code

import [Link].*;

// Observer
interface Observer {

void update(int temp);

// Subject

class WeatherStation {

private List<Observer> observers = new ArrayList<>();

private int temperature;

public void addObserver(Observer o) { [Link](o); }

public void removeObserver(Observer o) { [Link](o); }

public void setTemperature(int temp) {

[Link] = temp;

notifyObservers();

private void notifyObservers() {

for (Observer o : observers) [Link](temperature);

// Concrete Observer

class PhoneDisplay implements Observer {


public void update(int temp) {

[Link]("PhoneDisplay: temp = " + temp);

public class Main {

public static void main(String[] args) {

WeatherStation station = new WeatherStation();

[Link](new PhoneDisplay());

[Link](30);

[Link](32);

3) Advantages and disadvantages

Advantages

●​ Automatic update of many dependents (no manual calling each one).


●​ Loose coupling: you can add/remove observers without changing subject
code.
●​ Good for event-driven design (UI events, notifications, pub-sub).

Disadvantages

●​ If many observers exist, notification can be costly.


●​ Update order can be unpredictable (unless you manage it).
●​ Risk of memory leaks if observers are not removed when no longer
needed.
4) Where to apply (best use cases)

Use Observer when:

●​ Many parts of the system must react to a change in one object.


●​ You want a publish–subscribe style flow.

Examples:

●​ GUI: button click → multiple listeners respond.


●​ Stock price / weather updates: many displays update when data changes.
●​ Notification system: when a post is published, all followers get notified.
●​ Model–View updates: model changes → views refresh.

Command Pattern (Behavioral)


Command = encapsulate a request/action as an object, so it can be queued,
logged, passed around, or undone later.

1) Key characteristics (highly important) — 3–4


points
●​ Turns an action into an object (a “command object”), instead of calling
methods directly.
●​ Clear roles: Command (execute), ConcreteCommand (stores Receiver), Invoker
(calls execute), Receiver (does real work).
●​ Invoker is decoupled from the real action: invoker knows “call
execute()”, but not the details of how the work is done.
●​ Supports history features like undo/redo, queues, logs (core motivation
in slides).

2) Very simple Java code (Light + Remote)


// Command
interface Command {

void execute();

// Receiver

class Light {

void on() { [Link]("Light ON"); }

void off() { [Link]("Light OFF"); }

// Concrete Commands

class LightOnCommand implements Command {

private Light light;

LightOnCommand(Light light) { [Link] = light; }

public void execute() { [Link](); }

class LightOffCommand implements Command {

private Light light;

LightOffCommand(Light light) { [Link] = light; }

public void execute() { [Link](); }

// Invoker
class RemoteControl {

private Command button;

void setCommand(Command c) { button = c; }

void pressButton() { [Link](); }

// Client

public class Main {

public static void main(String[] args) {

Light light = new Light();

RemoteControl remote = new RemoteControl();

[Link](new LightOnCommand(light));

[Link]();

[Link](new LightOffCommand(light));

[Link]();

This is the exact slide-style structure: [Link](), Light as receiver, and


RemoteControl as invoker calling execute().

3) Advantages and disadvantages


Advantages

●​ Undo/Redo support by keeping a history (stack) of executed commands.


●​ Queue / background jobs: commands can be stored and executed later
(job scheduler idea).
●​ Loose coupling: invoker doesn’t depend on receiver details (only calls
execute()).

Disadvantages

●​ More classes (one class per action/command).


●​ Can feel over-engineered for tiny apps where direct method calls are
enough.
●​ If you add undo, you must carefully store state/parameters (more
complexity).

4) Where to apply (best use cases)


Use Command when you need any of these:

●​ Undo/Redo (text editor operations: insert/delete/format as commands)


●​ Queueing / retry / background execution (web app jobs:
SendEmailCommand, GenerateReportCommand)
●​ Logging / auditing actions (store the command + parameters as a record)
●​ UI actions like buttons/menus/remote control where “press” triggers a
command (smart remote idea)

State Pattern (Behavioral)


State pattern lets an object change its behavior when its internal state changes
(so it looks like the object “changed its class”)—your slide summary: “to handle
different states”.

1) Key characteristics (highly important) — 3–4


points
●​ Encapsulates state-specific behavior into separate State classes (each
state has its own logic).
●​ Context holds a current State and delegates actions to it (no big if-else
on state).
●​ State transitions happen cleanly: state can switch the context to another
state.
●​ Helps when the same actions behave differently depending on state (e.g.,
play() behaves differently when Paused vs Playing).

2) Very simple Java code (Traffic Light)


// State

interface LightState {

void next(TrafficLight light);

String color();

// Concrete States

class Red implements LightState {

public void next(TrafficLight light) { [Link](new Green()); }

public String color() { return "RED"; }

class Green implements LightState {

public void next(TrafficLight light) { [Link](new Yellow()); }

public String color() { return "GREEN"; }

}
class Yellow implements LightState {

public void next(TrafficLight light) { [Link](new Red()); }

public String color() { return "YELLOW"; }

// Context

class TrafficLight {

private LightState state = new Red();

public void setState(LightState state) { [Link] = state; }

public void next() { [Link](this); }

public void show() { [Link]("Light: " + [Link]()); }

// Client

public class Main {

public static void main(String[] args) {

TrafficLight light = new TrafficLight();

[Link](); // RED

[Link]();

[Link](); // GREEN

[Link]();

[Link](); // YELLOW
}

3) Advantages and disadvantages


Advantages

●​ Removes huge if-else / switch based on state (cleaner code).


●​ Easy to add new states: add a new state class without breaking others.
●​ State behavior is organized: each state class is focused and readable.

Disadvantages

●​ More classes (one per state).


●​ State transitions can become confusing if many states exist (need clear
transition rules).
●​ Sometimes a simple enum + switch is enough for tiny problems (State may
be overkill).

4) Where to apply (best use cases)


Use State when:

●​ An object has clearly defined states and behavior changes based on the
state.
●​ You keep writing code like:​
if(state == X) doA(); else if(state == Y) doB(); ... (State pattern
fits here)

Examples:

●​ ATM machine: NoCard → HasCard → Authenticated → Dispensing


●​ Media player: Playing / Paused / Stopped (same button does different
actions)
●​ Order processing: New → Paid → Shipped → Delivered → Cancelled
●​ Traffic light / vending machine / workflow systems
Memento Pattern (Behavioral)
1) Key characteristics (highly important) — 3–4 points

●​ Snapshot/restore: Captures and externalizes an object’s internal state so


it can be restored later, without violating encapsulation.
●​ 3 roles:
○​ Originator = the object whose state you save/restore
○​ Memento = the snapshot object (opaque)
○​ Caretaker = stores snapshots but must not inspect/modify internal
data.
●​ History stack idea: Caretaker often keeps a stack of mementos to
support “undo/backtrack” (restore previous snapshot).
●​ State is stored safely: Outside code stores a memento, but doesn’t
“peek” into private internals.

2) Very simple Java code

import [Link];

// Memento (Snapshot)

class Memento {

private final String state;

public Memento(String s) { [Link] = s; }

public String getState() { return state; }

// Originator

class TextEditor {

private String text = "";


public void write(String t) { text = t; }

public Memento save() { return new Memento(text); }

public void restore(Memento m) { text = [Link](); }

public String getText() { return text; }

// Caretaker (History)

class History {

private Stack<Memento> stack = new Stack<>();

public void push(Memento m) { [Link](m); }

public Memento pop() { return [Link]() ? null : [Link](); }

public class Main {

public static void main(String[] args) {

TextEditor ed = new TextEditor();

History hist = new History();

[Link]("Version 1");

[Link]([Link]());

[Link]("Version 2");

[Link]([Link]()); // back to Version 1


[Link]([Link]());

(Structure matches the lecture’s Memento, [Link]()/restore(), and


History stack idea.)

3) Advantages and disadvantages

Advantages

●​ Enables undo/redo, backtracking, checkpoints by restoring old


snapshots.
●​ Protects encapsulation: caretaker stores snapshots without looking into
object internals.
●​ Works nicely with Command pattern for undoable actions (store
memento before execute).

Disadvantages

●​ Memory cost: storing many snapshots can be heavy (especially big


objects).
●​ You must decide how much state to save (full snapshot vs minimal diff).
●​ Managing history (stack size, when to save) adds extra logic.

4) Where to apply (best use cases)

●​ Game saves / checkpoints: save file = memento, UI/menu = caretaker,


game engine = originator.
●​ Photoshop/history panel / editor undo stack: each step saves a
snapshot; history manages snapshots.
●​ Text editor undo/redo: commonly Command + Memento (command
stores a memento before executing; undo restores it).
Visitor Pattern (Behavioral)
Visitor is a behavioral pattern that separates algorithms (operations) from the
objects they operate on, so you can add new operations without changing the
element/node classes.

1) Key characteristics (highly important) — 3–4


points
●​ Add new operations without modifying classes: write a new Visitor to
add a new operation; element classes stay unchanged.
●​ Double-dispatch style: elements expose accept(visitor) and inside it
call [Link](this).
●​ Best when structure is stable but operations change often (Composite =
structure; Visitor = operations).
●​ Avoids the “interface explosion” problem where you keep adding
methods like exportJSON(), scanVirus(), countFiles() to every node
class.

2) Very simple Java code (Document export


example)
// Element

interface Element {

void accept(Visitor v);

class TextBlock implements Element {

public void accept(Visitor v) { [Link](this); }

}
class ImageBlock implements Element {

public void accept(Visitor v) { [Link](this); }

// Visitor

interface Visitor {

void visitText(TextBlock t);

void visitImage(ImageBlock i);

// Concrete Visitor (new operation: export to PDF)

class PdfExporter implements Visitor {

public void visitText(TextBlock t) {

[Link]("Rendering text...");

public void visitImage(ImageBlock i) {

[Link]("Rendering image...");

public class Main {

public static void main(String[] args) {

Element[] doc = { new TextBlock(), new ImageBlock() };


Visitor exporter = new PdfExporter();

for (Element e : doc) [Link](exporter);

This matches the lecture structure: accept(visitor) then


[Link](this).

3) Advantages and disadvantages


Advantages

●​ Easy to add new operations: just add a new visitor class (no need to
touch element classes).
●​ Keeps element classes clean (they don’t keep growing with new
operations).
●​ Great for traversing trees like file systems / AST where you run different
operations (size, print, export, etc.).

Disadvantages

●​ If you add a new element type, you must update the Visitor interface
and all concrete visitors (because each visitor needs visitNewType(...)).
(This is the tradeoff of Visitor.)
●​ Adds extra classes (visitors), and the pattern can feel heavy for small
projects.

4) Where to apply (best use cases)


Use Visitor when:
●​ You have a complex object structure (tree like file system / AST /
document blocks) and you keep needing new operations on it, but don’t
want to edit every node class each time.
●​ Structure is stable, operations change frequently (exact rule from
slides).
●​ Real examples from your lectures:
○​ Document export: Word-like doc elements (TextBlock, Image,
Table) + PdfExporter visitor
○​ Insurance/Tax inspector visiting different buildings: same
structure, different visiting logic per building type
○​ AST visitors: EvalVisitor (compute value) and PrintVisitor
(pretty print) over the same expression tree

Interpreter Pattern (Behavioral)


1) Key characteristics (highly important) — 3–4 points

●​ Works for a “mini language”: you define a small grammar, then


represent each grammar rule as a class.
●​ You build an expression tree (AST) from the sentence, then call
interpret() on the root to evaluate it.
●​ Common structure: Expression interface + Terminal expressions +
Non-terminal expressions (like Add combining two expressions).
●​ Best when the language is simple and stable, and you want a clean
object-based way to evaluate it. (Grammar → classes mapping)

2) Very simple Java code

// Expression

interface Expression {

int interpret();

// Terminal Expression
class Number implements Expression {

private int n;

public Number(int n) { this.n = n; }

public int interpret() { return n; }

// Non-terminal Expression

class Add implements Expression {

private Expression left, right;

public Add(Expression l, Expression r) { left = l; right = r; }

public int interpret() { return [Link]() + [Link](); }

public class Main {

public static void main(String[] args) {

Expression exp = new Add(new Number(5), new Number(10));

[Link]([Link]()); // 15

(Exactly the basic logic shown in your lecture.)

3) Advantages and disadvantages

Advantages
●​ Very clear mapping: grammar rule → class (easy to extend for small
additions like Sub, Mul).
●​ Clean evaluation: build a tree once, then just call interpret() on the
root.

Disadvantages

●​ If the language grows large, you end up with many classes (one per
rule/operator).
●​ Parsing a real language can be complex; Interpreter is best for small
grammars.

4) Where to apply (best use cases)

●​ SQL query parsing inside a database engine (parse tree of expressions).


●​ Calculator / expression evaluation (including Reverse Polish Notation
idea).
●​ Email filters / search queries like (from:"boss") AND (subject contains
"urgent") represented as a tree and interpreted on an email object.

Iterator Pattern (Behavioral)


1) Key characteristics (highly important) — 3–4 points

●​ Sequential access: provides a way to traverse elements one by one without


exposing the internal structure (array/list/tree etc.).
●​ Common iterator interface: standard operations like hasNext() and
next() (client code stays same).
●​ Decouples traversal from collection: collection stores data; iterator
handles traversal logic.
●​ Supports multiple traversals: you can have different iterators (forward,
reverse, filtered) without changing the collection.

2) Very simple Java code

import [Link].*;
// A simple custom collection

class NameCollection {

private String[] names = {"Ashik", "Rahman", "Amigo"};

public Iterator<String> iterator() {

return new NameIterator();

// Iterator implementation

private class NameIterator implements Iterator<String> {

private int index = 0;

public boolean hasNext() {

return index < [Link];

public String next() {

return names[index++];

// Client
public class Main {

public static void main(String[] args) {

NameCollection c = new NameCollection();

Iterator<String> it = [Link]();

while ([Link]()) {

[Link]([Link]());

3) Advantages and disadvantages

Advantages

●​ Hides internal representation of the collection (client doesn’t care if it’s


array/list/tree).
●​ Cleaner traversal code and consistent style (hasNext/next) across
collections.
●​ Easy to add new traversal types (reverse, skip, filtered) by adding new
iterator classes.

Disadvantages

●​ Adds extra classes for custom iterators (though Java’s built-in collections
already solve this).
●​ For very simple collections, iterator can feel like extra boilerplate.

4) Where to apply (best use cases)


●​ When you want to traverse a collection without exposing internal
structure (classic reason).
●​ When you need multiple traversal strategies (forward, reverse, filtered).
●​ Real examples:
○​ Iterating over custom data structures (trees, graphs, special lists).
○​ UI lists/menus: same iteration logic, different underlying storage.

Tier 1 — focus these first (very likely)

1.​ Singleton (most common creational)​


Lecture 16
2.​ Factory​
Lecture 16
3.​ Abstract Factory​
Lecture 16
4.​ Builder​
Lecture 16
5.​ Adapter​
Lecture 16
6.​ Decorator​
Lecture 16
7.​ Composite​
Lecture 16
8.​ Strategy​
Lecture 16
9.​ Template Method​
Lecture 16
10.​Observer​
Lecture 16
11.​Command​
Lecture 16
12.​State​
Lecture 16

Tier 2 — important, but usually after Tier 1

13.​Facade​
Lecture 16
14.​Proxy​
Lecture 16
15.​Chain of Responsibility​
Lecture 16
16.​Iterator​
Lecture 16
17.​Flyweight​
Lecture 16
18.​Prototype​
Lecture 16

Tier 3 — often asked less, but can appear as theory/short notes

19.​Mediator​
Lecture 16
20.​Memento​
Lecture 16
21.​Visitor​
Lecture 16
22.​Interpreter​
Lecture 16
23.​Bridge​
Lecture 16

You might also like