-
-
Notifications
You must be signed in to change notification settings - Fork 282
/
factory.texy
227 lines (171 loc) · 7.32 KB
/
factory.texy
1
2
3
4
5
6
7
8
9
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
Generierte Fabriken
*******************
.[perex]
Nette DI kann automatisch Fabrikcode basierend auf der Schnittstelle generieren, was Ihnen das Schreiben von Code erspart.
Eine Fabrik ist eine Klasse, die Objekte erstellt und konfiguriert. Sie gibt daher auch deren Abhängigkeiten an sie weiter. Bitte nicht mit dem Entwurfsmuster *Fabrikmethode* verwechseln, das eine spezielle Art der Verwendung von Fabriken beschreibt und nicht mit diesem Thema zusammenhängt.
Wir haben im [Einführungskapitel |introduction#factory] gezeigt, wie eine solche Fabrik aussieht:
```php
class ArticleFactory
{
public function __construct(
private Nette\Database\Connection $db,
) {
}
public function create(): Article
{
return new Article($this->db);
}
}
```
Nette DI kann automatisch Factory-Code generieren. Alles, was Sie tun müssen, ist, eine Schnittstelle zu erstellen, und Nette DI wird eine Implementierung generieren. Die Schnittstelle muss genau eine Methode namens `create` haben und einen Rückgabetyp deklarieren:
```php
interface ArticleFactory
{
function create(): Article;
}
```
Die Fabrik `ArticleFactory` hat also eine Methode `create`, die Objekte `Article` erzeugt. Die Klasse `Article` könnte z.B. wie folgt aussehen:
```php
class Article
{
public function __construct(
private Nette\Database\Connection $db,
) {
}
}
```
Fügen Sie die Fabrik in die Konfigurationsdatei ein:
```neon
services:
- ArticleFactory
```
Nette DI wird die entsprechende Fabrikimplementierung erzeugen.
Im Code, der die Fabrik verwendet, fordern wir das Objekt also über die Schnittstelle an, und Nette DI verwendet die generierte Implementierung:
```php
class UserController
{
public function __construct(
private ArticleFactory $articleFactory,
) {
}
public function foo()
{
// Die Fabrik soll ein Objekt erstellen
$article = $this->articleFactory->create();
}
}
```
Parametrisierte Fabrik .[#toc-parameterized-factory]
====================================================
Die Factory-Methode `create` kann Parameter akzeptieren, die sie dann an den Konstruktor weitergibt. Fügen wir zum Beispiel der Klasse `Article` eine Artikelautor-ID hinzu:
```php
class Article
{
public function __construct(
private Nette\Database\Connection $db,
private int $authorId,
) {
}
}
```
Wir fügen auch den Parameter zur Fabrik hinzu:
```php
interface ArticleFactory
{
function create(int $authorId): Article;
}
```
Da der Parameter im Konstruktor und der Parameter in der Fabrik den gleichen Namen haben, werden sie von Nette DI automatisch übergeben.
Erweiterte Definition .[#toc-advanced-definition]
=================================================
Die Definition kann auch in mehrzeiliger Form mit der Taste `implement` geschrieben werden:
```neon
services:
articleFactory:
implement: ArticleFactory
```
Beim Schreiben in dieser längeren Form ist es möglich, zusätzliche Argumente für den Konstruktor im Schlüssel `arguments` und zusätzliche Konfigurationen mit `setup` anzugeben, genau wie bei normalen Diensten.
Beispiel: Wenn die Methode `create()` den Parameter `$authorId` nicht akzeptiert, könnte man in der Konfiguration einen festen Wert angeben, der an den Konstruktor `Article` übergeben wird:
```neon
services:
articleFactory:
implement: ArticleFactory
arguments:
authorId: 123
```
Oder umgekehrt, wenn `create()` den Parameter `$authorId` akzeptiert, dieser aber nicht Teil des Konstruktors ist, sondern von der Methode `Article::setAuthorId()` übergeben wird, würden wir in Abschnitt `setup` auf ihn verweisen:
```neon
services:
articleFactory:
implement: ArticleFactory
setup:
- setAuthorId($authorId)
```
Accessor .[#toc-accessor]
=========================
Neben Fabriken kann Nette auch so genannte Accessoren erzeugen. Ein Accessor ist ein Objekt mit der Methode `get()`, die einen bestimmten Dienst aus dem DI-Container zurückgibt. Mehrere `get()` Aufrufe geben immer dieselbe Instanz zurück.
Accessoren bringen Lazy-Loading in Abhängigkeiten. Nehmen wir an, eine Klasse protokolliert Fehler in einer speziellen Datenbank. Wenn die Datenbankverbindung als Abhängigkeit in ihrem Konstruktor übergeben würde, müsste die Verbindung immer erstellt werden, obwohl sie nur selten verwendet würde, wenn ein Fehler auftritt, so dass die Verbindung meist ungenutzt bliebe.
Stattdessen kann die Klasse einen Accessor übergeben, und wenn ihre Methode `get()` aufgerufen wird, wird erst dann das Datenbankobjekt erstellt:
Wie erstellt man einen Accessor? Schreiben Sie nur eine Schnittstelle und Nette DI wird die Implementierung generieren. Die Schnittstelle muss genau eine Methode namens `get` haben und den Rückgabetyp deklarieren:
```php
interface PDOAccessor
{
function get(): PDO;
}
```
Fügen Sie den Accessor in die Konfigurationsdatei ein, zusammen mit der Definition des Dienstes, den der Accessor zurückgibt:
```neon
services:
- PDOAccessor
- PDO(%dsn%, %user%, %password%)
```
Der Accessor gibt einen Dienst des Typs `PDO` zurück, und da es nur einen solchen Dienst in der Konfiguration gibt, gibt der Accessor ihn zurück. Bei mehreren konfigurierten Diensten dieses Typs können Sie angeben, welcher Dienst zurückgegeben werden soll, indem Sie seinen Namen verwenden, zum Beispiel `- PDOAccessor(@db1)`.
Multifactory/Accessor .[#toc-multifactory-accessor]
===================================================
Bisher konnten die Fabriken und Accessoren nur ein einziges Objekt erstellen oder zurückgeben. Es kann auch eine Multifactory in Kombination mit einem Accessor erstellt werden. Die Schnittstelle einer solchen Multifactory-Klasse kann aus mehreren Methoden bestehen, die `create<name>()` und `get<name>()`bestehen, zum Beispiel:
```php
interface MultiFactory
{
function createArticle(): Article;
function getDb(): PDO;
}
```
Anstatt mehrere generierte Fabriken und Accessoren zu übergeben, können Sie nur eine komplexe Multifabrik übergeben.
Alternativ können Sie auch `get()` mit einem Parameter anstelle von mehreren Methoden verwenden:
```php
interface MultiFactoryAlt
{
function get($name): PDO;
}
```
In diesem Fall bewirkt `MultiFactory::getArticle()` dasselbe wie `MultiFactoryAlt::get('article')`. Die alternative Syntax hat jedoch einige Nachteile. Es ist nicht klar, welche `$name` Werte unterstützt werden, und der Rückgabetyp kann nicht in der Schnittstelle angegeben werden, wenn mehrere verschiedene `$name` Werte verwendet werden.
Definition mit einer Liste .[#toc-definition-with-a-list]
---------------------------------------------------------
Auf diese Weise können Sie in der Konfiguration eine Mehrfachfabrik definieren: .{data-version:3.2.0}
```neon
services:
- MultiFactory(
article: Article # defines createArticle()
db: PDO(%dsn%, %user%, %password%) # defines getDb()
)
```
Oder wir können in der Fabrikdefinition auf bestehende Dienste mit einer Referenz verweisen:
```neon
services:
article: Article
- PDO(%dsn%, %user%, %password%)
- MultiFactory(
article: @article # defines createArticle()
db: @\PDO # defines getDb()
)
```
Definition mit Tags .[#toc-definition-with-tags]
------------------------------------------------
Eine weitere Möglichkeit, eine Multifabrik zu definieren, ist die Verwendung von [Tags |services#Tags]:
```neon
services:
- App\Core\RouterFactory::createRouter
- App\Model\DatabaseAccessor(
db1: @database.db1.explorer
)
```