-
-
Notifications
You must be signed in to change notification settings - Fork 277
Expand file tree
/
Copy pathfactory.texy
More file actions
226 lines (170 loc) · 7.01 KB
/
factory.texy
File metadata and controls
226 lines (170 loc) · 7.01 KB
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
Generierte Fabriken
*******************
.[perex]
Nette DI kann automatisch Code für Fabriken basierend auf Schnittstellen generieren, was Ihnen das Schreiben von Code erspart.
Eine Fabrik ist eine Klasse, die Objekte herstellt und konfiguriert. Sie übergibt ihnen also auch ihre Abhängigkeiten. Bitte verwechseln Sie dies nicht mit dem Entwurfsmuster *Factory Method*, das eine spezifische Art der Verwendung von Fabriken beschreibt und mit diesem Thema nichts zu tun hat.
Wie eine solche Fabrik aussieht, haben wir im [Einführungskapitel |introduction#Fabrik] gezeigt:
```php
class ArticleFactory
{
public function __construct(
private Nette\Database\Connection $db,
) {
}
public function create(): Article
{
return new Article($this->db);
}
}
```
Nette DI kann den Code von Fabriken automatisch generieren. Alles, was Sie tun müssen, ist, eine Schnittstelle zu erstellen, und Nette DI generiert die Implementierung. 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 `Article`-Objekte erstellt. Die Klasse `Article` könnte beispielsweise so aussehen:
```php
class Article
{
public function __construct(
private Nette\Database\Connection $db,
) {
}
}
```
Wir fügen die Fabrik zur Konfigurationsdatei hinzu:
```neon
services:
- ArticleFactory
```
Nette DI generiert die entsprechende Implementierung der Fabrik.
Im Code, der die Fabrik verwendet, fordern wir das Objekt über die Schnittstelle an, und Nette DI verwendet die generierte Implementierung:
```php
class UserController
{
public function __construct(
private ArticleFactory $articleFactory,
) {
}
public function foo()
{
// lassen wir die Fabrik das Objekt erstellen
$article = $this->articleFactory->create();
}
}
```
Parametrisierte Fabrik
======================
Die Fabrikmethode `create` kann Parameter annehmen, die sie dann an den Konstruktor weitergibt. Ergänzen wir beispielsweise die Klasse `Article` um die ID des Artikelautors:
```php
class Article
{
public function __construct(
private Nette\Database\Connection $db,
private int $authorId,
) {
}
}
```
Wir fügen den Parameter auch zur Fabrik hinzu:
```php
interface ArticleFactory
{
function create(int $authorId): Article;
}
```
Da der Parameter im Konstruktor und der Parameter in der Fabrik denselben Namen haben, übergibt Nette DI sie vollautomatisch.
Erweiterte Definition
=====================
Die Definition kann auch in mehrzeiliger Form unter Verwendung des Schlüssels `implement` geschrieben werden:
```neon
services:
articleFactory:
implement: ArticleFactory
```
Bei dieser längeren Schreibweise können zusätzliche Argumente für den Konstruktor im Schlüssel `arguments` und zusätzliche Konfigurationen mittels `setup` angegeben werden, genau wie bei regulären Diensten.
Beispiel: Wenn die Methode `create()` den Parameter `$authorId` nicht akzeptieren würde, könnten wir einen festen Wert in der Konfiguration angeben, der an den Konstruktor von `Article` übergeben würde:
```neon
services:
articleFactory:
implement: ArticleFactory
arguments:
authorId: 123
```
Oder umgekehrt, wenn `create()` den Parameter `$authorId` akzeptieren würde, aber er nicht Teil des Konstruktors wäre und über die Methode `Article::setAuthorId()` übergeben würde, würden wir im Abschnitt `setup` darauf verweisen:
```neon
services:
articleFactory:
implement: ArticleFactory
setup:
- setAuthorId($authorId)
```
Accessor
========
Nette kann neben Fabriken auch sogenannte Accessoren generieren. Dies sind Objekte mit einer `get()`-Methode, die einen bestimmten Dienst aus dem DI-Container zurückgibt. Wiederholte Aufrufe von `get()` geben immer dieselbe Instanz zurück.
Accessoren ermöglichen Lazy-Loading für Abhängigkeiten. Nehmen wir an, wir haben eine Klasse, die Fehler in eine spezielle Datenbank schreibt. Wenn diese Klasse die Datenbankverbindung als Abhängigkeit im Konstruktor übergeben bekäme, müsste die Verbindung immer erstellt werden, obwohl in der Praxis ein Fehler nur selten auftritt und die Verbindung daher meist ungenutzt bliebe. Stattdessen übergibt sich die Klasse einen Accessor, und erst wenn dessen `get()` aufgerufen wird, wird das Datenbankobjekt erstellt:
Wie erstellt man einen Accessor? Schreiben Sie einfach eine Schnittstelle, und Nette DI generiert die Implementierung. Die Schnittstelle muss genau eine Methode namens `get` haben und einen Rückgabetyp deklarieren:
```php
interface PDOAccessor
{
function get(): PDO;
}
```
Wir fügen den Accessor zur Konfigurationsdatei hinzu, wo auch die Definition des Dienstes steht, den er zurückgeben wird:
```neon
services:
- PDOAccessor
- PDO(%dsn%, %user%, %password%)
```
Da der Accessor einen Dienst vom Typ `PDO` zurückgibt und in der Konfiguration nur ein solcher Dienst vorhanden ist, wird er genau diesen zurückgeben. Wenn es mehrere Dienste dieses Typs gäbe, würden wir den zurückgegebenen Dienst anhand seines Namens bestimmen, z. B. `- PDOAccessor(@db1)`.
Mehrfachfabrik/-accessor
========================
Unsere Fabriken und Accessoren konnten bisher immer nur ein Objekt herstellen oder zurückgeben. Es ist jedoch sehr einfach, auch Mehrfachfabriken in Kombination mit Accessoren zu erstellen. Die Schnittstelle einer solchen Klasse enthält eine beliebige Anzahl von Methoden mit den Namen `create<name>()` und `get<name>()`, z. B.:
```php
interface MultiFactory
{
function createArticle(): Article;
function getDb(): PDO;
}
```
Anstatt also mehrere generierte Fabriken und Accessoren zu übergeben, übergeben wir eine komplexere Fabrik, die mehr kann.
Alternativ kann anstelle mehrerer Methoden `get()` mit einem Parameter verwendet werden:
```php
interface MultiFactoryAlt
{
function get($name): PDO;
}
```
Dann gilt, dass `MultiFactory::getArticle()` dasselbe tut wie `MultiFactoryAlt::get('article')`. Die alternative Schreibweise hat jedoch den Nachteil, dass nicht ersichtlich ist, welche Werte für `$name` unterstützt werden, und logischerweise können in der Schnittstelle auch keine unterschiedlichen Rückgabewerte für verschiedene `$name` unterschieden werden.
Definition durch Liste
----------------------
Auf diese Weise kann eine Mehrfachfabrik in der Konfiguration definiert werden: .{data-version:3.2.0}
```neon
services:
- MultiFactory(
article: Article # definiert createArticle()
db: PDO(%dsn%, %user%, %password%) # definiert getDb()
)
```
Oder wir können uns in der Fabrikdefinition mittels Referenz auf bestehende Dienste beziehen:
```neon
services:
article: Article
- PDO(%dsn%, %user%, %password%)
- MultiFactory(
article: @article # definiert createArticle()
db: @\PDO # definiert getDb()
)
```
Definition mittels Tags
-----------------------
Die zweite Möglichkeit ist, zur Definition [Tags |services#Tags] zu verwenden:
```neon
services:
- App\Core\RouterFactory::createRouter
- App\Model\DatabaseAccessor(
db1: @database.db1.explorer
)
```