W8_L5: Design Patterns β Structural#
| Video | W8_L5: Design Patterns - Structural |
| Channel | IIT Madras β B.S. Degree Programme (Software Engineering) |
| Duration | 16:12 |
| Covers | Facade pattern, Adapter pattern |
0. Where structural patterns sit β 00:27#
The previous lecture covered creational patterns, which "provide object creation mechanisms β they help us create objects in an efficient manner."
This lecture covers structural patterns, which:
help us assemble objects and classes into larger structures, and help us keep these structures flexible and efficient.

| Type | Concern |
|---|---|
| Creational | Used during the process of object creation |
| Structural β this lecture | Composition of classes or objects |
| Behavioural | How classes/objects interact and distribute responsibility |
Two structural patterns are taught here:
- Facade β 01:12
- Adapter β 09:03
1. Facade Design Pattern#
1.1 The problem β 01:22#

You have a task that requires you to:
- Create several objects + perform several steps β initialize many objects, execute methods in the correct order, pass the right parameters
- Use existing complicated library functions
- β¦and as a result, others reusing your code must know details about which objects to create and which library functions to call
That last point is the real cost: the complexity leaks out to the client, and using your code becomes difficult.
1.2 Concrete example β the seller portal β 02:26#
After a buyer places an order, the system must do four things:

- Create order
- Notify seller
- Prepare packaging
- Send out for delivery
1.3 The naive implementation ("before") β 03:03#
Four independent classes, each doing one job:

class Order {
public void placeOrder(String product){
System.out.println("Order placed for Product " + product);
}
}
class Seller {
public void sellerNotification(String sellerName){
System.out.println("Notified Seller " + sellerName);
}
}
class Package {
public void packageOrder(String packagingType){
System.out.println("Order packed with " + packagingType);
}
}
class Delivery {
public void deliver(String buyerName, String address) {
System.out.println("Sent package to delivery to " + buyerName
+ " with address " + address);
}
}
And the client's main has to do all of this by hand, in the right order:

public class FacadeBefore {
public static void main(String[] args) {
Order o = new Order();
o.placeOrder("Lenovo Laptop");
Seller s = new Seller();
s.sellerNotification("Seller 1");
Package p = new Package();
p.packageOrder("bubble wrap");
Delivery d = new Delivery();
d.deliver("abc", "xyz");
}
}
Reflection question posed at 04:15: how can I make this better for the client? The client "should necessarily not need to know the exact steps which are required."
1.4 The pattern β 04:47#
Facade design pattern: provides a simple interface to a library, or a complex set of classes.

You create one class with one method that internally calls all the required functions across all the required classes. Clients call only that method.
doSomething() {
Class1 c1 = new Class1();
Class2 c2 = new Class2();
Class3 c3 = new Class3();
c1.doStuff(c2)
c3.setX(c1.getX());
return c3.getY();
}
Client1 and Client2 both call FaΓ§ade.doSomething(). Neither knows that package1.Class1, package2.Class2, package3.Class3 exist.
1.5 The refactored code ("after") β 06:00#

The individual classes do not change. Order, Seller, Package, Delivery stay exactly as they were. You only add a facade class:
class DeliveryFacade {
public void deliver(String product, String seller, String packagingType,
String buyerName, String address) {
Order o = new Order();
o.placeOrder(product);
Seller s = new Seller();
s.sellerNotification(seller);
Package p = new Package();
p.packageOrder(packagingType);
Delivery d = new Delivery();
d.deliver(buyerName, address);
}
}
public class FacadeAfter {
public static void main(String[] args) {
DeliveryFacade d = new DeliveryFacade();
d.deliver("Lenovo Laptop", "Seller 1", /* packaging, buyer, address */ ...);
}
}
The client now creates one object and calls one function. It doesn't remember which objects to create, nor the exact order of calls.
Before vs After#
| Before | After | |
|---|---|---|
| Objects client creates | 4 (Order, Seller, Package, Delivery) |
1 (DeliveryFacade) |
| Method calls client makes | 4, in a specific order | 1 (deliver(...)) |
| Client must know call order? | Yes | No |
| Worker classes changed? | β | No β facade is purely additive |
1.6 Pros and cons β 07:46#

| Pros | Cons |
|---|---|
| Isolates code from other libraries'/classes' complexity β clients are exposed to just a single method | Tightly coupled to other objects, so maintenance becomes more difficult |
The coupling cost, shown concretely β DeliveryFacade references Order, Seller, Package and Delivery. If any one of those classes or its methods changes, DeliveryFacade must change too:

2. Adapter Design Pattern#
2.1 The problem: incompatible interfaces β 09:13#

On the seller portal, some products have cost in dollars or euros, so conversion to rupees is needed.
- There is an existing
ConversionCalculatorclass/library β you do not have control over it. - The
Productclass cannot directly access it: its interface is incompatible.
βββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββββββ
β Product β β ConversionCalculator β
βββββββββββββββββββββββββββββββ€ ββββββββββββββββββββββββββββββββββββ€
β - productId: String β β β
β - productName: String β βββ β βββ β + dollarsToRupee(money): double β
β - productType: String β β + eurosToRupee(money): double β
β - productCost: double β β β
βββββββββββββββββββββββββββββββ€ ββββββββββββββββββββββββββββββββββββ
β + addProduct(id,name,type, β
β cost) β
β + getProductCost(): double β
βββββββββββββββββββββββββββββββ
2.2 The pattern β 10:24#

The client wants to call methodB() but can't reach it. So insert an Adapter that acts as a wrapper / link between client and adaptee:
| Role | What it does |
|---|---|
| Client | Calls adaptor.methodA() β the only thing it knows |
| Adaptor | Holds a reference adaptee: Adaptee; its methodA() calls adaptee.methodB() |
| Adaptee | The existing, uncontrollable class exposing methodB() |
The two methods might be incompatible with each other, but that issue of incompatibility is taken care of by the adapter. The adapter wraps one of the objects to hide the complexity of what is happening behind the scenes.
2.3 Implementation β 12:00#
The adaptee β an existing library class with the actual conversion rates:

class ConversionCalculator {
public double dollarsToRupee(double money) {
return (money * 75);
}
public double eurosToRupees(double money) {
return (money * 80);
}
}
The adapter β bridges Product and ConversionCalculator. It takes a Product p, creates a ConversionCalculator, and passes the product's cost into the right function:

class CostCalculatorAdapter {
public double getProductCost(Product p) {
ConversionCalculator calc = new ConversionCalculator();
return calc.dollarsToRupee(p.getProductCost());
}
}
The client β Product.addProduct() creates an adapter object and calls the adapter's function. Product needs to know nothing about ConversionCalculator:
public void addProduct(String productId, String name, String type, double cost){
this.productId = productId;
this.productName = name;
this.productType = type;
this.productCost = cost;
CostCalculatorAdapter adapter = new CostCalculatorAdapter();
this.productCost = adapter.getProductCost(this);
}
2.4 Running it β 14:09#

public class Adapter {
public static void main(String[] args) {
//Product from US
Product p = new Product();
p.addProduct("1", "Product 1", "Book", 300.00);
System.out.println("Product cost in Rupees== " + p.getProductCost());
}
}
Product cost in Rupees== 22500.0
The cost was 300 (dollars); addProduct created the adapter, the adapter called dollarsToRupee, and 300 Γ 75 = 22500 rupees came back.
2.5 Pros and cons β 14:23#

| Pros | Cons |
|---|---|
Single Responsibility Principle β data-conversion code is separated from the primary business logic (Product's job is add/edit/delete products, not currency maths) |
Overall code complexity increases β a new class plus new methods for every adapter |
| Open/Closed Principle β new currencies just mean new adapter types; the class is open for extension | If you could modify Product, or did have access to ConversionCalculator, you wouldn't need a separate class at all |
3. Facade vs Adapter β the easy confusion#
Both wrap other classes and both hide complexity from a client. The difference is why:
| Facade | Adapter | |
|---|---|---|
| Problem it solves | Too many steps / objects for the client to manage | Interfaces are incompatible β client literally cannot call the target |
| What it hides | The number and order of calls | The mismatch in interfaces |
| Wraps | A whole subsystem (several classes) | Typically one object (the adaptee) |
| Interface it exposes | A brand-new, simplified one | One that the existing client already expects |
| Would work without it? | Yes β just verbosely | No β the call is impossible |
| Example here | DeliveryFacade.deliver(...) replacing 4 objects + 4 calls |
CostCalculatorAdapter letting Product reach ConversionCalculator |
| Main cost | Tight coupling to the subsystem | Extra class β more complexity |
4. Cheat sheet#
Structural patterns = composition of classes/objects into larger structures, kept flexible and efficient.
FACADE ADAPTER
Client βββΊ Facade.doSomething() Client βββΊ Adaptor.methodA()
β β
βββΊ Class1.m() βββΊ Adaptee.methodB()
βββΊ Class2.m()
βββΊ Class3.m() (incompatible interface bridged)
(many steps collapsed into one)
| Pattern | One-line definition | Trigger to use it |
|---|---|---|
| Facade | Provides a simple interface to a library or a complex set of classes | Client must create many objects and call many methods in a fixed order |
| Adapter | Provides a wrapper/link between a client and an incompatible adaptee | You need a class you can't modify, and its interface doesn't match |
| Term | Meaning |
|---|---|
| Adaptee | The existing class you can't change (ConversionCalculator) |
| Adaptor | The bridge class (CostCalculatorAdapter) |
| Facade | The single-entry-point class (DeliveryFacade) |
Principles referenced:
- Single Responsibility Principle β a class should do one job (adapter keeps conversion out of
Product) - Open/Closed Principle β open for extension; add new adapters without touching existing classes
5. File manifest#
w8-l5-design-patterns-structural/
βββ NOTES.md # this file
βββ shots/ # 15 verified screenshots
βββ transcript_ts.txt # timestamped transcript
βββ video.en.vtt # raw subtitles
βββ video.mkv # source video (720p)
Commands used:
yt-dlp --skip-download --print "%(title)s | %(duration_string)s | %(uploader)s" URL
yt-dlp --skip-download --write-auto-subs --write-subs \
--sub-lang en --sub-format vtt -o "video.%(ext)s" URL
python3 ~/.claude/skills/youtube-notes/scripts/vtt2txt.py video.en.vtt > transcript_ts.txt
yt-dlp -f "bestvideo[height<=720]+bestaudio/best[height<=720]" -o "video.%(ext)s" URL
~/.claude/skills/youtube-notes/scripts/grab_frames.sh video.mkv shots <<'EOF'
00:00:54|01_types_of_patterns
...
EOF