Для того, чтобы система оставалась
независимой от различных типов объектов,
паттерн «Фабричный метод» использует
механизм полиморфизма - классы всех
конечных типов наследуются от одного
абстрактного базового класса,
предназначенного для полиморфного
использования. В этом базовом классе
определяется единый интерфейс, через
который пользователь будет оперировать
объектами конечных типов.
Для обеспечения относительно простого
добавления в систему новых типов паттерн
локализует создание объектов конкретных
типов в специальном классе-фабрике.
Методы этого класса, посредством которых
создаются объекты конкретных классов,
называются фабричными. Существуют две
разновидности паттерна Factory Method:
1. Обобщенный
конструктор, когда в том же самом
полиморфном базовом классе, от которого
наследуют производные классы всех
создаваемых в системе типов, определяется
статический фабричный метод. В качестве
параметра в этот метод должен передаваться
идентификатор типа создаваемого
объекта.
2. Классический
вариант фабричного метода, когда
интерфейс фабричных методов объявляется
в независимом классе-фабрике, а их
реализация определяется конкретными
подклассами этого класса.
Где на диаграмме:
- Product — определяет интерфейс объектов, создаваемых абстрактным методом;
- ConcreteProduct — реализует интерфейс Product;
- Creator — создатель
- объявляет фабричный метод, который возвращает объект типа Product. Может также содержать реализацию этого метода «по умолчанию»;
- может вызывать фабричный метод для создания объекта типа Product;
- ConcreteCreator — переопределяет фабричный метод таким образом, чтобы он создавал и возвращал объект класса ConcreteProduct.
Рассмотрим примеры
обоих вариантов.
Обобщенный конструктор:
Обобщенный конструктор:
Boat.java
1: package ua.dp.allmycircuits.factorymethod;
2: /**
3: * Это лодка. Она плавает
4: * @author winkiller
5: */
6: public class Boat extends Vehicle{
7: public void info() {
8: System.out.println("Let's swim!");
9: }
10: }
Car.java1: package ua.dp.allmycircuits.factorymethod;
2: /**
3: * Это машина. Она ездит
4: * @author winkiller
5: */
6: public class Car extends Vehicle{
7: public void info() {
8: System.out.println("Let's drive!");
9: }
10: }
Plane.java1: package ua.dp.allmycircuits.factorymethod;
2: /**
3: * Это самолет. Он летает
4: * @author winkiller
5: */
6: public class Plane extends Vehicle{
7: public void info() {
8: System.out.println("Let's fly!");
9: }
10: }
1: package ua.dp.allmycircuits.factorymethod;
2: /**
3: * Базовый класс с фабричным методом
4: * @author winkiller
5: */
6: public class Vehicle {
7: /**
8: * Метод будет информировать нас о действиях экземпляра класса. У каждого потомка действие будет свое.
9: */
10: public void info(){
11: System.out.println("This is base class");
12: }
13: /**
14: * Фабричный метод
15: * @param type на вход передаем тип
16: * @return
17: */
18: public static Vehicle createVehicle(VehicleType type){
19: Vehicle ret;
20: // для пущего изврата, можно определение написать с помощью рефлексии.Можно и switch использовать, только учесть что type может быть null
21: if(type == VehicleType.BOAT){
22: ret = new Boat();
23: }else if(type == VehicleType.CAR){
24: ret = new Car();
25: }else if(type == VehicleType.PLANE){
26: ret = new Plane();
27: }else{
28: ret = new Vehicle();
29: }
30: return ret;
31: }
32: }
VehicleType.java1: package ua.dp.allmycircuits.factorymethod;
2: /**
3: * Перечисление возможных типов. Можно без него, но с ним надежнее))
4: * @author winkiller
5: */
6: public enum VehicleType {
7: PLANE,
8: CAR,
9: BOAT
10: }
FactoryMethod.java1: package ua.dp.allmycircuits.factorymethod;
2: import java.util.ArrayList;
3: import java.util.List;
4: /**
5: * Этот класс запускает все описанное безобразие
6: * @author winkiller
7: */
8: public class FactoryMethod {
9: /**
10: * @param args the command line arguments
11: */
12: public static void main(String[] args) {
13: //создаем список и заполняем его потомками базового класса Vehicle
14: List<Vehicle> vehicles = new ArrayList<Vehicle>();
15: vehicles.add(Vehicle.createVehicle(VehicleType.CAR));
16: vehicles.add(Vehicle.createVehicle(VehicleType.BOAT));
17: vehicles.add(Vehicle.createVehicle(VehicleType.PLANE));
18: vehicles.add(Vehicle.createVehicle(null));
19: //А теперь по очереди у каждого дергаем метод info()
20: for(Vehicle v: vehicles){
21: v.info();
22: }
23: }
24: }
С точки зрения "чистоты"
объектно-ориентированного кода у этого
варианта есть следующие недостатки:
- Так как код по созданию объектов всех возможных типов сосредоточен в статическом фабричном методе класса
Vehicle
, то базовый классVehicle
обладает знанием обо всех производных от него классах, что является нетипичным для объектно-ориентированного подхода. - Подобное использование оператора
if-else или будь
то switch
createVehicle()
) в объектно-ориентированном программировании также не приветствуется.
Указанные недостатки отсутствуют в
классической реализации паттерна
Factory Method.
Vehicle.java1: package ua.dp.allmycircuits.factorymethod;
2: /**
3: * Интерфейс, описывающий транспорт
4: * @author winkiller
5: */
6: public interface Vehicle {
7: /**
8: * Метод будет информировать нас о действиях реализаций этого интерфейса.
9: */
10: public void info();
11: }
VehicleFactory.java1: package ua.dp.allmycircuits.factorymethod;
2: /**
3: * Интерфейс, описывающий фабрики
4: * @author winkiller
5: */
6: public interface VehicleFactory {
7: public Vehicle createVehicle();
8: }
Boat.java1: package ua.dp.allmycircuits.factorymethod;
2: /**
3: * Это лодка. Она плавает
4: * @author winkiller
5: */
6: public class Boat implements Vehicle{
7: public void info() {
8: System.out.println("Let's swim!");
9: }
10: }
Car.java1: package ua.dp.allmycircuits.factorymethod;
2: /**
3: * Это машина. Она ездит
4: * @author winkiller
5: */
6: public class Car implements Vehicle{
7: public void info() {
8: System.out.println("Let's drive!");
9: }
10: }
Plane.java1: package ua.dp.allmycircuits.factorymethod;
2: /**
3: * Это самолет. Он летает
4: * @author winkiller
5: */
6: public class Plane implements Vehicle{
7: public void info() {
8: System.out.println("Let's fly!");
9: }
10: }
BoatFactory.java1: package ua.dp.allmycircuits.factorymethod;
2: /**
3: * Фабрика для лодок
4: * @author winkiller
5: */
6: public class BoatFactory implements VehicleFactory{
7: @Override
8: public Vehicle createVehicle() {
9: return new Boat();
10: }
11: }
CarFactory.java1: package ua.dp.allmycircuits.factorymethod;
2: /**
3: * Фабрика для машин
4: * @author winkiller
5: */
6: public class CarFactory implements VehicleFactory{
7: @Override
8: public Vehicle createVehicle() {
9: return new Car();
10: }
11: }
PlaneFactory.java1: package ua.dp.allmycircuits.factorymethod;
2: /**
3: * Фабрика для самолетов
4: * @author winkiller
5: */
6: public class PlaneFactory implements VehicleFactory{
7: @Override
8: public Vehicle createVehicle() {
9: return new Plane();
10: }
11: }
FactoryMethodClassic.java1: package ua.dp.allmycircuits.factorymethod;
2: import java.util.ArrayList;
3: import java.util.List;
4: /**
5: *
6: * @author winkiller
7: */
8: public class FactoryMethodClassic {
9: /**
10: * @param args the command line arguments
11: */
12: public static void main(String[] args) {
13: //инициируем фабрики. Можно в цикле инициировать. Боже, храни полиморфизм!
14: BoatFactory bf = new BoatFactory();
15: CarFactory cf = new CarFactory();
16: PlaneFactory pf = new PlaneFactory();
17: //создаем список и заполняем его потомками базового класса Vehicle
18: List<Vehicle> vehicles = new ArrayList<Vehicle>();
19: vehicles.add(bf.createVehicle());
20: vehicles.add(cf.createVehicle());
21: vehicles.add(pf.createVehicle());
22: //А теперь по очереди у каждого дергаем метод info()
23: for(Vehicle v: vehicles){
24: v.info();
25: }
26: }
27: }
Классический вариант
паттерна Factory Method использует идею
полиморфной фабрики. Специально
выделенный для создания объектов
полиморфный интерфейс Vehicle
Fa
ctory
объявляет интерфейс фабричного метода
createVehicle()
, а производные
классы его реализуют.
Один из примеров использования в Java это LogManager.getLogManager(), Calendar.getInstance(), etc, которые используют фабричный метод чтобы создать инстанс соответствующего класса
Достоинства паттерна Factory Method
- Создает объекты разных типов, позволяя системе оставаться независимой как от самого процесса создания, так и от типов создаваемых объектов.
Недостатки паттерна Factory Method
- В случае классического варианта паттерна даже для порождения единственного объекта необходимо создавать соответствующую фабрику
Очень доступное оъбяснение, спасибо.
ОтветитьУдалитьПожалуйста. Рад был помочь.
Удалить