Поемете контрол с модела за дизайн на прокси

Един мой приятел - доктор по медицина, не по-малко - веднъж ми каза, че е убедил свой приятел да му вземе изпит в колеж. Някой, който заема мястото на някой друг, е известен като пълномощник. За съжаление на моя приятел, неговият прокси пиеше прекалено много предната вечер и не успя на теста.

В софтуера моделът на прокси дизайн се оказва полезен в много контексти. Например, използвайки Java XML Pack, използвате прокси за достъп до уеб услуги с JAX-RPC (Java API за XML-базирани извиквания на отдалечени процедури). Пример 1 показва как клиентът осъществява достъп до проста услуга Hello World Web:

Пример 1. Прокси за SOAP (Simple Object Access Protocol)

публичен клас HelloClient {public static void main (String [] args) {try {HelloIF_Stub proxy = (HelloIF_Stub) (new HelloWorldImpl (). getHelloIF ()); прокси ._setTargetEndpoint (аргументи [0]); System.out.println ( proxy .sayHello ("Duke!")); } catch (Изключение ex) {ex.printStackTrace (); }}}

Кодът на Пример 1 наподобява примера на Hello World Web Services, включен в JAX-RPC. Клиентът получава препратка към проксито и задава крайната точка на проксито (URL адреса на уеб услугата) с аргумент от командния ред. След като клиентът има препратка към проксито, той извиква sayHello()метода на проксито . Проксито препраща извикването на този метод към уеб услугата, която често се намира на машина, различна от тази на клиента.

Пример 1 илюстрира едно използване на шаблона за проектиране на прокси: достъп до отдалечени обекти. Прокситата също се оказват полезни за създаване на скъпи ресурси при поискване, виртуален прокси сървър и за контрол на достъпа до обекти, защитен прокси сървър.

Ако сте прочели моя „Украсете своя Java код“ ( JavaWorld, декември 2001 г.), може да видите прилики между шаблоните за дизайн на Decorator и Proxy. И двата модела използват прокси, който препраща извиквания на метод към друг обект, известен като истински обект. Разликата е, че с модела на прокси връзката между прокси и истинския обект обикновено се задава по време на компилиране, докато декораторите могат да бъдат конструирани рекурсивно по време на изпълнение. Но аз изпреварвам себе си.

В тази статия първо представям модела на прокси, започвайки с пример за прокси за икони Swing. Завършвам с поглед върху вградената поддръжка на JDK за прокси модела.

Забележка: В първите две части от тази колона - „Удивете приятелите си разработчици с дизайнерски модели“ (октомври 2001 г.) и „Украсете своя Java код“ - обсъдих шаблона на декоратор, който е тясно свързан с модела на прокси, така че може да пожелаете да разгледате тези статии, преди да продължите.

Прокси моделът

Прокси: Контролирайте достъпа до обект с прокси (известен също като заместител или заместител).

Swing иконите, поради причини, разгледани в раздела "Приложимост на прокси" по-долу, представляват отличен избор за илюстриране на модела на прокси. Започвам с кратко въведение в иконите на Swing, последвано от обсъждане на прокси за икона на Swing.

Люлки икони

Иконите Swing са малки картинки, използвани в бутони, менюта и ленти с инструменти. Можете също да използвате икони Swing сами по себе си, както е показано на фигура 1.

Приложението, показано на фигура 1, е изброено в пример 2:

Пример 2. Икони за люлеене

импортиране на java.awt. *; импортиране на java.awt.event. *; импортиране на javax.swing. *; // Този клас тества икона на изображение. публичен клас IconTest разширява JFrame {private static String IMAGE_NAME = "mandrill.jpg"; частен статичен int FRAME_X = 150, FRAME_Y = 200, FRAME_WIDTH = 268, FRAME_HEIGHT = 286; частна икона imageIcon = null, imageIconProxy = null; static public void main (String args []) {Приложение IconTest = ново IconTest (); app.show (); } публичен IconTest () {супер („Тест на икони“); imageIcon = нов ImageIcon (IMAGE_NAME); setBounds (FRAME_X, FRAME_Y, FRAME_WIDTH, FRAME_HEIGHT); setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); } публична боя за празнота (Графика g) {super.paint (g); Вмъква вмъквания = getInsets (); imageIcon.paintIcon (this, g, insets.left, insets.top); }}

Предишното приложение създава икона на изображение - екземпляр на javax.swing.ImageIcon- и след това отменя paint()метода за рисуване на иконата.

Завъртете прокси за изображение-икона

Приложението, показано на фигура 1, използва лошо иконите на Swing изображения, защото трябва да използвате икони на изображения само за малки снимки. Това ограничение съществува, защото създаването на изображения е скъпо и ImageIconекземплярите създават своите изображения, когато са конструирани. Ако дадено приложение създаде много големи изображения наведнъж, това може да доведе до значително постигане на производителност. Освен това, ако приложението не използва всички свои изображения, е напразно да ги създавате предварително.

По-доброто решение зарежда изображения, когато станат необходими. За целта проксито може да създаде истинската икона при първото paintIcon()извикване на метода на проксито . Фигура 2 показва приложение, което съдържа икона на изображение (вляво) и прокси за икона на изображение (вдясно). Горната снимка показва приложението веднага след стартирането му. Тъй като иконите на изображения зареждат своите изображения, когато са конструирани, изображението на икона се показва веднага щом се отвори прозорецът на приложението. За разлика от това, проксито не зарежда изображението си, докато не е нарисувано за първи път. Докато изображението се зареди, проксито очертава граница по целия му периметър и показва „Зареждане на изображение ...“ Долната снимка на фигура 2 показва приложението, след като проксито е заредил изображението си.

Изброих приложението, показано на фигура 2 в пример 3:

Пример 3. Прокси на Swing icon

импортиране на java.awt. *; импортиране на java.awt.event. *; импортиране на javax.swing. *; // Този клас тества виртуален прокси, който е прокси, който // отлага зареждането на скъп ресурс (икона), докато този // ресурс не е необходим. публичен клас VirtualProxyTest разширява JFrame {private static String IMAGE_NAME = "mandrill.jpg"; частен статичен int IMAGE_WIDTH = 256, IMAGE_HEIGHT = 256, ПРОСТРАНСТВО = 5, FRAME_X = 150, FRAME_Y = 200, FRAME_WIDTH = 530, FRAME_HEIGHT = 286; частна икона imageIcon = null, imageIconProxy = null; static public void main (String args []) {VirtualProxyTest app = new VirtualProxyTest (); app.show (); } публичен VirtualProxyTest () {super ("Тест за виртуален прокси"); // Създаване на икона на изображение и прокси на изображение-икона. imageIcon = нов ImageIcon (IMAGE_NAME); imageIconProxy = новоImageIconProxy (IMAGE_NAME, IMAGE_WIDTH, IMAGE_HEIGHT); // Задайте границите на рамката и операцията по подразбиране // затваряне. setBounds (FRAME_X, FRAME_Y, FRAME_WIDTH, FRAME_HEIGHT); setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); } публична боя за празнота (Графика g) {super.paint (g); Вмъква вмъквания = getInsets (); imageIcon.paintIcon (this, g, insets.left, insets.top); imageIconProxy.paintIcon (this, g, insets.left + IMAGE_WIDTH + SPACING, // width insets.top); // височина}}

Пример 3 е почти идентичен с пример 2, с изключение на добавянето на проксито на изображението-икона. Приложението Пример 3 създава иконата и проксито в своя конструктор и заменя своя paint()метод, за да ги рисува. Преди да обсъдите изпълнението на проксито, погледнете Фигура 3, която е диаграма на класа на истинския предмет на проксито, javax.swing.ImageIconкласа.

В javax.swing.Iconинтерфейса, който определя същността на икони люлка, включва три основни метода: paintIcon(), getIconWidth(), и getIconHeight(). В ImageIconклас изпълнява Iconинтерфейс и добавя методи за себе си. Иконите на изображения също поддържат описание и препратка към техните изображения.

Прокситата на Image-icon изпълняват Iconинтерфейса и поддържат препратка към икона на изображение - истинския обект - както илюстрира схемата на класа на Фигура 4.

В ImageIconProxyкласа в Пример 4 е вписана.

Пример 4. ImageIconProxy.java

// ImageIconProxy е прокси (или заместител) за икона. // Проксито забавя зареждането на изображението до първия път, когато // изображението е изчертано. Докато иконата зарежда изображението си, // проксито очертава граница и съобщението „Зареждане на изображение ...“ клас ImageIconProxy реализира javax.swing.Icon {private Icon realIcon = null; boolean isIconCreated= невярно; частен низ imageName; private int ширина, височина; публичен ImageIconProxy (String imageName, int width, int height) {this.imageName = imageName; this.width = ширина; this.height = височина; } public int getIconHeight () {return isIconCreated? височина: realIcon.getIconHeight (); } public int getIconWidth () {return isIconCreated realIcon == null? ширина: realIcon.getIconWidth (); } // Методът paint () на проксито е претоварен, за да се очертае граница // и съобщение ("Зареждане на изображение ..."), докато изображението // се зарежда. След като изображението се зареди, то се изчертава. Забележете //, че проксито не зарежда изображението, докато не е // действително необходимо. публична невалидна paintIcon (краен компонент c, графика g, int x, int y) { if (isIconCreated) { realIcon.paintIcon (c, g, x, y); } else { g.drawRect(x, y, ширина-1, височина-1); g.drawString ("Зареждане на изображение ...", x + 20, y + 20); // Иконата се създава (което означава, че изображението се зарежда) // върху друга нишка. синхронизирано (това) {SwingUtilities.invokeLater (new Runnable () {public void run () {try {// Забавяне на процеса на зареждане на изображението. Thread.currentThread (). sleep (2000); // ImageIcon конструктор създава изображението . realIcon = нов ImageIcon (imageName); isIconCreated = вярно;} улов (InterruptedException ех) {ex.printStackTrace ();} // пребоядисвам на иконата компонент след като е създаден // иконата. c.repaint (); }} ); }}}}

ImageIconProxyподдържа препратка към реалната икона с realIconпроменливата член. Първият път, когато проксито е боядисано, истинската икона се създава в отделна нишка, за да позволи правоъгълникът и низът да бъдат боядисани (извикванията g.drawRect()и g.drawString()не влизат в сила, докато paintIcon()методът не се върне). След като се създаде истинската икона и следователно изображението се зареди, компонентът, който показва иконата, се пребоядисва. Фигура 5 показва диаграма на последователността за тези събития.

Диаграмата на последователността на Фигура 5 е типична за всички прокси: Проксите контролират достъпа до техния реален обект. Поради този контрол прокситата често създават екземпляри за истинския си обект , какъвто е случаят с проксито на иконата на изображението, изброено в Пример 4. Това инстанциране е една от разликите между шаблона на прокси и декоратора: Декораторите рядко създават своите истински обекти.

Вградената поддръжка на JDK за шаблона за дизайн на прокси

Прокси шаблонът е един от най-важните дизайнерски модели, тъй като предоставя алтернатива на разширяване на функционалността с наследяване. Тази алтернатива е състава на обект, където обект (прокси) препраща извикване на метод към затворен обект (реален обект).

Съставът на обекта е за предпочитане пред наследяването, тъй като с композицията, заграждащите обекти могат да манипулират затворения си обект само чрез интерфейса на затворения обект, което води до свободно свързване между обектите. За разлика от това, при наследяването класовете са тясно свързани с базовия си клас, защото вътрешните елементи на базовия клас са видими за неговите разширения. Поради тази видимост наследяването често се нарича повторно използване на бяла кутия. От друга страна, при композицията вътрешните елементи на заграждащия обект не се виждат от затворения обект (и обратно); поради това съставът често се нарича повторна употреба на черна кутия. При равни други условия повторното използване на черната кутия (състав) е за предпочитане пред повторното използване на бялата кутия (наследяване), тъй като разхлабеното свързване води до по-ковък и гъвкав системи.

Тъй като моделът на прокси е толкова важен, J2SE 1.3 (платформа Java 2, стандартно издание) и не само го поддържа директно. Това подпомагане се изразява в три класа от java.lang.reflectпакета: Proxy, Method, и InvocationHandler. Пример 5 показва прост пример, който използва JDK поддръжката за прокси модела: