Virtual Proxy

In the previous post I have summarized the formal description for the Proxy Pattern, as described in HFDP - Head First Design Patterns. Now it is time to talk about Virtual Proxy, a pattern that is close to the Remote Proxy, referring to a resource that is not local.

The Virtual Proxy is typically used when the RealSubject is expensive to create. So, we initially don't create the RealSubject, but we give the user just a cheap surrogate; in the meanwhile the proxy start working to make available the RealSubject and then it would delegate the user requests to it.

As an example of virtual proxy, we can think of an application that displays images that have to be fetched on the internet. Since the connection could be slow, it makes sense to implement the viewer as a virtual proxy, displaying to the user just a label until the real thing is locally available.

I rearranged a bit the code to let us put some waiting inside, and using a lot of faking, instead of real pictures. As a bonus, I cleaned up the code throwing in the State pattern (as it is actually suggested by the HFDP's authors).

Our fake image is an object of a class implementing this interface:
public interface FakeImage {
    void paint();
    int size();
}
We assume that a real FakeImage, whatever it would be, implements too this interface, but here we see just the virtual proxy, that is designed to assume two states: Faking, until the FakeImage is loaded, and then Available.

Here is the states:
public interface State extends FakeImage {} // 1

// ...

public class StateAvailable implements State { // 2
    FakeImageProxy proxy;

    public StateAvailable(FakeImageProxy proxy) {
        this.proxy = proxy;
    }

    public void paint() {
        System.out.println(proxy.getFakeImage());
    }

    public int size() {
        return proxy.getFakeImage().length();
    }
}

// ...

public class StateFaking implements State {
    private Thread tLoader;
    private boolean retrieving;
    private FakeImageProxy proxy;

    public StateFaking(FakeImageProxy proxy) {
        this.proxy = proxy;
    }

    public void paint() { // 3
        System.out.println("please wait ...");
        if (!retrieving) {
            retrieving = true;
            tLoader = new Thread(new Runnable() {
                public void run() {
                    synchronized (this) {
                        try { wait(1000); } catch (Exception e) {}
                    }
                    proxy.setFakeImage("A string pretending to be an image");
                    proxy.imageLoaded(); // 4
                }
            });

            tLoader.start();
        }
    }

    public int size() {
        return 0;
    }
}
1. We actually could avoid to use a specific interface for the State, since it is just a synonym for FakeImage. But I reckon it makes the code clearer this way, and I'd say it is worthy.
2. When we are in the Available state, there is not much to do.
3. When in Fake, on the other side, we see what really is the pattern about. The paint() method provides a surrogate, and takes care of making available the real thing.
4. When the (fake) image is available, we signal the proxy we should change state accordingly.

The code for the proxy, thanks to the State Pattern, is quite simple:
public class FakeImageProxy implements FakeImage {
    private String fakeImage;

    private State faking = new StateFaking(this);
    private State available = new StateAvailable(this);
    private State current = faking;

    public void paint() {
        current.paint();
    }

    public int size() {
        return current.size();
    }

    void setFakeImage(String fakeImage) {
        this.fakeImage = fakeImage;
    }

    void imageLoaded() {
        current = available;
    }

    public String getFakeImage() {
        return fakeImage;
    }
}
Here is a short tester:
FakeImage fk = new FakeImageProxy();
while(fk.size() == 0) {
    synchronized (fk) {
        fk.paint();
        try { fk.wait(200); } catch(Exception ex) {ex.printStackTrace();}
    }
}
fk.paint();

2 comments:

  1. How would you create a FakeImageClass that implements FakeImage interface?

    ReplyDelete
    Replies
    1. Hey, thank you for passing by.

      For what we care here, a FakeImageClass.paint() could simply print a String on the output console (something like "The real image") and return the String length as its size.

      Delete