Facade Design Pattern in Java

Learn the Facade design pattern with a runnable Java report example, clear UML diagrams, and practical guidance on file errors and subsystem access.

java-design-patterns

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.

ParticipantResponsibility
ClientRequests a useful operation without coordinating every subsystem call.
FacadeOffers that operation and delegates its steps to the appropriate components.
Subsystem componentsPerform 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.

Conceptual UML diagram showing Client calling Facade, which calls three subsystem components

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 participantClass in this exampleRole
ClientFacadeDemoSupplies a title and destination, then requests a report.
FacadeReportGeneratorFacadeCalls the source, formatter, and writer in order.
Subsystem componentsReportDataSource, ReportFormatter, ReportWriterProvide 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.

UML diagram showing FacadeDemo calling ReportGeneratorFacade.generate and the facade using ReportDataSource, ReportFormatter and ReportWriter

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

PatternMain purposeIn this reporting example
FacadeSimplifies use of several components.generate() coordinates loading, formatting, and writing.
AdapterConverts one interface into another interface a client expects.An adapter could make a different row provider satisfy ReportDataSource.
MediatorCoordinates 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!

Leave a Comment

  1. 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.

Comments are closed.

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.