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;
}
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 представляет сложный блок кода, такое решение позволит повторно использовать его для разных типов игр.
В следующей главе будет представлен более элегантный способ реализации фабрик на базе анонимных внутренних классов.
Резюме
После первого знакомства интерфейсы выглядят так хорошо, что может показаться, будто им всегда следует отдавать предпочтение перед реальными классами. Конечно, в любой ситуации, когда вы создаете класс, вместо него можно создать интерфейс и фабрику.
Многие программисты поддались этому искушению. Тем не менее любая абстракция должна быть мотивирована реальной потребностью. Основное назначение интерфейсов — возможность переработки реализации в случае необходимости, а не введение лишнего уровня абстракции вместе с дополнительными сложностями. Дополнительные сложности могут оказаться довольно существенными. А представьте, что кто-то будет вынужден разбираться в вашем коде и в конечном итоге поймет, что интерфейсы были добавлены «на всякий случай», без веских причин — в таких ситуациях все проектирование, которое выполнялось данным разработчиком, начинает выглядеть довольно сомнительно.
В общем случае рекомендуется
Внутренние (j
классы кМэ vv
Определение класса может размещаться внутри определения другого класса. Такие классы называются
Внутренние классы весьма полезны, так как они позволяют группировать классы, логически принадлежащие друг другу, и управлять доступом к ним. Однако следует понимать, что внутренние классы заметно отличаются от композиции.
На первый взгляд создается впечатление, что внутренние классы представляют собой простой механизм сокрытия кода. Однако вскоре вы узнаете, что возможности внутренних классов гораздо шире (они знают о существовании внешних классов и могут работать с ними), а программный код с внутренними классами часто бывает более элегантным и понятным (хотя конечно, этого никто не гарантирует).
В этой главе подробно исследуется синтаксис внутренних классов. Эти возможности представлены для полноты материала, хотя, скорее всего, на первых порах они вам не понадобятся. Возможно, начальные разделы этой главы содержат все, что вам действительно необходимо знать на этой стадии, а к более подробным объяснениям можно относиться как к справочному, дополнительному материалу.
Создание внутренних классов
Внутренние классы создаются в точности так, как и следовало ожидать, — определение класса размещается внутри окружающего класса:
//: innerclasses/Parcel 1.java // Создание внутренних классов.
public class Parcel 1 {
class Contents {
private int i = 11,
public int valueO { return i; }
}
class Destination {
private String label, DestinationCString whereTo) { label = whereTo;
}
String readLabeK) { return label; }
}
// Использование внутренних классов имеет много общего // с использованием любых других классов в пределах Parcel 1: public void shipCString dest) {