[Из песочницы] Абстрактная фабрика на пальцах
Написать данную статью меня заставили две причины. Совсем недавно я познакомился с паттерном Абстрактная фабрика. Как говорится — «Не умеешь сам, научи товарища». Известно, что один из лучших способов закрепления материала — это объяснение кому-либо ранее изученного. Вторая причина — в процессе изучения данного паттерна я не встретил материала, который лично для меня излагал бы вполне ясно суть Абстрактной фабрики (по крайней мере на Хабре).
Итак, приступим. Самый первый вопрос, на который нужно ответить самому себе, изучая данный паттерн: «Что же такое Абстрактная фабрика». Самый простой и точный ответ, гласит, что Абстрактная фабрика — это «фабрика фабрик». Но здесь появляется второй вопрос: «Для чего вообще может кому-нибудь понадобиться «фабрика фабрик»? Чтобы на него ответить рассмотрим пример из реальной жизни.
Допустим, вы решили полностью взять под свой контроль рынок автомобилей. Как это сделать? Вы можете создать свою марку автомобиля, своё производство, провести масштабную рекламную компанию и т.д. Но, в этом случае вам придётся сражаться с такими гигантами авторынка, как Toyota или Ford. Не факт, что из данной борьбы вы выйдите победителем. Гораздо лучшим решением будет скупить заводы всех этих компаний, продолжить выпускать автомобили под их собственными марками, а прибыль класть себе в карман. Если я не ошибаюсь, такая структура в экономике называется — холдинг. Вот этот холдинг и будет Абстрактной фабрикой или «фабрикой фабрик». В нашей программе Абстрактная фабрика (холдинг) будет представлена интерфейсом или абстрактным классом. Предприятия, входящие в холдинг, представлены классами, реализующими данный интерфейс.
public interface CarsFabric { }
public class ToyotaFabric implements CarsFabric {}
public class FordFabric implements CarsFabric {}
Далее, вы собираете управляющих вашими фабриками и говорите: «Отныне на наших фабриках мы будем делать автомобили с 2 типами кузова — седан и купе. Например, японцы будут делать ToyotaSedan и ToyotaCoupe, американцы — FordSedan и FordCoupe». А чтобы на фабриках не забыли, что именно нужно производить, и не начали делать, например, внедорожники, повесим общие чертежи седана и купе в офисе нашего холдинга на самом видном месте (на конкретной фабрике инженеры сами разберутся как именно им делать нужные автомобили). Таким образом, в нашем интерфейсе CarsFabric появляются 2 метода:
public interface CarsFabric {
Sedan createSedan();
Coupe createCoupe();
}
Соответственно, в дочерних классах интерфейса CarsFabric, данные методы тоже должны быть реализованы.
public class ToyotaFabric implements CarsFabric {
@Override
public Sedan createSedan() {
return new ToyotaSedan();
}
@Override
public Coupe createCoupe() {
return new ToyotaCoupe();
}
}
public class FordFabric implements CarsFabric {
@Override
public Sedan createSedan() {
return new FordSedan();
}
@Override
public Coupe createCoupe() {
return new FordCoupe();
}
}
Обратите внимание, что типом возвращаемого значения в методах будет являться именно общий для возвращаемых значений тип — sedan и coupe. Возвращаясь к нашей аналогии — вы сказали фабрике сделать седан — вам сделали седан. Особенности, например, седана марки Ford, вас не интересуют.
Как несложно догадаться из приведённого кода, у нас в программе должны появится некие сущности, описывающие конкретные типы кузова — седан и купе. Такими сущностями будут интерфейсы.
public interface Sedan {}
public interface Coupe {}
Ну и конечно же, данные чертежи должны иметь конкретное воплощение в виде автомобилей, создаваемых на той или иной фабрике.
public class ToyotaCoupe implements Coupe {
public ToyotaCoupe() {
System.out.println("Create ToyotaCoupe");
}
}
public class ToyotaSedan implements Sedan {
public ToyotaSedan() {
System.out.println("Create ToyotaSedan");
}
}
public class FordCoupe implements Coupe {
public FordCoupe () {
System.out.println("Create FordCoupe");
}
}
public class FordSedan implements Sedan {
public FordSedan() {
System.out.println("Create FordSedan");
}
}
Вот и всё, наша «фабрика фабрик» способная производить автомобили любой марки и любого типа, готова. В будущем вы можете решить, что было бы неплохо начать выпускать внедорожники. Вам нужно будет создать ещё один интерфейс, а в офисе холдинга повесить чертёж внедорожника (добавить в CarsFabric нужный метод и реализовать его в дочерних фабриках). Так же возможно, что вы решите захватить ещё один кусок рынка и купить, например, все заводы Nissan. Это означает, что вам нужно создать ещё один класс, реализующий CarsFabric — NissanFabric, и начать выпускать ваши автомобили под этой маркой (NissanCoupe, NissanSedan и т.д.)
Но как будет взаимодействовать конкретный пользователь (покупатель автомобиля) с нашим холдингом? Покупатель вообще знать не знает о том, что вы захватили все фабрики мира по производству автомобилей. Он приходит в маленький скромный офис холдинга и говорит: «Мне нужен автомобиль!» «Отлично!» — говорим мы ему, — «вы обратились по адресу! Фабрика фабрик — это то, что вам нужно!»
CarsFabric fabric;
«Автомобили какой фирмы предпочитаете в данное время суток?», — спрашиваем мы. Допустим, покупатель хочет приобрести тойоту. Нет проблем!
fabric = new ToyotaFabric();
«А какой тип кузова вы бы хотели?» Допустим — седан. «Прекрасный выбор!»
fabric.createSedan();
Автомобиль готов, можно ехать!