Статьи

Шаблоны проектирования: шаблон фабричного метода

В предыдущей статье мы рассмотрели простой шаблон фабрики . Теперь в этой статье мы рассмотрим шаблон Factory Method Design. В случае Simple Factory он предоставляет интерфейс для создания объектов, в то время как метод Factory делает то же самое, но в дополнение к этому позволяет подклассу принимать решение о том, какой класс создавать.

Для объяснения шаблона Simple Factory Method я использовал пример создания объекта автомобиля с различными типами автомобилей, такими как седан, внедорожник и т. Д. Я продолжу тот же пример, чтобы объяснить шаблон Factory Method, чтобы у нас получилось лучше идея и преемственность.

На простой фабрике мы создавали объект класса автомобиля, но на самом деле он создавал объект класса автомобиля в зависимости от типа автомобиля, который мы проехали. Это был хороший шаг для начала создания объектов для автомобилей разных типов.

Ранее наша компания ограничивалась продажей автомобилей только в США, и у нее есть только один производственный центр в США. Но со временем компания расширяется, и она решает продавать автомобили в Великобритании и построить новый производственный центр в Великобритании.

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

В этом случае у нас есть два варианта: либо изменить текущий код (несколько, если еще условия), чтобы получить желаемый объект автомобиля, либо реструктурировать наши классы таким образом, чтобы не требовалось грязных условий if в вашем классе и в будущем, если Вы хотите добавить еще несколько функций, но только для ограниченных типов классов.

Первый подход никто не хотел бы использовать, потому что это своего рода пэчворк, который вы делаете в своем коде, а не реальная реализация. В следующем разделе мы увидим, как мы можем реализовать второй подход, используя шаблон проектирования Factory Method.

Этот шаблон проектирования относится к категории структурных шаблонов, поэтому он больше касается того, как вы структурируете свои классы. Итак, давайте перейдем к решению нашей проблемы, правильно структурировав наши классы.

На простой фабрике мы не использовали структуру, потому что нам нужен был только один класс, который отвечал за создание объекта автомобиля. Исходя из текущей проблемы, нам нужно создать отдельный класс фабрики для обоих производственных центров, поэтому мы хотим убедиться, что все наши фабрики следуют одному и тому же процессу для создания объекта класса. Следовательно, мы сделаем один интерфейс, который будут реализованы на фабрике в США и Великобритании.

1
2
3
interface carFactory {
    public function buildCar($type);
}

Теперь наш интерфейс для заводов готов, и пришло время создавать классы наших заводов, которые являются автомобильным заводом США и Великобритании.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// Factory class to build US based center
class USCarFactory implements carFactory {
    public function __construct() {
         
    }
     
    public function buildCar($type){
         
        $car = null;
         
        switch($type) {
            case ‘suv’:
                $car = new USSuvFactory();
                break;
            case ‘sedan’:
                $car = new USSedanFactory();
                break;
            default:
                $car = new USSedanFactory();
                break;
        }
         
        return $car;
    }
}
 
// Factory class to build UK based center
class UKCarFactory implements carFactory {
 
    public function __construct() {
         
    }
     
    public function buildCar($type){
 
        $car = null;
 
        switch($type) {
            case ‘suv’:
                $car = new UKSuvFactory();
                break;
            case ‘sedan’:
                $car = new UKSedanFactory();
                break;
            default:
                $car = new UKSedanFactory();
                break;
        }
 
        return $car;
    }
}

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

1
2
3
4
interface car {
    public function getLocation();
    public function getType();
}

Мы добавили в наш класс очень простые методы для определения местоположения и типа автомобиля. Теперь мы будем реализовывать наши конкретные классы, как показано ниже.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
class USSuvFactory implements car {
     
    private $location;
    private $carType;
     
    public function __construct() {
        $this->location = ‘US’;
        $this->carType = ‘SUV’;
    }
     
    public function getLocation() {
        return $this->location;
    }
     
    public function getType() {
        return $this->carType;
    }
     
}
 
class USSedanFactory implements car {
 
    private $location;
    private $carType;
     
    public function __construct() {
        $this->location = ‘US’;
        $this->carType = ‘Sedan’;
    }
     
    public function getLocation() {
        return $this->location;
    }
     
    public function getType() {
        return $this->carType;
    }
     
}
 
class UKSuvFactory implements car {
 
    private $location;
    private $carType;
     
    public function __construct() {
        $this->location = ‘UK’;
        $this->carType = ‘SUV’;
    }
     
    public function getLocation() {
        return $this->location;
    }
     
    public function getType() {
        return $this->carType;
    }
     
}
 
class UKSedanFactory implements car {
 
    private $location;
    private $carType;
     
    public function __construct() {
        $this->location = ‘UK’;
        $this->carType = ‘Sedan’;
    }
     
    public function getLocation() {
        return $this->location;
    }
     
    public function getType() {
        return $this->carType;
    }
     
}

Мы закончили с реализацией наших конкретных классов, которые на самом деле являются объектом автомобиля в зависимости от местоположения и типа автомобиля. Здесь осталось использовать эту реализацию, чтобы увидеть, как она решает нашу проблему, описанную выше.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
// US Car Factory
$USFactory = new USCarFactory();
 
// US based SUV model
$USSuv = $USFactory->buildCar(‘suv’);
echo $USSuv->getLocation().’
 
// US based Sedan model
$USSedan = $USFactory->buildCar(‘sedan’);
echo $USSedan->getLocation().’
 
// UK Car Factory
$UKFactory = new UKCarFactory();
 
// UK based SUV model
$UKSuv = $UKFactory->buildCar(‘suv’);
echo $UKSuv->getLocation().’
 
// UK based Sedan model
$UKSedan = $UKFactory->buildCar(‘sedan’);
echo $UKSedan->getLocation().’

Можно сказать, что мы эффективно создали объекты всевозможных типов автомобилей с локацией. Наши классы теперь хорошо структурированы, поэтому не составит труда добавить новую фабрику.

Давайте представим, что компания решила открыть новый производственный центр в Австралии.

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
// Factory class to build AUS based center
class AUSCarFactory implements carFactory {
    public function __construct() {
         
    }
     
    public function buildCar($type){
         
        $car = null;
         
        switch($type) {
            case ‘suv’:
                $car = new AUSSuvFactory();
                break;
            case ‘sedan’:
                $car = new AUSSedanFactory();
                break;
            default:
                $car = new AUSSedanFactory();
                break;
        }
         
        return $car;
    }
}
 
// Concrete classes for AUS based center
 
class AUSSuvFactory implements car {
 
    private $location;
    private $carType;
     
    public function __construct() {
        $this->location = ‘AUS’;
        $this->carType = ‘SUV’;
    }
     
    public function getLocation() {
        return $this->location;
    }
     
    public function getType() {
        return $this->carType;
    }
     
}
 
class AUSSedanFactory implements car {
 
    private $location;
    private $carType;
     
    public function __construct() {
        $this->location = ‘AUS’;
        $this->carType = ‘Sedan’;
    }
     
    public function getLocation() {
        return $this->location;
    }
     
    public function getType() {
        return $this->carType;
    }
     
}
 
// AUS Car Factory
$AUSFactory = new AUSCarFactory();
 
// AUS based SUV model
$AUSSuv = $AUSFactory->buildCar(‘suv’);
echo $AUSSuv->getLocation().’
 
// AUS based Sedan model
$AUSSedan = $AUSFactory->buildCar(‘sedan’);
echo $AUSSedan->getLocation().’

Итак, мы увидели, как мы можем структурировать наши классы таким образом, чтобы они могли быть расширены без каких-либо изменений в ваших базовых классах и клиентском коде. Не стесняйтесь добавлять свои комментарии в разделе комментариев ниже или пишите мне на @XpertDevelopers .