Эккель Брюс
Шрифт:
Метод getD усугубляет сложности, связанные с private-интерфейсом, — это открытый (public) метод, возвращающий ссылку на закрытый (private) интерфейс. Что можно сделать с возвращаемым значением этого метода? В методе main мы видим несколько попыток использовать это возвращаемое значение, и все они оказались неудачными. Заставить метод работать можно только одним способом — передать возвращаемое значение некоторому объекту, которому разрешено его использование (в нашем случае это еще один объект А, у которого имеется необходимый метод receiveD).
Интерфейс Е показывает, что интерфейсы могут быть вложены друг в друга. Впрочем, правила для интерфейсов — в особенности то, что все элементы интерфейса должны быть открытыми (public), — здесь строго соблюдаются, поэтому интерфейс, вложенный внутрь другого интерфейса, автоматически объявляется открытым и его нельзя сделать закрытым (private).
Пример Nestinglnterfaces демонстрирует разнообразные способы реализации вложенных интерфейсов. Особо стоит отметить тот факт, что при реализации интерфейса вы не обязаны реализовывать вложенные в него интерфейсы. Также закрытые (private) интерфейсы нельзя реализовать за пределами классов, в которых они описываются.
Интерфейсы и фабрики
Предполагается, что интерфейс предоставляет своего рода «шлюз» к нескольким альтернативным реализациям. Типичным способом получения объектов, соответствующих интерфейсу, является паттерн «фабрика». Вместо того, чтобы вызывать конструктор напрямую, вы вызываете метод объекта-фабрики, который предоставляет реализацию интерфейса — в этом случае программный код теоретически отделяется от реализации интерфейса, благодаря чему становится возможной совершенно прозрачная замена реализации. Следующий пример демонстрирует типичную структуру фабрики:
//: interfaces/Factories.java
import static net.mindview.util.Print.*;
interface Service { void methodic); void method2;
}
interface ServiceFactory { Service getServiceO;
}
class Implementationl implements Service {
ImplementationlO {} // Доступ в пределах пакета public void methodic) {print("Implementationl methodl");}
public void method2 {print("Implementationl method2");} } • . .
class ImplementationlFactory implements ServiceFactory { public Service getServiceO {
return new ImplementationlO;
}
}
class Implementation2 implements Service {
Implementation2 {} // Доступ в пределах пакета publ/ic void methodlО {print("Implementation2 methodl");} public void method2 {print("Implementation2 method2");}
}
class Implementation2Factory implements ServiceFactory { public Service getService.O {
return new Implementation2;
}
}
public class Factories {
public static void serviceConsumer(ServiceFactory fact) { Service s = fact.getServiceO;
s methodic). s.method2;
}
public static void main(String[] args) {
serviceConsumer(new ImplementationlFactoryO); // Реализации полностью взаимозаменяемы serviceConsumec(new Implementation2FactoryO);
}
} /* Output. Implementation! methodl Implementationl method2 Implementation2 methodl Implementation2 method2 *///:-
Без применения фабрики вам пришлось бы где-то указать точный тип создаваемого объекта Service, чтобы он мог вызвать подходящий конструктор.
Но зачем вводить лишний уровень абстракции? Данный паттерн часто применяется при создании библиотек. Допустим, вы создаете систему для игр, которая позволяла бы играть в шашки и шахматы на одной доске:
//: interfaces/Games.java
// Игровая библиотека с использованием фабрики
import static net.mindview.util.Print.*:
interface Game { boolean moveO; } interface GameFactory { Game getGameO; }
class Checkers implements Game { private int moves = 0: private static final int MOVES = 3: public boolean motfeO {
print("Checkers move " + moves): return ++moves != MOVES;
}
}
class CheckersFactory implements GameFactory {
public Game getGameO { return new CheckersO; }
}
class Chess implements Game { private int moves = 0; private static final int MOVES = 4; public boolean moveO {
print("Chess move " + moves); return ++moves != MOVES;
}
}
class ChessFactory implements GameFactory {
public Game getGameO { return new ChessO; }