24

24


Пример 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; }

}

public class Games {

public static void playGame(GameFactory factory) {

Game s = factory.getGameO;продолжение &while(s.moveO)

}

public static void main(String[] args). { playGame(new CheckersFactoryO); playGame(new ChessFactoryO);

}

} /* Output: Checkers move 0 Checkers move 1 Checkers move 2 Chess move 0 Chess move 1 Chess move 2 Chess move 3 *///:-

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

В следующей главе будет представлен более элегантный способ реализации фабрик на базе анонимных внутренних классов.

Резюме

После первого знакомства интерфейсы выглядят так хорошо, что может пока­заться, будто им всегда следует отдавать предпочтение перед реальными клас­сами. Конечно, в любой ситуации, когда вы создаете класс, вместо него можно создать интерфейс и фабрику.

Многие программисты поддались этому искушению. Тем не менее любая аб­стракция должна быть мотивирована реальной потребностью. Основное назна­чение интерфейсов — возможность переработки реализации в случае необходи­мости, а не введение лишнего уровня абстракции вместе с дополнительными сложностями. Дополнительные сложности могут оказаться довольно сущест­венными. А представьте, что кто-то будет вынужден разбираться в вашем коде и в конечном итоге поймет, что интерфейсы были добавлены «на всякий слу­чай», без веских причин — в таких ситуациях все проектирование, которое вы­полнялось данным разработчиком, начинает выглядеть довольно сомнительно.

В общем случае рекомендуетсяотдавать предпочтение классам перед ин­терфейсами.Начните с классов, а если необходимость интерфейсов станет оче­видной — переработайте архитектуру своего проекта. Интерфейсы — замеча­тельный инструмент, но ими нередко злоупотребляют.



Внутренние классы

О
пределение класса может размещаться внутри определения другого класса. Такие классы называютсявнутренними(inner class).

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

На первый взгляд создается впечатление, что внутренние классы представ­ляют собой простой механизм сокрытия кода. Однако вскоре вы узнаете, что возможности внутренних классов гораздо шире (они знают о существовании внешних классов и могут работать с ними), а программный код с внутренними классами часто бывает более элегантным и понятным (хотя конечно, этого ни­кто не гарантирует).

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