Programmering

Tag kontrol med Proxy-designmønsteret

En af mine venner - ikke mindre en læge - fortalte mig engang, at han overbeviste en ven om at tage en collegeeksamen for ham. En person, der indtager en andens plads, er kendt som en fuldmagt. Desværre for min ven drak hans proxy lidt for meget natten før og mislykkedes testen.

I software viser Proxy-designmønsteret sig nyttigt i adskillige sammenhænge. For eksempel ved hjælp af Java XML Pack bruger du fuldmagter til at få adgang til webservices med JAX-RPC (Java API til XML-baserede fjernprocedurkald). Eksempel 1 viser, hvordan en klient får adgang til en simpel Hello World-webtjeneste:

Eksempel 1. En SOAP-proxy (Simple Object Access Protocol)

offentlig klasse HelloClient {public static void main (String [] args) {prøv {HelloIF_Stub fuldmagt = (HelloIF_Stub) (ny HelloWorldImpl (). GetHelloIF ()); fuldmagt._setTargetEndpoint (args [0]); System.out.println (fuldmagt.sigeHello ("Duke!")); } fange (Undtagelse ex) {ex.printStackTrace (); }}} 

Eksempel 1's kode ligner meget eksemplet med Hello World Web Services inkluderet i JAX-RPC. Klienten får en henvisning til proxyen og indstiller proxyens slutpunkt (webtjenestens URL) med et kommandolinjeargument. Når klienten har en henvisning til proxyen, påkalder den proxyen sig hej() metode. Proxyen videresender denne metodeopkald til webtjenesten, som ofte er placeret på en anden maskine end klientens.

Eksempel 1 illustrerer en anvendelse af Proxy-designmønsteret: adgang til eksterne objekter. Fuldmagter viser sig også nyttige til at skabe dyre ressourcer efter behov, a virtuel proxy, og til at kontrollere adgangen til objekter, a beskyttelse proxy.

Hvis du har læst min "Dekorer din Java-kode" (JavaWorld, December 2001), kan du muligvis se ligheder mellem Decorator og Proxy designmønstre. Begge mønstre bruger en proxy, der videresender metodeopkald til et andet objekt, kendt som rigtigt emne. Forskellen er, at forholdet mellem en proxy og det virkelige emne typisk indstilles på kompileringstidspunktet, mens dekoratører kan rekursivt konstrueres ved kørsel. Men jeg kommer foran mig selv.

I denne artikel introducerer jeg først proxy-mønsteret startende med et proxyeksempel for sving-ikoner. Jeg slutter med et kig på JDK's indbyggede support til Proxy-mønsteret.

Bemærk: I de første to rater af denne kolonne - "Overrask dine udviklervenner med designmønstre" (oktober 2001) og "Dekorér din Java-kode" - diskuterede jeg dekoratørmønsteret, der tæt relaterer til proxymønsteret, så du måske ønsker det at se på disse artikler, før du fortsætter.

Proxy-mønsteret

Proxy: Styr adgang til et objekt med en proxy (også kendt som en surrogat eller pladsholder).

Svingningsikoner er af grunde, der er diskuteret i afsnittet "Proxy-anvendelighed" nedenfor, et fremragende valg til at illustrere proxymønsteret. Jeg begynder med en kort introduktion til Swing-ikoner efterfulgt af en diskussion af en Swing-ikon-proxy.

Sving ikoner

Svingningsikoner er små billeder, der bruges i knapper, menuer og værktøjslinjer. Du kan også bruge Swing-ikoner alene, som figur 1 illustrerer.

Anvendelsen vist i figur 1 er anført i eksempel 2:

Eksempel 2. Sving ikoner

import java.awt. *; import java.awt.event. *; import javax.swing. *; // Denne klasse tester et billedikon. offentlig klasse IconTest udvider JFrame {privat statisk streng IMAGE_NAME = "mandrill.jpg"; privat statisk int FRAME_X = 150, FRAME_Y = 200, FRAME_WIDTH = 268, FRAME_HEIGHT = 286; privat ikon imageIcon = null, imageIconProxy = null; statisk offentlig tomrum hoved (String args []) {IconTest app = ny IconTest (); app.show (); } offentlig IconTest () {super ("Ikontest"); imageIcon = nyt ImageIcon(IMAGE_NAME); setBounds (FRAME_X, FRAME_Y, FRAME_WIDTH, FRAME_HEIGHT); setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); } offentlig tomrummning (grafik g) {super.paint (g); Insets insets = getInsets (); imageIcon.paintIcon(dette, g, insets.left, insets.top); }} 

Den foregående applikation opretter et billedikon - en forekomst af javax.swing.ImageIcon - og tilsidesætter derefter maling() metode til at male ikonet.

Sving billed-ikon fuldmagter

Applikationen vist i figur 1 er en dårlig brug af svingebilledikoner, fordi du kun skal bruge billedikoner til små billeder. Denne begrænsning findes, fordi oprettelse af billeder er dyrt, og ImageIcon forekomster skaber deres billeder, når de konstrueres. Hvis et program opretter mange store billeder på én gang, kan det medføre et betydeligt præstationshit. Også, hvis applikationen ikke bruger alle sine billeder, er det spildende at oprette dem på forhånd.

En bedre løsning indlæser billeder, når de bliver nødvendige. For at gøre dette kan en proxy oprette det rigtige ikon første gang proxyen paintIcon () metode kaldes. Figur 2 viser et program, der indeholder et billedikon (til venstre) og et billedikon-proxy (til højre). Det øverste billede viser applikationen lige efter lanceringen. Da billedikoner indlæser deres billeder, når de er konstrueret, vises et ikonbillede, så snart programmets vindue åbnes. I modsætning hertil indlæser proxyen ikke sit billede, før det males for første gang. Indtil billedet indlæses, tegner proxyen en kant omkring dets omkreds og viser "Indlæser billede ..." Det nederste billede i figur 2 viser applikationen, efter at proxyen har indlæst sit billede.

Jeg har listet applikationen vist i figur 2 i eksempel 3:

Eksempel 3. Swing icon proxies

import java.awt. *; import java.awt.event. *; import javax.swing. *; // Denne klasse tester en virtuel proxy, som er en proxy, der // forsinker indlæsning af en dyr ressource (et ikon), indtil den // ressource er nødvendig. offentlig klasse VirtualProxyTest udvider JFrame {privat statisk streng IMAGE_NAME = "mandrill.jpg"; privat statisk int IMAGE_WIDTH = 256, IMAGE_HEIGHT = 256, SPACING = 5, FRAME_X = 150, FRAME_Y = 200, FRAME_WIDTH = 530, FRAME_HEIGHT = 286; privat ikon imageIcon = null, imageIconProxy = null; statisk offentlig tomrum hoved (String args []) {VirtualProxyTest app = ny VirtualProxyTest (); app.show (); } offentlig VirtualProxyTest () {super ("Virtual Proxy Test"); // Opret et billedikon og en billedikon-proxy. imageIcon = nyt ImageIcon (IMAGE_NAME); imageIconProxy = nyt ImageIconProxy(IMAGE_NAME, IMAGE_WIDTH, IMAGE_HEIGHT); // Indstil rammens grænser og rammens standard // lukkefunktion. setBounds (FRAME_X, FRAME_Y, FRAME_WIDTH, FRAME_HEIGHT); setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); } offentlig tomrummning (grafik g) {super.paint (g); Insets insets = getInsets (); imageIcon.paintIcon(dette, g, insets.left, insets.top); imageIconProxy.paintIcon(dette, g, insets.left + IMAGE_WIDTH + SPACING, // bredde insets.top); // højde } } 

Eksempel 3 er næsten identisk med eksempel 2 bortset fra tilføjelsen af ​​billedikon-proxyen. Eksempel 3-applikationen opretter ikonet og proxyen i sin konstruktør og tilsidesætter dets maling() metode til at male dem. Inden du diskuterer proxyens implementering, skal du se på figur 3, som er et klassediagram over proxyens virkelige emne, the javax.swing.ImageIcon klasse.

Det javax.swing.Icon interface, der definerer essensen af ​​Swing-ikoner, indeholder tre metoder: paintIcon (), getIconWidth ()og getIconHeight (). Det ImageIcon klasse implementerer Ikon interface og tilføjer egne metoder. Billedikoner opretholder også en beskrivelse af og en henvisning til deres billeder.

Image-icon proxies implementerer Ikon interface og vedligeholde en henvisning til et billedikon - det virkelige emne - som klassediagrammet i figur 4 illustrerer.

Det ImageIconProxy klasse er anført i eksempel 4.

Eksempel 4. ImageIconProxy.java

// ImageIconProxy er en proxy (eller surrogat) til et ikon. // Proxyen forsinker indlæsningen af ​​billedet indtil første gang // billedet tegnes. Mens ikonet indlæser sit billede, tegner // proxyen en kant, og meddelelsen "Indlæser billede ..." klassen ImageIconProxy implementerer javax.swing.Icon {privat Ikon realIcon = null; boolsk isIconCreated = falsk; private String imageName; privat int bredde, højde; offentlig ImageIconProxy (String imageName, int width, int height) {this.imageName = imageName; denne.bredde = bredde; denne. højde = højde; } public int getIconHeight () {return isIconCreated? højde: realIcon.getIconHeight (); } public int getIconWidth () {return isIconCreated realIcon == null? bredde: realIcon.getIconWidth (); } // Proxyens paint () -metode er overbelastet for at tegne en kant // og en besked ("Indlæser billede ..."), mens billedet // indlæses. Når billedet er indlæst, tegnes det. Bemærk // at proxyen ikke indlæser billedet, før det // faktisk er nødvendigt. public void paintIcon (final Component c, Graphics g, int x, int y) { hvis (isIconCreated) { realIcon.paintIcon(c, g, x, y); } andet { g.drawRect(x, y, bredde-1, højde-1); g.drawString("Indlæser billede ...", x + 20, y + 20); // Ikonet oprettes (hvilket betyder, at billedet er indlæst) // på en anden tråd. synkroniseret (dette) {SwingUtilities.invokeLater (new Runnable () {public void run () {try {// Sænk billedindlæsningsprocessen. Thread.currentThread (). sleep (2000); // ImageIcon-konstruktør opretter billedet . realIcon = ny ImageIcon (imageName); isIconCreated = sandt; } fange (InterruptedException ex) {ex.printStackTrace (); } // Mal ikonets komponent igen, når ikonet // er oprettet. c.maling (); } }); } } } } 

ImageIconProxy opretholder en henvisning til det rigtige ikon med realIcon medlemsvariabel. Første gang, proxyen males, oprettes det rigtige ikon på en separat tråd, så rektanglet og strengen males (opkaldene til g.drawRect () og g.drawString () træder ikke i kraft før paintIcon () metode returnerer). Når det ægte ikon er oprettet, og billedet derfor er indlæst, males den komponent, der viser ikonet, igen. Figur 5 viser et sekvensdiagram for disse begivenheder.

Figur 5s sekvensdiagram er typisk for alle fuldmagter: Fuldmagter styrer adgangen til deres virkelige emne. På grund af denne kontrol, fuldmægtige instantierer ofte deres virkelige emne, som det er tilfældet med billedikon-proxyen, der er anført i eksempel 4. At instantiering er en af ​​forskellene mellem proxymønsteret og dekoratørmønsteret: Dekoratører skaber sjældent deres rigtige motiver.

JDK's indbyggede understøttelse af Proxy-designmønsteret

Proxy-mønsteret er et af de vigtigste designmønstre, fordi det giver et alternativ til at udvide funktionalitet med arv. Det alternativ er objektsammensætning, hvor et objekt (proxy) videresender metode kalder til et lukket objekt (rigtigt emne).

Objektsammensætning er at foretrække frem for arv, fordi sammenhængende objekter med komposition kun kan manipulere deres lukkede objekt gennem det lukkede objekts interface, hvilket resulterer i løs kobling mellem objekter. I modsætning hertil er klasser tæt forbundet med deres basisklasse med arv, fordi de indvendige i en basisklasse er synlig til dets udvidelser. På grund af denne synlighed kaldes arv ofte genbrug af hvidboks. På den anden side, med komposition, er det indre af det indesluttende objekt ikke synlig til det vedlagte objekt (og omvendt); derfor betegnes komposition ofte genbrug af sort boks. Alt i alt foretrækkes genbrug af sort boks (sammensætning) frem for genbrug af hvid boks (arv), fordi løs kobling resulterer i mere smidige og fleksible systemer.

Fordi Proxy-mønsteret er så vigtigt, understøtter J2SE 1.3 (Java 2 Platform, Standard Edition) og videre det direkte. Denne støtte involverer tre klasser fra java.lang.reflekteret pakke: Proxy, Metodeog InvocationHandler. Eksempel 5 viser et simpelt eksempel, der bruger JDK-understøttelsen til proxy-mønsteret: