The Facade pattern gives an application one simple operation for work that normally requires several classes. A caller uses that operation while the facade contacts the right components in the right order. The components still do the actual work.
“Provide a unified interface to a set of interfaces in a subsystem.” This is the Gang of Four definition of Facade. In the example below, a reporting feature loads rows, formats text, and writes a file. Its caller requests a completed report through one method.
How the Facade Pattern Works
The pattern has three kinds of participants. A subsystem is the group of components that perform the work; it does not need to know that a facade exists.
| Participant | Responsibility |
|---|---|
| Client | Requests a useful operation without coordinating every subsystem call. |
| Facade | Offers that operation and delegates its steps to the appropriate components. |
| Subsystem components | Perform their own specialized work and may also be available to other callers. |
The client calls the facade. The facade then calls the subsystem components. A facade simplifies the common path; it does not remove the subsystem or require its classes to be inaccessible.
Facade Pattern in Action
Consider a reporting feature in an application. It needs to produce a text file containing a title and a few rows. Without a facade, each caller would need to load the rows, format them, and pass the result to a file writer. ReportGeneratorFacade collects that sequence in generate().
| Pattern participant | Class in this example | Role |
|---|---|---|
| Client | FacadeDemo | Supplies a title and destination, then requests a report. |
| Facade | ReportGeneratorFacade | Calls the source, formatter, and writer in order. |
| Subsystem components | ReportDataSource, ReportFormatter, ReportWriter | Provide rows, build text, and write the file. |
The data source is an interface so the demo can provide rows without setting up a database. The formatter produces a string; the writer stores that string. The caller supplies the data source to the facade. The facade creates the formatter and writer, then calls all three components.
Facade Pattern Implementation
ReportDataSource supplies rows and ReportFormatter turns them into the report text. ReportWriter writes that text to the requested path. These excerpts omit imports and the demo class; the complete runnable source contains them.
interface ReportDataSource {
List<String> loadRows();
}
final class ReportFormatter {
String format(String title, List<String> rows) {
return title + "\n" + String.join("\n", rows);
}
}
final class ReportWriter {
void write(String content, Path destination) throws IOException {
Files.writeString(destination, content);
}
}
The facade’s generate() method makes the sequence explicit. It rejects a null title or destination, loads rows, formats them, and asks the writer to save the result.
final class ReportGeneratorFacade {
private final ReportDataSource dataSource;
private final ReportFormatter formatter = new ReportFormatter();
private final ReportWriter writer = new ReportWriter();
ReportGeneratorFacade(ReportDataSource dataSource) {
this.dataSource = Objects.requireNonNull(dataSource);
}
void generate(String title, Path destination) throws IOException {
Objects.requireNonNull(title);
Objects.requireNonNull(destination);
List<String> rows = dataSource.loadRows();
String content = formatter.format(title, rows);
writer.write(content, destination);
}
}
IOException is declared on generate() because the file write can fail. The facade passes that failure to the caller, which can decide how to handle it.
Demo
FacadeDemo supplies two rows, asks the facade to write a temporary report, and reads it back. It also tries to write to a directory, which produces a file write error. The complete source cleans up both temporary paths.
javac -d out src/main/java/com/howtodoinjava/patterns/structural/facade/FacadeDemo.java
java -cp out com.howtodoinjava.patterns.structural.facade.FacadeDemo
Weekly report
Orders 12
Refunds 2
Report write failed: AccessDeniedException
The first three lines come from the file that was written and read back. The final line shows the failure reaching the caller. The exact exception subtype can vary by file system, but the caller receives an IOException rather than a false success.
When to Use a Facade
Use a facade when callers repeatedly perform the same multi-step task or when they should depend on a smaller, easier interface. Keep the facade focused on a coherent operation. If it starts accumulating unrelated business tasks, split those tasks into more suitable interfaces.
The facade does not decide file policy unless we explicitly put that policy into its contract. The example uses Files.writeString(), which creates a missing file and truncates an existing one by default. A production reporting feature may instead reject existing files or write to a temporary file before replacing the destination. Such choices depend on the application’s requirements.
The facade also does not have to block direct use of ReportFormatter or ReportWriter. Another caller can use a lower-level component when it needs different behavior. Nor does the facade need to be a singleton.
Facade Compared With Similar Patterns
| Pattern | Main purpose | In this reporting example |
|---|---|---|
| Facade | Simplifies use of several components. | generate() coordinates loading, formatting, and writing. |
| Adapter | Converts one interface into another interface a client expects. | An adapter could make a different row provider satisfy ReportDataSource. |
| Mediator | Coordinates interactions among participating objects. | The formatter and writer do not communicate through a mediator; the facade calls each as a step. |
Choose Facade to simplify a workflow; choose Adapter when an existing interface does not fit the interface a client needs.
FAQs
Does a facade have to hide every subsystem class?
No. The facade provides a convenient path for common work. Other callers can still use subsystem classes directly if the application exposes them.
Can an application have more than one facade?
Yes. Different groups of operations can have different facades. A reporting facade need not also manage billing or user accounts.
Does the facade handle failures automatically?
No. It can translate or handle failures if that is part of its contract. In this example, generate() declares IOException so the caller knows that writing the file can fail.
Conclusion
Facade gives the caller a clear operation while specialized classes keep their own responsibilities. In the report example, generate() gathers rows, formats them, and writes a file. The caller gets a simpler way to request a report and still receives a write failure when one occurs.
Happy Learning!
In challenges of Facade Design pattern, you mentioned upon the overhead of managing an extra layer of abstraction.
Changes to internal structure of the subsystem should follow the open-close principle right? Extension is acceptable but modifications reeks of bad design.
That’s right.
fix typo – interbnal
Thanks, Deepak for reporting. Much appreciated !!
It looks exactly like the Factory pattern, what is the difference?