Эккель Брюс
Шрифт:
}
public void commando {
System out.println(name + " чистит крышу"),
}
}
);
}
public static void main(String[] args) {
Robot Test.test(new SnowRemovalRobot("SIusher"));
}
} /* Output: Название: Slusher Модель: SnowBot Series 11 Slusher может убирать снег Slusher убирает снег Slusher может колоть лед-Si usher колет лед Slusher может чистить крышу Slusher чистит крышу *///:-
Предполагается, что существуют разные типы роботов, и для каждого типа Robot объект с неопределенным состоянием должен делать что-то особенное — в нашем примере выдавать информацию о конкетном типе Robot, представленном объектом. Эта информация перехватывается динамическим посредником:
//: typeinfo/NullRobot.java
// Использование динамического посредника для создания
// объекта с неопределенным состоянием import java.lang.reflect *, import java util *, import net.mindview util.*;
class NullRobotProxyHandler implements InvocationHandler { private String null Name; private Robot proxied = new NRobotO. NullRobotProxyHandler(CIass<? extends Robot> type) {
nullName = type getSimpleNameO + " NullRobot";
}
private class NRobot implements Null, Robot {
public String nameO { return nullName; } public String model О { return nullName; } public List<Operation> operations О {
return Col lections.emptyList;
}
}
public Object
invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(proxied, args);
public class NullRobot { public static Robot
newNullRobot(CIass<? extends Robot> type) {
return (Robot)Proxy.newProxyInsta nee(
NullRobot class.getClassLoaderO, new Class[]{ Null.class, Robot.class }, new Nul1RobotProxyHandler(type));
}
public static void main(String[] args) { Robot[] bots = {
new SnowRemova1 Robot("SnowBee"), newNul1 Robot(SnowRemova1 Robot.class)
}:
for(Robot bot : bots)
Robot.Test.test(bot),
}
} /* Output-Название: SnowBee Модель: SnowBot Series 11 SnowBee может убирать снег SnowBee убирает снег SnowBee может колоть лед SnowBee колет лед SnowBee может чистить крышу SnowBee чистит крышу [Null Robot]
Название. SnowRemova1 Robot NullRobot Модель: SnowRemovalRobot NullRobot *///.-
Каждый раз, когда вам требуется объект Robot с неопределенным состоянием, вы вызываете newNuURobotQ и передаете тип Robot, для которого создается
посредник. Посредник выполняет требования о поддержке интерфейсов Robot и Null, а также предоставляет имя опосредованного типа.
Интерфейсы и информация о типах
Одной из важных целей ключевого слова interface является изоляция компонентов и сокращение привязок. Использование интерфейсов вроде бы позволяет добиться этой цели, однако RTTI позволяет обойти ограничения — интерфейсы не обеспечивают стопроцентной изоляции. Начнем следующий пример с интерфейса:
//: typeinfo/interfacea/A.java package typeinfo.interfacea;
public interface A {
void f; } ///:-
Затем интерфейс реализуется, и выясняется, что можно «в обход» добраться до фактического типа реализации:
//• typei nfo/1nterfaceVi olati on.java // Интерфейс можно обойти import typeinfo.interfacea.*;
class В implements A { public void f {} public void g {}
}
public class InterfaceViolation {
public static void main(String[] args) { A a = new BO; a.fO;
// a.gO; // Ошибка компиляции System.out.pri ntln(a.getClass.getName); if(a instanceof B) { В b = (B)a; b.gO;
}
}
} /* Output: В
*///:-
Используя RTTI, мы выясняем, что объект а реализован в форме В. Преобразование к типу В позволяет вызвать метод, не входящий в интерфейс А.
Все это абсолютно законно и допустимо, но, скорее всего, вы предпочли бы оградить клиентских программистов от подобных выходок. Казалось бы, ключевое слово interaface должно защищать вас, но на самом деле этого не происходит, а факт использования В для реализации А становится известен любому желающему.
Одно из возможных решений: просто скажите программистам, что если они будут использовать фактический класс вместо интерфейса, то пускай сами разбираются со всеми возникающими проблемами. Вероятно, во многих случаях этого достаточно, но если «вероятно» вас не устраивает — можно применить более жесткие меры.
Проще всего установить для реализации пакетный уровень доступа, чтобы она оставалась невидимой для клиентов за пределами пакета:
//. typeinfo/packageaccess/HiddenC.java package typeinfo.packageaccess; import typeinfo interfacea.*; import static net mindview util.Print *.