Sometimes the problem that you’re
solving is as simple as “I don’t have the interface that I
want.” Two of the patterns in Design Patterns solve this problem:
Adapter takes one type and produces an interface to some other type.
Façade creates an interface to a set of classes, simply to provide
a more comfortable way to deal with a library or bundle of
resources.
When you’ve got this, and
you need that, Adapter solves the problem. The only requirement is
to produce a that, and there are a number of ways you can accomplish this
adaptation.
//: c07:Adapter.java // Variations on the Adapter pattern. import com.bruceeckel.test.*; class WhatIHave { public void g() {} public void h() {} } interface WhatIWant { void f(); } class ProxyAdapter implements WhatIWant { WhatIHave whatIHave; public ProxyAdapter(WhatIHave wih) { whatIHave = wih; } public void f() { // Implement behavior using // methods in WhatIHave: whatIHave.g(); whatIHave.h(); } } class WhatIUse { public void op(WhatIWant wiw) { wiw.f(); } } // Approach 2: build adapter use into op(): class WhatIUse2 extends WhatIUse { public void op(WhatIHave wih) { new ProxyAdapter(wih).f(); } } // Approach 3: build adapter into WhatIHave: class WhatIHave2 extends WhatIHave implements WhatIWant { public void f() { g(); h(); } } // Approach 4: use an inner class: class WhatIHave3 extends WhatIHave { private class InnerAdapter implements WhatIWant{ public void f() { g(); h(); } } public WhatIWant whatIWant() { return new InnerAdapter(); } } public class Adapter extends UnitTest { WhatIUse whatIUse = new WhatIUse(); WhatIHave whatIHave = new WhatIHave(); WhatIWant adapt= new ProxyAdapter(whatIHave); WhatIUse2 whatIUse2 = new WhatIUse2(); WhatIHave2 whatIHave2 = new WhatIHave2(); WhatIHave3 whatIHave3 = new WhatIHave3(); public void test() { whatIUse.op(adapt); // Approach 2: whatIUse2.op(whatIHave); // Approach 3: whatIUse.op(whatIHave2); // Approach 4: whatIUse.op(whatIHave3.whatIWant()); } public static void main(String args[]) { new Adapter().test(); } } ///:~
I’m taking liberties
with the term “proxy” here, because in Design Patterns they
assert that a proxy must have an identical interface with the object that it is
a surrogate for. However, if you have the two words together: “proxy
adapter,” it is perhaps more
reasonable.
A general principle that I apply when
I’m casting about trying to mold requirements into a first-cut object is
“If something is ugly, hide it inside an object.” This is basically
what Façade accomplishes. If you have a rather confusing
collection of classes and interactions that the client programmer doesn’t
really need to see, then you can create an interface that is useful for the
client programmer and that only presents what’s
necessary.
Façade is often implemented as
singleton abstract factory. Of course, you can easily get this effect by
creating a class containing static factory methods:
//: c07:Facade.java import com.bruceeckel.test.*; class A { public A(int x) {} } class B { public B(long x) {} } class C { public C(double x) {} } // Other classes that aren't exposed by the // facade go here ... public class Facade extends UnitTest { static A makeA(int x) { return new A(x); } static B makeB(long x) { return new B(x); } static C makeC(double x) { return new C(x); } // The client programmer gets the objects // by calling the static methods: A a = Facade.makeA(1); B b = Facade.makeB(1); C c = Facade.makeC(1.0); public void test() {} public static void main(String args[]) { new Facade().test(); } } ///:~
The example given in
Design Patterns isn’t really a Façade but just a
class that uses the other classes.
To me, the Façade has a
rather “procedural” (non-object-oriented) feel to it: you are just
calling some functions to give you objects. And how different is it, really,
from Abstract Factory? The point of Façade is to hide part
of a library of classes (and their interactions) from the client programmer, to
make the interface to that group of classes more digestible and easier to
understand.
However, this is precisely what the
packaging features in Java accomplish: outside of the library, you can only
create and use public classes; all the non-public classes are only
accessible within the package. It’s as if Façade is a
built-in feature of Java.
To be fair, Design Patterns is
written primarily for a C++ audience. Although C++ has namespaces to prevent
clashes of globals and class names, this does not provide the class hiding
mechanism that you get with non-public classes in Java. The majority of
the time I think that Java packages will solve the Façade
problem.