Рейтинговые книги
Читем онлайн Философия Java3 - Брюс Эккель

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 76 77 78 79 80 81 82 83 84 ... 132

// typeinfo/SimpleDynamicProxy java import java lang.reflect *.

class DynamicProxyHandler implements InvocationHandler { private Object proxied; public DynamicProxyHandler(Object proxied) { this proxied = proxied,

}

public Object

invoke(Object proxy, Method method, Object[] args) throws Throwable {

System out.printlnC'**** proxy. " + proxy.getClass() +

method- " + method + ", args " + args); if(args != nul 1) продолжение &

for(Object arg : args)

System.out.println(" " + arg); return method.invoke(proxied, args);

}

}

class SimpleDynamicProxy {

public static void consumer(Interface iface) { iface.doSomething(); i face.somethi ngElse("bonobo");

}

public static void main(String[] args) {

Real Object real = new Real ObjectО; consumer(real);

// Вставляем посредника и вызываем снова: Interface proxy = (Interface)Proxy.newProxyInstance( Interface.class.getClassLoader(), new Class[]{ Interface.class }. new DynamicProxyHandler(real)); consumer(proxy);

}

} /* Output. doSomething somethingElse bonobo

**** proxy: class SProxyO. method: public abstract void Interface.doSomething(), args: null

doSomething

**** proxy: class SProxyO. method: public abstract void

Interface.somethi ngElse(java.1ang.Stri ng), args: [[email protected]

bonobo somethingElse bonobo *///:-

Динамический посредник создается вызовом статического метода Proxy. newProxyInstance(), которому должен передаваться загрузчик класса, список интерфейсов, которые должны реализовываться посредником (а не классов или абстрактных классов!), а также реализация интерфейса Invocation Handler. Динамический посредник перенаправляет все вызовы обработчику, поэтому конструктор обработчика обычно получает ссылку на «настоящий» объект для перенаправления ему запросов.

Метод invoke() получает объект посредника на случай, если ему понадобится определить, откуда поступил запрос — впрочем, обычно это несущественно. Будьте внимательны при вызове методов посредника из invoke(), потому что вызовы через интерфейс перенаправляются через посредника.

В общем случае вы выполняете опосредованную операцию, а затем используете Method.invoke() для перенаправления запроса опосредованному объекту с передачей необходимых аргументов. При этом некоторые вызовы методов могут отфильтровываться, а другие — проходить:

//: typeinfo/SelectingMethods.java

// Looking for particular methods in a dynamic proxy.

import java.lang.reflect.*;

import static net.mindview.util.Print.*;

class MethodSelector implements InvocationHandler {

private Object proxied; public MethodSelector(Object proxied) { this proxied = proxied;

}

public Object

invoke(Object proxy, Method method. Objectd args) throws Throwable {

i f(method.getName().equals("i nteresti ng"))

print("Посредник обнаружил интересный метод"); return method.invoke(proxied. args);

}

}

interface SomeMethods { void boringlO; void boring2(); void interesting(String arg). void boring3();

}

class Implementation implements SomeMethods { public void boringlO { printC'boringl"); } public void boring2() { print("boring2"); } public void interesting(String arg) { print("interesting " + arg);

}

public void boring3() { print("boring3"); }

}

class SelectingMethods {

public static void main(String[] args) {

SomeMethods proxy= (SomeMethods)Proxy.newProxyInstance( SomeMethods.class.getClassLoader().

new Class[]{ SomeMethods.class }. new MethodSelector(new Implementation))); proxy.boringlO; proxy.boring2(); proxy.i nteresti ng("bonobo"); proxy.boring3();

}

} /* Output:

boringl

boring2

Посредник обнаружил интересный метод interesting bonobo boring3 *///:-

В данном случае мы просто проверяем имена методов, но с таким же успехом можно анализировать другие аспекты сигнатуры и даже значения аргументов.

Вряд ли вам придется каждый день пользоваться динамическими посредниками, но они хорошо подходят для решения многих разновидностей задач.

Объекты с неопределенным состоянием

Если использовать для обозначения неопределенного состояния (то есть отсутствия) объекта встроенное значение null, то при каждом использовании ссылки придется проверять, не равна ли она null. Это быстро утомляет, а код получается излишне громоздким. Проблема заключается в том, что null не имеет собственного поведения, кроме выдачи NullPointerException при попытке выполнения с ним какой-либо операции. Иногда бывает полезно ввести понятие объекта с неопределенным состояниемкоторый принимает сообщения, но возвращает значение, свидетельствующее об отсутствии «настоящего» объекта. Таким образом, вы можете считать, что все объекты действительны, и вам не придется тратить время на проверки null (и читать полученный код).

Было бы интересно представить себе язык программирования, автоматически создающий объекты с неопределенным состоянием, но на практике они применяются не так уж часто — иногда проверки null оказывается достаточно, иногда можно уверенно считать, что значение null вам не попадется, а иногда даже обработка аномальных ситуаций через NullPointerException является допустимой. Наибольшую пользу объекты с неопределенным состоянием приносят «вблизи от данных», представляя сущности в пространстве задачи. Простой пример: во многих системах имеется класс Person, а в коде возникают ситуации, когда объект не представляет конкретную личность (или, по крайней мере, информация о ней недоступна); при традиционном подходе вам следовало бы проверить ссылку null. Также можно воспользоваться объектом с неопределенным состоянием, но, даже несмотря на то, что такой объект будет отвечать на все сообщения, на которые отвечает «настоящий» объект, все равно потребуется способ проверки его на «определенность». Проще всего определить для этого специальный интерфейс:

//. net/mindview/uti1/Null java package net.mindview util; public interface Null {} ///•-

Это позволяет instanceof обнаруживать объекты с неопределенным состоянием и, что еще важнее, не требует включения метода isNull() во все классы (в конце концов, это фактически будет другим способом выполнения RTTI — так почему бы сразу не воспользоваться встроенными средствами?):

// typeinfo/Person java

// Класс с неопределенным состоянием объекта

import net mindview util.*;

class Person {

public final String first; public final String last; public final String address; // И т д.

public Person(String first. String last, String address){ this.first = first;

1 Идея принадлежит Бобби Вульфу (Bobby Woolf) и Брюсу Андерсону (Bruce Anderson).

this.last = last; this.address = address;

}

public String toStringO {

return "Person: " + first + " " + last + " " + address;

}

public static class NullPerson extends Person implements Null {

private NullPerson^) { super("None". "None". "None"); } public String toStringO { return "NullPerson"; }

}

public static final Person NULL = new NullPersonO;

} ///:-

В общем случае объект с неопределенным состоянием является синглетным, поэтому он создается как экземпляр static final. Это возможно благодаря тому, что объект Person неизменяем — значения задаются в конструкторе, а затем читаются, но не могут изменяться (поскольку поля String по своей природе неизменяемы). Если вы захотите изменить NullPerson, его придется заменить новым объектом Person. Обратите внимание: для обнаружения обобщенной поддержки Null или более конкретного типа NullPerson можно использовать instanceof, но при синглетной архитектуре можно воспользоваться просто equals() или даже == для сравнения с Person.NULL.

Представьте, что вы собираетесь открыть новое предприятие, но, пока вакансии еще не заполнены, в каждой должности Position можно временно хранить «заполнитель» — объект Person с неопределенным состоянием:

//• typeinfo/Position.java

class Position {

private String title; private Person person;

public Position(String jobTitle. Person employee) { title = jobTitle; person = employee; if(person == null)

person = Person.NULL;

}

public Position(String jobTitle) { title = jobTitle; person = Person.NULL;

}

public String getTitleO { return title; } public void setTitle(String newTitle) { title = newTitle;

}

public Person getPersonО { return person; } public void setPerson(Person newPerson) { person = newPerson. if(person == null)

person = Person.NULL;

}

public String toStringO {

return "Position: " + title + " " + person; продолжение &

}

} ///:-

Превращать Position в объект с неопределенным состоянием не обязательно, потому что существование Person.NULL подразумевает неопределенность Position (возможно, позднее выяснится, что явная поддержка неопределенного состояния для Position нужна, и вы добавите ее, но в соответствии с одним из канонов экстремального программирования в начальный проект следует включить «простейшее решение, которое будет работать», и включать новые функции лишь по мере возникновения реальной необходимости).

Теперь класс Staff может проверять объекты с неопределенным состоянием при заполнении вакансий:

//: typeinfo/Staff.java

import java util.*;

public class Staff extends ArrayList<Position> {

public void addCString title, Person person) { add(new PositionCtitle, person)),

}

public void add(String.. titles) { for(String title : titles)

add(new PositionCtitle));

}

public StaffCString. titles) { add(titles); }

public boolean positionAvailable(String title) { for(Position position : this)

if(position.getTitleO.equals(title) &&

position.getPersonO == Person.NULL) return true;

return false,

}

public void fillPosition(String title, Person hire) { for(Position position ; this)

if(position.getTitleO.equals(title) &&

position.getPersonO == Person.NULL) { position.setPerson(hire); return;

}

throw new RuntimeException(

"Position " + title + " not available");

}

public static void main(String[] args) {

Staff staff = new Staff("President", "СТО",

"Marketing Manager", "Product Manager". "Project Lead", "Software Engineer", "Software Engineer", "Software Engineer", "Software Engineer", "Test Engineer", "Technical Writer"); sta ff.fi11Pos i t i on("Pres i dent".

new PersonCMe", "Last". "The Top, Lonely At")); staff.fi11Position("Project Lead".

new PersonC"Janet". "Planner". "The Burbs")); if(staff.positionAvailableC"Software Engineer")) staff.fi11Position("Software Engi neer". new PersonO'Bob". "Coder". "Bright Light City"));

System.out.printin(staff).

}

} /* Output:

[Position: President Person: Me Last The Top. Lonely At. Position. СТО NullPerson. Position: Marketing Manager NullPerson. Position: Product Manager NullPerson. Position. Project Lead Person: Janet Planner The Burbs. Position: Software Engineer Person: Bob Coder Bright Light City. Position: Software Engineer NullPerson. Position: Software Engineer NullPerson. Position- Software Engineer NullPerson. Position. Test Engineer NullPerson. Position: Technical Writer NullPerson] *///.-

Обратите внимание: в некоторых местах нам по-прежнему приходится проверять объекты на определенное состояние, что принципиально не отличается от проверки null, но в других местах, скажем, при преобразованиях toStringO), лишние проверки не нужны; мы просто считаем, что ссылка на объект действительна.

1 ... 76 77 78 79 80 81 82 83 84 ... 132
На этой странице вы можете бесплатно читать книгу Философия Java3 - Брюс Эккель бесплатно.
Похожие на Философия Java3 - Брюс Эккель книги

Оставить комментарий