MindView Inc.
[ Viewing Hints ] [ Revision History ] [ Build the Code ] [ Report an Error ]
[ Free Newsletter ] [ Seminars ] [ Consulting ]

Thinking in Patterns with Java, Revision 0.6

©2001 by Bruce Eckel

[ Previous Chapter ] [ Short TOC ] [ Table of Contents ] [ Index ] [ Next Chapter ]


7: Changing the interface

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.

Adapter

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.

Façade

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.

Package as a variation of Façade

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.

Exercises

  1. The java.util.Map has no way to automatically load a two-dimensional array of objects into a Map as key-value pairs. Create an adapter class that does this.
[ Previous Chapter ] [ Short TOC ] [ Table of Contents ] [ Index ] [ Next Chapter ]
Last Update:09/08/2001