Programování

Převezměte kontrolu s designovým vzorem Proxy

Můj přítel - lékař, o nic méně - mi jednou řekl, že přesvědčil přítele, aby pro něj udělal zkoušku na vysoké škole. Někdo, kdo nahradí někoho jiného, ​​je znám jako proxy. Bohužel pro mého přítele jeho zástupce večer před tím vypil příliš mnoho a neprošel zkouškou.

V softwaru se návrhový vzor Proxy osvědčuje v mnoha kontextech. Například pomocí sady Java XML Pack používáte proxy pro přístup k webovým službám pomocí JAX-RPC (Java API pro vzdálená volání procedur na základě XML). Příklad 1 ukazuje, jak klient přistupuje k jednoduché webové službě Hello World:

Příklad 1. Proxy server SOAP (Simple Object Access Protocol)

public class HelloClient {public static void main (String [] args) {try {HelloIF_Stub proxy = (HelloIF_Stub) (nový HelloWorldImpl (). GetHelloIF ()); proxy._setTargetEndpoint (args [0]); System.out.println (proxy.sayHello („Duke!“)); } catch (Exception ex) {ex.printStackTrace (); }}} 

Kód z příkladu 1 se velmi podobá příkladu webových služeb Hello World, který je součástí JAX-RPC. Klient získá odkaz na proxy a nastaví koncový bod proxy (URL webové služby) pomocí argumentu příkazového řádku. Jakmile má klient odkaz na proxy, vyvolá proxy Řekni Ahoj() metoda. Proxy předává volání této metody webové službě, která se často nachází na jiném počítači, než je klient.

Příklad 1 ilustruje jedno použití pro návrhový vzor Proxy: přístup ke vzdáleným objektům. Proxy se také osvědčily při vytváření drahých zdrojů na vyžádání, a virtuální proxy, a pro řízení přístupu k objektům, a ochrana proxy.

Pokud jste si přečetli moje „Ozdobte svůj Java kód“ (JavaWorld, Prosinec 2001), můžete vidět podobnosti mezi návrhovými vzory Decorator a Proxy. Oba vzory používají proxy, které předává volání metody jinému objektu, známému jako skutečný předmět. Rozdíl je v tom, že se vzorem Proxy je vztah mezi proxy a skutečným subjektem obvykle nastaven v době kompilace, zatímco dekoratéry lze rekurzivně konstruovat za běhu. Ale předcházím sám sebe.

V tomto článku nejprve představím vzor Proxy, počínaje příkladem proxy pro ikony Swing. Na závěr se podívám na vestavěnou podporu JDK pro vzor Proxy.

Poznámka: V prvních dvou částech tohoto sloupce - „Ohromte své vývojářské přátele designovými vzory“ (říjen 2001) a „Ozdobte svůj kód Java“ - jsem diskutoval o vzorovém dekorátoru, který úzce souvisí se vzorem Proxy, takže si možná budete přát než budete pokračovat, podívejte se na tyto články.

Vzorec proxy

Proxy: Řízení přístupu k objektu pomocí serveru proxy (také známého jako náhradní nebo zástupný symbol).

Ikony Swing, z důvodů popsaných v části „Použitelnost proxy“ níže, představují vynikající volbu pro ilustraci vzoru proxy. Začnu krátkým úvodem do ikon Swing, následovaným diskusí o proxy ikonách Swing.

Swing ikony

Ikony otočení jsou malé obrázky používané v tlačítkách, nabídkách a panelech nástrojů. Ikony Swing můžete také použít samostatně, jak ukazuje obrázek 1.

Aplikace zobrazená na obrázku 1 je uvedena v příkladu 2:

Příklad 2. Swingové ikony

importovat java.awt. *; importovat java.awt.event. *; importovat javax.swing. *; // Tato třída testuje ikonu obrázku. veřejná třída IconTest rozšiřuje JFrame {private static String IMAGE_NAME = "mandrill.jpg"; soukromý statický int FRAME_X = 150, FRAME_Y = 200, FRAME_WIDTH = 268, FRAME_HEIGHT = 286; soukromá ikona imageIcon = null, imageIconProxy = null; static public void main (String args []) {IconTest app = new IconTest (); app.show (); } public IconTest () {super ("Test ikon"); imageIcon = nový ImageIcon(IMAGE_NAME); setBounds (FRAME_X, FRAME_Y, FRAME_WIDTH, FRAME_HEIGHT); setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); } public void paint (Grafika g) {super.paint (g); Vložky vložky = getInsets (); imageIcon.paintIcon(this, g, insets.left, insets.top); }} 

Předchozí aplikace vytvoří ikonu obrázku - instanci javax.swing.ImageIcon - a poté přepíše malovat() metoda malování ikony.

Swing image-icon proxy

Aplikace zobrazená na obrázku 1 špatně využívá obrazové ikony Swing, protože obrazové ikony byste měli používat pouze pro malé obrázky. Toto omezení existuje, protože vytváření obrázků je drahé a ImageIcon instance vytvářejí své obrázky, když jsou vytvořeny. Pokud aplikace vytvoří mnoho velkých obrázků najednou, může to způsobit významný zásah do výkonu. Pokud aplikace také nepoužívá všechny své obrázky, je zbytečné je vytvářet předem.

Lepší řešení načte obrázky podle potřeby. Chcete-li tak učinit, může proxy vytvořit skutečnou ikonu poprvé proxy paintIcon () metoda se nazývá. Obrázek 2 ukazuje aplikaci, která obsahuje obrazovou ikonu (vlevo) a proxy obrazové ikony (vpravo). Horní obrázek ukazuje aplikaci těsně po jejím spuštění. Protože obrázkové ikony načtou své obrázky, jakmile jsou vytvořeny, obrázek ikony se zobrazí, jakmile se otevře okno aplikace. Naproti tomu server proxy nenačte svůj obraz, dokud není namalován poprvé. Dokud se obrázek nenačte, proxy nakreslí ohraničení po svém obvodu a zobrazí „Načítání obrázku ...“ Spodní obrázek na obrázku 2 zobrazuje aplikaci poté, co proxy načte svůj obrázek.

Uvedl jsem aplikaci zobrazenou na obrázku 2 v příkladu 3:

Příklad 3. Proxy serveru Swing

importovat java.awt. *; importovat java.awt.event. *; importovat javax.swing. *; // Tato třída testuje virtuální server proxy, což je server proxy, který // zpožďuje načítání nákladného zdroje (ikony), dokud tento zdroj // není potřeba. veřejná třída VirtualProxyTest rozšiřuje JFrame {soukromý statický řetězec IMAGE_NAME = "mandrill.jpg"; soukromý statický int IMAGE_WIDTH = 256, IMAGE_HEIGHT = 256, SPACING = 5, FRAME_X = 150, FRAME_Y = 200, FRAME_WIDTH = 530, FRAME_HEIGHT = 286; soukromá ikona imageIcon = null, imageIconProxy = null; static public void main (String args []) {VirtualProxyTest app = new VirtualProxyTest (); app.show (); } public VirtualProxyTest () {super ("Test virtuálního proxy"); // Vytvořte ikonu obrázku a proxy ikony obrázku. imageIcon = nový ImageIcon (IMAGE_NAME); imageIconProxy = nový ImageIconProxy(IMAGE_NAME, IMAGE_WIDTH, IMAGE_HEIGHT); // Nastaví hranice rámečku a výchozí // zavírací operaci rámečku. setBounds (FRAME_X, FRAME_Y, FRAME_WIDTH, FRAME_HEIGHT); setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); } public void paint (Grafika g) {super.paint (g); Vložky vložky = getInsets (); imageIcon.paintIcon(this, g, insets.left, insets.top); imageIconProxy.paintIcon(this, g, insets.left + IMAGE_WIDTH + SPACING, // width insets.top); // výška}} 

Příklad 3 je téměř totožný s příkladem 2, s výjimkou přidání proxy ikony s obrázkem. Aplikace z příkladu 3 vytvoří ikonu a proxy v konstruktoru a přepíše jejich malovat() způsob jejich malování. Před diskusí o implementaci proxy se podívejte na obrázek 3, což je třídní diagram skutečného subjektu proxy, javax.swing.ImageIcon třída.

The javax.swing.Icon rozhraní, které definuje podstatu ikon Swing, zahrnuje tři metody: paintIcon (), getIconWidth (), a getIconHeight (). The ImageIcon třída implementuje Ikona rozhraní a přidává vlastní metody. Ikony obrázků také udržují popis a odkaz na jejich obrázky.

Proxy zástupci s ikonami implementují Ikona rozhraní a udržovat odkaz na ikonu obrázku - skutečný předmět - jak ukazuje diagram tříd na obrázku 4.

The ImageIconProxy třída je uvedena v příkladu 4.

Příklad 4. ImageIconProxy.java

// ImageIconProxy je proxy (nebo náhradní) pro ikonu. // Proxy server zpožďuje načítání obrázku až do prvního vykreslení // obrazu. Zatímco ikona načítá svůj obrázek, // proxy nakreslí ohraničení a zpráva „Načítání obrázku ...“ třída ImageIconProxy implementuje javax.swing.Icon {private Ikona realIcon = null; booleovský isIconCreated = false; private String imageName; soukromá int šířka, výška; public ImageIconProxy (String imageName, int width, int height) {this.imageName = imageName; this.width = width; this.height = výška; } public int getIconHeight () {return isIconCreated? výška: realIcon.getIconHeight (); } public int getIconWidth () {návrat isIconCreated realIcon == null? width: realIcon.getIconWidth (); } // Metoda paint () proxy je přetížena, aby nakreslila ohraničení // a zprávu („Načítání obrázku ...“) při načítání obrázku //. Po načtení se obrázek nakreslí. Všimněte si //, že proxy nenačte obrázek, dokud jej // skutečně nepotřebujete. public void paintIcon (konečná složka c, grafika g, int x, int y) { pokud (isIconCreated) { realIcon.paintIcon(c, g, x, y); } else { g.drawRect(x, y, šířka-1, výška-1); g.drawString("Načítání obrázku ...", x + 20, y + 20); // Ikona je vytvořena (což znamená, že je obrázek načten) // v jiném vlákně. synchronized (this) {SwingUtilities.invokeLater (new Runnable () {public void run () {try {// Zpomalit proces načítání obrázků. Thread.currentThread (). sleep (2000); // ImageIcon konstruktor vytvoří obrázek . realIcon = new ImageIcon (imageName); isIconCreated = true; } catch (InterruptedException ex) {ex.printStackTrace (); } // Po vytvoření ikony // překreslete komponentu ikony. c.repaint (); } }); } } } } 

ImageIconProxy udržuje odkaz na skutečnou ikonu pomocí realIcon členské proměnné. Při prvním malování proxy je skutečná ikona vytvořena v samostatném vlákně, aby bylo možné malovat obdélník a řetězec (volání g.drawRect () a g.drawString () vstoupí v platnost až po paintIcon () metoda se vrací). Poté, co se vytvoří skutečná ikona, a proto se načte obrázek, komponenta, která zobrazuje ikonu, se překreslí. Obrázek 5 ukazuje sekvenční diagram pro tyto události.

Sekvenční diagram obrázku 5 je typický pro všechny proxy: Servery proxy řídí přístup k jejich skutečnému subjektu. Kvůli té kontrole proxy často instancují svůj skutečný předmět, jako je tomu v případě proxy ikon pro obrázky uvedených v příkladu 4. Toto vytvoření instance je jedním z rozdílů mezi vzorem Proxy a vzorem dekorátoru: Dekorátoři zřídka vytvářejí své skutečné předměty.

Integrovaná podpora JDK pro návrhový vzor Proxy

Vzor Proxy je jedním z nejdůležitějších návrhových vzorů, protože poskytuje alternativu k rozšíření funkcí s dědičností. Tato alternativa je složení objektu, kde metoda (proxy) přeposílá metodu volání na uzavřený objekt (skutečný předmět).

Skladba objektu je vhodnější než dědičnost, protože se složením mohou uzavřené objekty manipulovat se svým uzavřeným objektem pouze prostřednictvím rozhraní uzavřeného objektu, což má za následek volné propojení mezi objekty. Naproti tomu s dědičností jsou třídy pevně spojeny se svou základní třídou, protože interní prvky základní třídy jsou viditelné jeho rozšíření. Kvůli této viditelnosti se dědičnost často označuje jako opětovné použití bílé krabice. Na druhou stranu, s kompozicí, vnitřnosti obklopujícího objektu jsou neviditelný k uzavřenému objektu (a naopak); proto se složení často označuje jako opětovné použití černé skříňky. Všechny věci jsou stejné, opětovné použití černé skříňky (složení) je lepší než opětovné použití bílé skříňky (dědičnost), protože volné spojení vede k tvárnějším a flexibilnějším systémům.

Protože vzor Proxy je tak důležitý, J2SE 1.3 (Java 2 Platform, Standard Edition) a další jej přímo podporuje. Tato podpora zahrnuje tři třídy z java.lang.reflect balík: Proxy, Metoda, a InvocationHandler. Příklad 5 ukazuje jednoduchý příklad, který využívá podporu JDK pro vzor Proxy: