C#
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C#
-
Section 4: Working with Data
-
Section 5: Error Handling
-
Section 6: Delegates and Events
-
Section 7: Async Programming
-
Section 8: More Language Features
-
Section 9: File I/O and Serialization
-
Section 10: Networking in .NET
-
Section 11: The .NET Ecosystem
-
Section 12: Memory and Performance
-
Section 13: Concurrency Beyond Async
-
Section 14: Reflection and Attributes
-
Section 15: Testing and Best Practices
-
Section 16: Design Patterns in C#
-
Section 17: Standard Library Deep Dive
-
Section 18: Data Structures and Algorithms in C#
-
Section 19: GUI and Desktop Development Overview
-
Section 20: Practical Projects
-
Section 21: More Practice Exercises
-
Section 22: More Standard Library and Text Processing
-
Section 23: More Design Patterns
-
Section 24: More Projects
-
Section 25: Interview Practice
-
Section 26: C# Keywords Reference (Modifiers)
-
Section 27: C# Keywords Reference (Statements)
-
Section 28: C# Keywords Reference (Operators)
-
Section 29: BCL: System.Collections.Generic
-
Section 30: BCL: System.Linq
-
Section 31: BCL: System.Threading
-
Section 32: BCL: System.IO
-
Section 33: BCL: System.Text and System.Text.Json
-
Section 34: BCL: System.Net.Http
-
Section 35: C# Language Specification Topics
-
Section 36: More Practice Exercises
-
Section 37: More Async Patterns
-
Section 38: More BCL: System.Reflection and System.Diagnostics
-
Section 39: Nullable Reference Types In Depth
-
Section 40: C# Records and Pattern Matching In Depth
-
Section 41: Dependency Injection Deep Dive
-
Section 42: More Interview and Whiteboard Practice
148: The Facade Pattern
A few years ago, I was onboarding a junior developer to a project that used a massive, third-party library for PDF generation. The library was powerful, but it was a nightmare to use. To create a simple one-page invoice, the dev had to instantiate a DocumentSettings object, initialize a FontRegistry, configure a PageLayoutManager, and then manually call a sequence of five different methods in a very specific order. If you swapped step three and four, the whole thing threw a cryptic NullReferenceException.
I could see them struggling, spending hours just trying to figure out the "boilerplate" required to get a single line of text on a page. It was a classic case of a subsystem being too exposed. I ended up writing a small wrapper class called PdfGenerator with a single method: CreateInvoice(InvoiceData data). Inside that method, I handled all the messy setup. The junior dev's face lit up—suddenly, a complex orchestration became a single method call. That's exactly what the Facade pattern is for.
Taming the Subsystem Chaos
At its core, the Facade pattern provides a simplified interface to a larger, more complex body of code, such as a class library or a set of interconnected subsystems. It doesn't "hide" the underlying classes—you can still access the complex guts if you absolutely have to—but it gives you a "fast track" for the most common tasks.
I like to think of it like the dashboard of a car. You don't manually manage the fuel-to-air ratio in the cylinders or timing the spark plugs every time you want to accelerate; you just press the gas pedal. The pedal is the Facade. The engine, transmission, and fuel system are the subsystems.
In C#, we implement this by creating a class that holds references to the subsystem classes and coordinates their interactions. This reduces the coupling between your main business logic and the messy details of the external library.
Building a Unified Interface
Let's look at a practical example. Imagine you're building a "Home Cinema" system. To start a movie, you have to turn on the lights, dim them, power up the projector, set the input, and start the streaming service. Doing this in your main Program.cs would be tedious and repetitive.
// The complex subsystems
public class Lights {
public void Dim() => Console.WriteLine("Lights dimming...");
public void Off() => Console.WriteLine("Lights off.");
}
public class Projector {
public void On() => Console.WriteLine("Projector warming up...");
public void SetInput(string input) => Console.WriteLine($"Input set to {input}.");
}
public class SoundSystem {
public void SetVolume(int level) => Console.WriteLine($"Volume set to {level}.");
public void On() => Console.WriteLine("Sound system online.");
}
// The Facade
public class HomeTheaterFacade {
private readonly Lights _lights = new();
private readonly Projector _projector = new();
private readonly SoundSystem _sound = new();
public void WatchMovie() {
Console.WriteLine("Get ready for the movie...");
_lights.Dim();
_projector.On();
_projector.SetInput("HDMI 1");
_sound.On();
_sound.SetVolume(20);
Console.WriteLine("Enjoy the show!");
}
public void EndMovie() {
Console.WriteLine("Shutting down...");
_projector.Off(); // Assume Off() exists
_lights.Off();
}
}
Now, instead of managing three different objects and five different method calls throughout your app, you just do this:
var cinema = new HomeTheaterFacade();
cinema.WatchMovie();
One quick side note: be careful not to let your Facade become a "God Object" that knows everything about every single part of your system. A Facade should be a convenience, not a dumping ground for all your logic. If your Facade class grows to 2,000 lines, you've probably just traded one complex problem for another.
📋 Practical Task
Exercise: Simplifying a Legacy Shipping Integration
You have inherited a codebase that integrates with a legacy shipping provider. To send a package, the code currently has to interact with three different classes: AddressValidator, ShippingRateCalculator, and LabelGenerator. Each of these must be called in a specific sequence, and the output of one is needed for the next.
Your Goal: Create a ShippingFacade class that simplifies this process into a single method called ShipPackage(string address, double weight).
Requirements:
- Create the three subsystem classes (
AddressValidator,ShippingRateCalculator, andLabelGenerator) with simple methods that print to the console (e.g.,Validate(address),Calculate(weight),GenerateLabel(address, cost)). - Implement the
ShippingFacadethat orchestrates these calls internally. - In your
Mainmethod, instantiate the facade and ship a package with a single line of code. - Ensure that the client (the
Mainmethod) never has to instantiate the validator, calculator, or generator directly.
There are no comments for now.