Типичным механизмом создания объектов, реализуемых посредством интерфейсов, является паттерн (шаблон) проектирования "Фабричный метод".
Суть паттерна практически полностью описывается его названием. Когда вам требуется получать какие-то объекты, например пакеты сока, вам совершенно не нужно знать как их делают на фабрике. Вы просто говорите «сделайте мне пакет апельсинового сока», а «фабрика» возвращает вам требуемый пакет. Как? Всё это решает сама фабрика, например «копирует» уже существующий эталон. Основное предназначение «фабрики» в том, чтобы можно было при необходимости изменять процесс «появления» пакета сока, а самому потребителю ничего об этом не нужно было сообщать, чтобы он запрашивал его как и прежде.
Как правило, одна фабрика занимается «производством» только одного рода «продуктов». Не рекомендуется «фабрику соков» создавать с учетом производства автомобильных покрышек. Как и в жизни, паттерн «фабрика» часто создается «одиночкой».
Фабричные методы избавляют проектировщиков от необходимости встраивать в код зависящие от приложения классы.
Ну и чтобы все стало более-менее понятно попрактикуемся…
В первом примере мы будем выпускать сервисы как продукт. И у нас будет фабрика которая выпускает эти продукты. Сервис и фабрика будут описаны как интерфейсы. И так же мы создадим по паре реализаций для сервиса и фабрики сервисов. Пример взят из книги "Философия Java". Правда я его немного изменил, чтобы он стал чуть более понятным на мой взгляд.
package pro.java.factory1; | |
import static pro.java.util.Print.*; | |
// Пример фабричных методов. Делаем фабрику сервисов | |
// это то, что будет выпускать наша фабрика, это продукт | |
interface Service { | |
void method1(); | |
void method2(); | |
} | |
// а это наша фабрика сервисов | |
interface ServiceFactory { | |
Service getService(); // фабричный метод | |
Service getService(String name); // фабричный метод | |
} | |
// это первая имплементация "продукта" сервиса | |
class Implementation1 implements Service { | |
String serviceName; | |
public Implementation1() { | |
serviceName = "NoNaMe1"; | |
} | |
public Implementation1(String name) { | |
serviceName = name; | |
} | |
@Override | |
public void method1() { | |
println("Implementation1 method1"); | |
} | |
@Override | |
public void method2() { | |
println("Implementation1 method2"); | |
} | |
public String getName() { | |
return serviceName; | |
} | |
} | |
// это первая имплементация фабрики | |
class Implementation1Factory implements ServiceFactory { | |
String serviceName; | |
public Implementation1Factory() { | |
serviceName = "NoName1F"; | |
} | |
public Implementation1Factory(String name) { | |
serviceName = name; | |
} | |
// метод возвращающий реальный продукт Service | |
@Override | |
public Service getService() { | |
return new Implementation1(serviceName); | |
} | |
@Override | |
public Service getService(String name) { | |
return new Implementation1(name); | |
} | |
} | |
// это вторая имплементация "продукта" сервиса | |
class Implementation2 implements Service { | |
String serviceName; | |
public Implementation2() { | |
serviceName = "NoNaMe2"; | |
} | |
public Implementation2(String name) { | |
serviceName = name; | |
} | |
@Override | |
public void method1() { | |
println("Implementation2 method1"); | |
} | |
@Override | |
public void method2() { | |
println("Implementation2 method2"); | |
} | |
public String getName() { | |
return serviceName; | |
} | |
} | |
// это вторая имплементация фабрики | |
class Implementation2Factory implements ServiceFactory { | |
String serviceName; | |
public Implementation2Factory() { | |
serviceName = "NoName2F"; | |
} | |
public Implementation2Factory(String name) { | |
serviceName = name; | |
} | |
// метод возвращающий реальный продукт Service | |
@Override | |
public Service getService() { | |
return new Implementation2(serviceName); | |
} | |
@Override | |
public Service getService(String name) { | |
return new Implementation2(name); | |
} | |
} | |
public class Factories { | |
public static void main(String[] args) { | |
// выпускаем наши сервисы с помощью разных фабрик | |
serviceConsumer(new Implementation1Factory("Service1")); | |
println(); | |
serviceConsumer(new Implementation2Factory("Service2")); | |
} | |
static void serviceConsumer(ServiceFactory fact) { | |
Service s = fact.getService(); | |
s.method1(); | |
s.method2(); | |
if (s instanceof Implementation1) { | |
Implementation1 i1 = (Implementation1) s; | |
println(i1.getName()); | |
} | |
if (s instanceof Implementation2) { | |
Implementation2 i2 = (Implementation2) s; | |
println(i2.getName()); | |
} | |
} | |
} |
Вывод у программы следующий:
Все эти строки выводятся статическим методом serviceConsumer(), в который я так же добавил код приведения типов, причем очень интересного приведения, когда интерфейсная ссылка приводится к ссылке на объект класса реализующего этот интерфейс. Это делается в блоках if метода serviceConsumer().
Так же я добавил имя сервиса в код имплементации сервиса, а метод получения этого имени описан в классе, а не в интерфейсе.
И еще один примерчик из той же книги…
В нем создаются имплементации игр шашки и шахматы. Ну как бы шашки и шахматы :) Это же пример…