Background
Some time back we saw Decorator design pattern which was the 1st Structural design pattern that we tried to understand. Adapter and Facade are two more design pattern that come under same category - Structural Design pattern.
Adapter Design Pattern
Adapter pattern converts one interface to another interface that is expected by the target or client system. It simply make interfaces compatible so that integration is possible. To visualize see following diagram
Target or client expects an interface where as interface that is going to use it provides different interface and none of them can be modified. In such cases Adapter proves to be beneficial.
Lets take an example -
Lets say you have designed a program that computes cost of a list of items supplied to it. In your computeCost() method you expect that customer would provide Iterator instance. You write code something like below -
Also you have exposed a Product interface. Those who want to compute cost should have models extending this Product interface implement it's getCost() method and then can pass the iterator of it to above utility method.
Now here's something you did not expect. You get a Toy manufacturing company as your client and their code is something like -
and
and then they tried to use your code as follows -
and viola -
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
The method computeCost(Iterator<Product>) in the type CostComputer is not applicable for the arguments (Enumeration)
at ToysCostComputer.main(ToysCostComputer.java:6)
Program crashed. Your code expected a Iterator but customer code returns Enumeration. Now you or your customer have to build Adapter to support Enumeration.
It goes as follows -
and now client can use our code with the help of Adapter
and the output is - 27. Some random number but you get the point.
Class Diagram for our above demo is as follows -
Lets take an example -
Lets say you have designed a program that computes cost of a list of items supplied to it. In your computeCost() method you expect that customer would provide Iterator instance. You write code something like below -
import java.util.Iterator; public class CostComputer { public static int computeCost(Iterator<Product> iterator) { int cost = 0; while(iterator.hasNext()) { cost = cost + iterator.next().getCost(); } return cost; } }
Also you have exposed a Product interface. Those who want to compute cost should have models extending this Product interface implement it's getCost() method and then can pass the iterator of it to above utility method.
public interface Product { int getCost(); }
Now here's something you did not expect. You get a Toy manufacturing company as your client and their code is something like -
public class Toy implements Product { int cost; public Toy(int cost) { this.cost = cost; } @Override public int getCost() { return cost; } }
and
import java.util.Enumeration; import java.util.Random; import java.util.Vector; public class ToysCreator { int count; public ToysCreator(int count) { this.count = count; } public Enumeration createToys() { Vector toys = new Vector<>(); for(int i=0; i<count ;i++) { toys.add(new Toy(new Random().nextInt(10))); } return toys.elements(); } }
and then they tried to use your code as follows -
public class ToysCostComputer { public static void main(String args[]) { System.out.println(CostComputer.computeCost(new ToysCreator(6).createToys())); // this will fail } }
and viola -
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
The method computeCost(Iterator<Product>) in the type CostComputer is not applicable for the arguments (Enumeration)
at ToysCostComputer.main(ToysCostComputer.java:6)
Program crashed. Your code expected a Iterator but customer code returns Enumeration. Now you or your customer have to build Adapter to support Enumeration.
It goes as follows -
import java.util.Enumeration; import java.util.Iterator; public class EnumToIteratorAdapter implements Iterator<Product> { Enumeration<Product> enumeration; public EnumToIteratorAdapter(Enumeration<Product> enumeration) { this.enumeration = enumeration; } @Override public boolean hasNext() { return enumeration.hasMoreElements(); } @Override public Product next() { return enumeration.nextElement(); } @Override public void remove() { throw new UnsupportedOperationException(); } }
and now client can use our code with the help of Adapter
import java.util.Iterator; public class ToysCostComputer { public static void main(String args[]) { Iterator toysIterator = new EnumToIteratorAdapter(new ToysCreator(6).createToys()); System.out.println(CostComputer.computeCost(toysIterator)); } }
and the output is - 27. Some random number but you get the point.
Class Diagram for our above demo is as follows -
That was Adapter Pattern. Now lets come to facade pattern.
so in facade pattern Adapter implements target and has an instance of Adaptee. So whenever methods of target are called we call suitable methods of adaptee.
Facade Pattern.
Facade Pattern is kind of an overlay. It provides a unified interface to set of interfaces in the sub system. Note that we cannot say it encapsulates methods of sub system classes as methods of sub system classes are still available for direct use. Facade pattern just simplifies it by abstraction.There is no fixed generic class diagram as such but it will look like -
Lets say you have multiple modules for handling program bugs. May be
- class BugCreator that creates various types of bugs - UI, functional, localization etc.
- Methods : createBug(String bugType) etc
- class BugsReplicator that tries replicates the bug as per the description of filed bug.
- Methods : replicateBug(Bug newBug) etc
- class BugsSolver that finds the fix and checks in the code.
- Methods : fixBug(Bug newBug) etc
- class BugsDeployer that deploys the fix to dev or qa environments.
- Methods : deployFix(CodeFix fix) etc
- class BugsTester that tests out the fix
- Methods : testFix(Bug bug) etc
public boolean resolveBug(String desc, String bugType) { Bug newBug = new BugCreator().createBug(bugType); boolean replicated = new BugsReplicator .replicateBug(newBug); if(replicated) { CodeFix fix = new BugsSolver().fixBug(newBug); new BugsDeployer().deployFix(fix); if(new BugsTester.testFix(Bug bug)) { return true; } else { return false; } } else { return false; } }
Now this is just an example that I am giving to help you understand how Facade pattern simplifies subsystem complexity.
Notes
- Facade does not encapsulate subsystem classes. It simply provides simplified interface to complex subsystem classes. Subsystem classes are still accessible for direct use.
Summary
- Decorator pattern does not alter any interface. It simply adds responsibility.
- Adapter pattern Converts one interface (Adaptee) to another (Target) for compatibility.
- Facade pattern simplifies interface for complex subsystem subclasses.