Ce face factory
Un constructor obișnuit creează mereu un obiect nou al clasei lui. Un constructor factory este mai liber: are un corp ca o funcție și trebuie să întoarcă un obiect, care poate fi unul deja existent (dintr-un cache), o instanță a unei subclase sau rezultatul altui constructor.
Pentru cine îl apelează, arată exact ca un constructor: Moneda('RON'). Este folosit des pentru fromJson, pentru a verifica datele înainte de creare sau pentru a alege implementarea potrivită.
Într-un constructor factory nu ai acces la this (obiectul nu a fost creat) și nu poți folosi lista de inițializare sau parametri this.camp.
Exemple
Exemplul 1: Cache: același obiect pentru același cod
A doua cerere pentru „RON” întoarce obiectul creat la prima, nu unul nou.
class Moneda {
final String cod;
static final Map<String, Moneda> _cache = {};
Moneda._intern(this.cod) {
print('Creez moneda $cod');
}
factory Moneda(String cod) => _cache.putIfAbsent(cod, () => Moneda._intern(cod));
}
void main() {
final a = Moneda('RON');
final b = Moneda('EUR');
final c = Moneda('RON');
print(identical(a, c));
print(identical(a, b));
}
Rezultat
Creez moneda RON
Creez moneda EUR
true
false
Exemplul 2: Alegerea subclasei potrivite
Forma.din primește un text și întoarce un Cerc sau un Dreptunghi.
abstract class Forma {
double get arie;
factory Forma.din(String descriere) {
final parti = descriere.split(' ');
final valori = parti.skip(1).map(double.parse).toList();
return switch (parti.first) {
'cerc' => Cerc(valori[0]),
'dreptunghi' => Dreptunghi(valori[0], valori[1]),
_ => throw ArgumentError('formă necunoscută: ${parti.first}'),
};
}
}
class Cerc implements Forma {
final double raza;
Cerc(this.raza);
@override
double get arie => 3.14159 * raza * raza;
}
class Dreptunghi implements Forma {
final double l, h;
Dreptunghi(this.l, this.h);
@override
double get arie => l * h;
}
void main() {
for (final text in ['cerc 2', 'dreptunghi 3 4.5']) {
final f = Forma.din(text);
print('${f.runtimeType}: arie ${f.arie.toStringAsFixed(2)}');
}
}
Rezultat
Cerc: arie 12.57
Dreptunghi: arie 13.50
Exemplul 3: Validare înainte de creare
factory Email curăță textul și aruncă o excepție dacă nu este o adresă validă.
class Email {
final String adresa;
Email._(this.adresa);
factory Email(String text) {
final curat = text.trim().toLowerCase();
if (!curat.contains('@')) {
throw FormatException('adresă invalidă', text);
}
return Email._(curat);
}
}
void main() {
print(Email(' Ana.Pop@Exemplu.RO ').adresa);
try {
Email('ana.exemplu.ro');
} on FormatException catch (e) {
print('Eroare: ${e.message} (${e.source})');
}
}
Rezultat
ana.pop@exemplu.ro
Eroare: adresă invalidă (ana.exemplu.ro)
Greșeli frecvente
- Folosirea lui
thisîntr-un constructorfactory: obiectul nu există încă. - Uitarea lui
return: un constructorfactorytrebuie să întoarcă un obiect. factorycând un constructor numit obișnuit ar fi suficient: folosește-l doar când ai nevoie de cache, subclase sau logică înainte de creare.
Comenzi înrudite
constructori: Funcțiile speciale care creează și inițializează obiectele unei clase.constructori numiți: Constructori cu nume propriu (Clasa.nume), pentru mai multe moduri de a crea același tip de obiect.static: Membri care aparțin clasei, nu fiecărui obiect: se folosesc prin numele clasei, ca Nume.camp.abstract: O clasă abstractă nu poate fi instanțiată direct; definește ce trebuie să facă subclasele ei.