Ein Proxy hat den Zweck, dass ein Client nicht direkt mit einem Zielobjekt kommunizieren kann. Stattdessen läuft die Kommunikation zwischen Client und Zielobjekt stets über den zwischengeschalteten Stellvertreter/Proxy. Dabei wird die ursprüngliche Anfrage des Client ggf. manipuliert, aufgezeichnet, verzögert, gänzlich blockiert oder ähnliches.

Der Proxy ist eine Klasse, die dieselbe Schnittstelle wie das eigentliche Zielobjekt erfüllt und daher an dessen Stelle eingesetzt werden kann. Im folgenden UML-Klassendiagramm wird das Proxy-Muster dargestellt. Der Proxy delegiert die Anfrage des Client an das Zielobjekt RealObject, welches ihm zu diesem Zweck bekannt ist (Attribut Proxy.realObject, s. Implementierung der Methode Proxy.operation). Im Gegensatz zum Adapter verändert der Proxy die Schnittstelle nicht, sondern implementiert sie seinerseits vollständig, wobei eine zusätzliche Funktionalität ergänzt wird, um das Zielobjekt zu entlasten.

Das Proxy-Muster bringt zunächst einen eindeutigen Nachteil mit sich: Es entsteht durch die Proxy-Klasse zusätzlicher Code, der die Komplexität im Call-Stack und den verbundenen Aufwand für Test und Wartung erhöht. Für die zusätzliche Kontrollebene, die durch den Proxy gewonnen wird, muss es also gute Gründe geben, die folgend exemplarisch vorgestellt werden. Voraussetzung für die Notwendigkeit einer Proxy-Klasse ist es, dass die dahinterliegende Klasse nicht verändert werden kann oder soll.

Im folgenden Code-Beispiel zur Verdeutlichung des Proxy-Musters besteht die Funktion des Zielobjekts RealVideo darin eine Video-Datei aus dem Web zu laden und abzuspielen. Das Laden des Videos findet im Konstruktor der Klasse RealVideo statt (Zeile 6). Das Abspielen startet bei Aufruf der Methode play (Zeilen 10-14). Das Interface Video schreibt nur diese Methode play vor, die auch von der Proxy-Klasse ProxyVideo implementiert wird. Die Proxy-Klasse verändert das Verhalten des Zielobjekts um zwei Aspekte:

class RealVideo implements Video {

    MediaPlayer player;

    RealVideo(String src) {
        player = new MediaPlayer(new Media(src));
    }

    @Override
    public void play(MediaView mediaView) {
        mediaView.setMediaPlayer(player);
        System.out.println("Start playing " + player.getMedia().getSource());
        player.play();
    }
}
interface Video {
    void play(MediaView mediaView);
}
class ProxyVideo implements Video {

    RealVideo video;
    String src;

    ProxyVideo(String src) {
        // video is not loaded here
        this.src = src;
    }

    @Override
    public void play(MediaView mediaView) {
        // lazy loading >> video is loaded only when played and not in constructor
        if (video == null) {
            video = new RealVideo(src);
            video.player.setOnReady(() -> checkBeforePlay(mediaView));
        }
        else { checkBeforePlay(mediaView); }
    }

    void checkBeforePlay(MediaView mediaView) {
        // don't play short advertising videos (duration < 30 s)
        Double duration = video.player.getMedia().getDuration().toSeconds();
        if (duration < 30) {
            System.out.printf("Ignored an advertising video %s (%.2f s)\n", src, duration);
            return;
        }
        video.play(mediaView);
    }
}
public class Client extends Application { // the client is a JavaFX application

    final static String VIDEO_URL = "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/";

    public static void main(String[] args) { launch(args); } // JavaFX application launcher

    @Override
    public void start(Stage stage) {
        MediaView mediaView = new MediaView();

        List<String> filenames = List.of("BigBuckBunny.mp4", "ElephantsDream.mp4", "ForBiggerBlazes.mp4", "ForBiggerEscapes.mp4");
        List<Video> videos = filenames.stream().map(filename -> new ProxyVideo(VIDEO_URL + filename))
            .collect(Collectors.toList());

        Button b = new Button("Play next video");
        b.setOnAction(e -> {
            Video anyVideo = videos.get(new Random().nextInt(videos.size()));
            anyVideo.play(mediaView);  // play random video
        });

        Scene scene = new Scene(new VBox(mediaView, b));
        stage.setScene(scene);
        stage.show();
    }
}

Der Client startet eine einfache JavaFX-Anwendung, die jedes Mal, wenn der Anwender auf einen Button klickt, ein zufälliges Video abspielt (Zeilen 15-19 in Client). Zuvor wird eine Liste mit ProxyVideo-Objekten befüllt (Zeile 12). In dieser Zeile könnte der Konstruktoraufruf new ProxyVideo durch new RealVideo ersetzt werden, um den Proxy nicht zu verwenden, und demzufolge die Videos bereits vorab zu laden und die kurzen Werbevideos abzuspielen anstatt sie zu überspringen.

Das folgende UML-Klassendiagramm entspricht dem Code-Beispiel für das Proxy-Muster.

Die gezeigten Code-Beispiele zum Entwurfsmuster Proxy finden sich im Verzeichnis /patterns/proxy des Modul-Repository.