Programmering

Ta kontroll med Proxy-designmønsteret

En venn av meg - ikke minst en lege - fortalte meg en gang at han overbeviste en venn om å ta en collegeeksamen for ham. Noen som tar plassen til noen andre er kjent som en fullmektig. Dessverre for min venn, hans fullmektig drakk litt for mye kvelden før og mislyktes i testen.

I programvare viser Proxy-designmønsteret seg nyttig i mange sammenhenger. Hvis du for eksempel bruker Java XML Pack, bruker du fullmakter til å få tilgang til webtjenester med JAX-RPC (Java API for XML-baserte eksterne prosedyreanrop). Eksempel 1 viser hvordan en klient får tilgang til en enkel 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 fullmektig = (HelloIF_Stub) (ny HelloWorldImpl (). GetHelloIF ()); fullmektig._setTargetEndpoint (args [0]); System.out.println (fullmektig.sayHello ("Duke!")); } fange (Unntak ex) {ex.printStackTrace (); }}} 

Eksempel 1s kode ligner nøye på Hello World Web Services-eksemplet som følger med JAX-RPC. Klienten får en referanse til proxyen, og setter proxyens endepunkt (webtjenestens URL) med et kommandolinjeargument. Når klienten har en referanse til fullmektigen, påkaller den fullmakten si hei() metode. Fullmakten videresender denne metoden til webtjenesten, som ofte ligger på en annen maskin enn klienten.

Eksempel 1 illustrerer en bruk for Proxy-designmønsteret: tilgang til eksterne objekter. Fullmakter viser seg også nyttige for å skape dyre ressurser på forespørsel, a virtuell proxy, og for å kontrollere tilgangen til objekter, a beskyttelsesfullmektig.

Hvis du har lest min "Dekorer Java-koden" (JavaWorld, Desember 2001), kan det hende du ser likheter mellom Decorator og Proxy designmønstre. Begge mønstrene bruker en proxy som videresender metoden samtaler til et annet objekt, kjent som ekte emne. Forskjellen er at forholdet mellom en proxy og det virkelige motivet med Proxy-mønsteret vanligvis settes til kompileringstid, mens dekoratører kan konstrueres rekursivt ved kjøretid. Men jeg kommer foran meg selv.

I denne artikkelen introduserer jeg først proxy-mønsteret, og begynner med et proxyeksempel for svingikoner. Jeg avslutter med å se på JDKs innebygde støtte for proxy-mønsteret.

Merk: I de to første delene av denne kolonnen - "Amaze Your Developer Friends with Design Patterns" (Oktober 2001) og "Decorate Your Java Code" - diskuterte jeg Decorator-mønsteret, som er nært knyttet til Proxy-mønsteret, så du kan ønske deg å se på disse artiklene før du fortsetter.

Proxy-mønsteret

Proxy: Kontroller tilgang til et objekt med en proxy (også kjent som en surrogat eller plassholder).

Sving-ikoner, av årsaker diskutert i "Proxy-anvendbarhet" -delen nedenfor, representerer et utmerket valg for å illustrere proxy-mønsteret. Jeg begynner med en kort introduksjon til svingikoner, etterfulgt av en diskusjon av en svingikon-proxy.

Sving ikoner

Sving-ikoner er små bilder som brukes i knapper, menyer og verktøylinjer. Du kan også bruke Swing-ikoner alene, som figur 1 illustrerer.

Søknaden vist i figur 1 er oppført i eksempel 2:

Eksempel 2. Sving ikoner

importer java.awt. *; importer java.awt.event. *; import javax.swing. *; // Denne klassen tester et bildeikon. offentlig klasse IconTest utvider JFrame {private static String IMAGE_NAME = "mandrill.jpg"; privat statisk int FRAME_X = 150, FRAME_Y = 200, FRAME_WIDTH = 268, FRAME_HEIGHT = 286; private Ikon imageIcon = null, imageIconProxy = null; statisk offentlig tomrom hoved (String args []) {IconTest app = new IconTest (); app.show (); } offentlig IconTest () {super ("Ikontest"); imageIcon = ny ImageIcon(IMAGE_NAME); setBounds (FRAME_X, FRAME_Y, FRAME_WIDTH, FRAME_HEIGHT); setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); } offentlig tomrom (Grafikk g) {super.paint (g); Insets insets = getInsets (); imageIcon.paintIcon(dette, g, innsatser. venstre, innsatser. toppen); }} 

Den forrige applikasjonen oppretter et bildeikon - en forekomst av javax.swing.ImageIcon - og overstyrer deretter maling() metode for å male ikonet.

Sving bilde-ikon fullmakter

Programmet vist i figur 1 er dårlig bruk av svingebildeikoner fordi du bare skal bruke bildesymboler for små bilder. Denne begrensningen eksisterer fordi det er dyrt å lage bilder, og ImageIcon forekomster lager sine bilder når de konstrueres. Hvis et program lager mange store bilder samtidig, kan det føre til et betydelig ytelseshit. Også, hvis applikasjonen ikke bruker alle bildene, er det bortkastet å lage dem på forhånd.

En bedre løsning laster inn bilder etter hvert som de blir behov for. For å gjøre det kan en proxy opprette det virkelige ikonet første gang proxyen paintIcon () metoden kalles. Figur 2 viser et program som inneholder et bildeikon (til venstre) og en bildeikon-proxy (til høyre). Det øverste bildet viser applikasjonen like etter lanseringen. Fordi bildeikoner laster inn bildene når de er konstruert, vises et ikonbilde så snart programmets vindu åpnes. Derimot laster ikke proxyen bildet sitt før det males for første gang. Inntil bildet lastes, tegner proxyen en kant rundt omkretsen og viser "Laster inn bilde ..." Det nederste bildet i figur 2 viser applikasjonen etter at proxyen har lastet inn bildet.

Jeg har listet opp applikasjonen vist i figur 2 i eksempel 3:

Eksempel 3. Sving ikon fullmakter

importer java.awt. *; importer java.awt.event. *; import javax.swing. *; // Denne klassen tester en virtuell proxy, som er en proxy som // utsetter lasting av en kostbar ressurs (et ikon) til den // ressursen er nødvendig. offentlig klasse VirtualProxyTest utvider 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; private Ikon imageIcon = null, imageIconProxy = null; statisk offentlig tomrom hoved (String args []) {VirtualProxyTest app = new VirtualProxyTest (); app.show (); } offentlig VirtualProxyTest () {super ("Virtual Proxy Test"); // Opprett et bildeikon og en bildeikon-proxy. imageIcon = ny ImageIcon (IMAGE_NAME); imageIconProxy = ny ImageIconProxy(IMAGE_NAME, IMAGE_WIDTH, IMAGE_HEIGHT); // Sett grensene til rammen, og rammens standard // lukkeoperasjon. setBounds (FRAME_X, FRAME_Y, FRAME_WIDTH, FRAME_HEIGHT); setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); } offentlig tomrom (Grafikk g) {super.paint (g); Insets insets = getInsets (); imageIcon.paintIcon(dette, g, innsatser. venstre, innsatser. toppen); imageIconProxy.paintIcon(dette, g, innsett. venstre + IMAGE_WIDTH + AVSTAND, // bredde innsatser.topp); // høyde}} 

Eksempel 3 er nesten identisk med eksempel 2, bortsett fra tillegg av bildeikon-proxyen. Eksempel 3-applikasjonen oppretter ikonet og proxyen i konstruktøren, og overstyrer dens maling() metode for å male dem. Før du diskuterer fullmaktsimplementeringen, se på figur 3, som er et klassediagram over proxyens virkelige emne, the javax.swing.ImageIcon klasse.

De javax.swing.Icon grensesnitt, som definerer essensen av svingikoner, inneholder tre metoder: paintIcon (), getIconWidth (), og getIconHeight (). De ImageIcon klasse implementerer Ikon grensesnitt, og legger til egne metoder. Bildeikoner opprettholder også en beskrivelse av og en referanse til bildene deres.

Image-icon proxyer implementerer Ikon grensesnitt og vedlikehold en referanse til et bildeikon - det virkelige motivet - som klassediagrammet i figur 4 illustrerer.

De ImageIconProxy klasse er oppført i eksempel 4.

Eksempel 4. ImageIconProxy.java

// ImageIconProxy er en fullmektig (eller surrogat) for et ikon. // Proxyen forsinker innlasting av bildet til første gang // tegnes. Mens ikonet laster inn bildet, tegner // proxyen en ramme og meldingen "Laster inn bilde ..." ImageIconProxy implementerer javax.swing.Icon {private Ikon realIcon = null; boolsk isIconCreated = falsk; private String imageName; privat int bredde, høyde; public ImageIconProxy (String imageName, int width, int height) {this.imageName = imageName; denne.bredde = bredde; dette. høyde = høyde; } public int getIconHeight () {return isIconCreated? høyde: realIcon.getIconHeight (); } public int getIconWidth () {return isIconCreated realIcon == null? bredde: realIcon.getIconWidth (); } // Proxyens paint () -metode er overbelastet for å tegne en ramme // og en melding ("Laster inn bilde ...") mens bildet // lastes inn. Etter at bildet er lastet, tegnes det. Legg merke til // at proxyen ikke laster inn bildet før det faktisk er nødvendig. public void paintIcon (final Component c, Graphics g, int x, int y) { hvis (isIconCreated) { realIcon.paintIcon(c, g, x, y); } annet { g.drawRect(x, y, bredde-1, høyde-1); g.drawString("Laster inn bilde ...", x + 20, y + 20); // Ikonet opprettes (noe som betyr at bildet er lastet) // på en annen tråd. synkronisert (dette) {SwingUtilities.invokeLater (new Runnable () {public void run () {try {// Slow down the image-loading process. Thread.currentThread (). sleep (2000); // ImageIcon constructor create image) . realIcon = ny ImageIcon (imageName); isIconCreated = sant; } fange (InterruptedException ex) {ex.printStackTrace (); } // Mal ikonets komponent på nytt etter at //-ikonet er opprettet. c.maling (); } }); } } } } 

ImageIconProxy opprettholder en referanse til det virkelige ikonet med realIcon medlemsvariabel. Første gang proxyen males, blir det virkelige ikonet opprettet på en egen tråd for å tillate at rektangelet og strengen males (samtalene til g.drawRect () og g.drawString () ikke tre i kraft før paintIcon () metoden returnerer). Etter at det virkelige ikonet er opprettet, og derfor bildet er lastet inn, blir komponenten som viser ikonet malt på nytt. Figur 5 viser et sekvensdiagram for disse hendelsene.

Figur 5s sekvensdiagram er typisk for alle fullmakter: Fullmakter kontrollerer tilgang til sitt virkelige subjekt. På grunn av den kontrollen, fullmakter instanserer ofte sitt virkelige emne, som er tilfelle for bildeikon-proxyen som er oppført i eksempel 4. At instantiering er en av forskjellene mellom proxy-mønsteret og dekoratørmønsteret: Dekoratører lager sjelden sine virkelige motiver.

JDKs innebygde støtte for Proxy-designmønsteret

Proxy-mønsteret er et av de viktigste designmønstrene fordi det gir et alternativ til å utvide funksjonalitet med arv. Det alternativet er objektsammensetning, der et objekt (proxy) videresender metoden kaller til et lukket objekt (reelt subjekt).

Objektsammensetning er å foretrekke fremfor arv, fordi innesluttende objekter med komposisjon bare kan manipulere deres lukkede objekt gjennom det vedlagte objektets grensesnitt, noe som resulterer i løs kobling mellom objekter. I motsetning til dette, med arv, er klasser tett koblet til basisklassen fordi det indre av en basisklasse er synlig til utvidelsene. På grunn av den synligheten blir arv ofte referert til som gjenbruk av hvit boks. På den annen side, med komposisjon, er det indre av det omsluttende objektet ikke synlig til det vedlagte objektet (og omvendt); derfor blir sammensetning ofte referert til som gjenbruk av svart boks. Alt som er like, er gjenbruk av svart boks (sammensetning) å foretrekke fremfor hvit boks gjenbruk (arv) fordi løs kobling resulterer i mer smidige og fleksible systemer.

Fordi proxy-mønsteret er så viktig, støtter J2SE 1.3 (Java 2 Platform, Standard Edition) og videre direkte det. Den støtten involverer tre klasser fra java.lang.refleksjon pakke: Fullmektig, Metode, og InvocationHandler. Eksempel 5 viser et enkelt eksempel som bruker JDK-støtten for proxy-mønsteret:

$config[zx-auto] not found$config[zx-overlay] not found