@mjanjic
Htedoh da odgovorim na tvoj pretposlednji post ali usled obaveza nisam stigao...
Elem, to je prilicno dobar odgovor na SO zasto su "potrebni" interfejsi iz ugla nekog idealnog OOP modela... ali, nedostaje onaj praktican deo, sta se desi kada zelis da implementiras na isti nacin tu funkcionalnost? Da, zelis da ti je code decoupled, pa samim tim prebacujes funkcionalnosti u razlicite delove, ali kako to onda iskoristiti a da ne moras uporno da ponavljas code? Mislim da je ovo problem na koji dizajneri interfejsa nisu mislili ili su mislili da to nije neki issue na koji ce se ljudi zaliti (a kao sto vidimo, sa ovim
defaults interface implementation upravo pokusavaju, ja bih rekao na polovican nacin, da rese).
Recimo, da vidimo iz ugla code-a, kako to izgleda:
Code:
interface ICanWash
{
void Wash();
bool IsDirty;
}
class Person {
string Name;
Date Born;
}
class Car {
string Model;
int Year;
}
Imamo dve klase, imamo jedan interfejs (contract) koji nam definise sta mozemo da radimo/implementiramo (ne nasledjujemo) sa tim objektima. Ako probamo da implementiramo:
Code:
class Person : ICanWash {
string Name;
Date Born;
bool IsDirty;
Wash() {
// do some common functionality
IsDirty = false;
}
}
class Car : ICanWash {
string Model;
int Year;
bool IsDirty;
Wash() {
// do some common functionality
IsDirty = false;
}
}
Iz ovog primera, vidimo da imamo redudantan code. Sta mozemo da radimo? Pa apsolutno nista, mozes common functionality da prebacis u neku common klasu i odatle implementiras interfejs (i opet imas redudantan code). Naravano, u ovom primeru bi mogli da namesitmo apstraktnu klasu i nasledimo je, ali to nije poenta, zato sto: prvo, zelimo da imamo cist OOP model, ne zelimo nasledjivanje, i drugo, ovaj primer nema u klasama Person i Car base klasu, da kojim slucajem ima, onda opet ne bi mogli da iskoristimo klasu.
E sad, kako bi code izlgedao sa klasom:
Code:
class CanWash {
bool IsDirty;
Wash() {
// do some common functionality
IsDirty = false;
}
}
class Person : CanWash {
string Name;
Date Born;
}
class Car : CanWash {
string Model;
int Year;
}
Hm, iz mog ugla ovo je daleko elegantnije nego ovo sa interfejsima ali... imamo jedan problem, mi smo
nasledili, tamo gde treba da
implementiramo, opet nekako to nije to ali... kako se implementira interface, recimo u C#, a kako se nasledjuje klasa:
Code:
// interface implementation
class Car: ICanWash
// class inheritance
class Car: CanWash
WTF? Pa zar ovo nije igra reci onda? Nije naravno, onaj odgovor na SO nam govori suptilnu razliku.
OK, ali onda, trebalo je tu suptilnu razliku
eksplicitno naglasiti, pa bi onda valjda i oni pure OOP concept zealoti bili zadovoljeni.
I, sad cu se osvrnuti na TypeScript. Nisam pratio razvoj TypeScripta, ali cini mi se da su tamo upravo ovo hteli da naglase, plus, imamo skoro proof-of-concept kako bi ovo moglo da funkcionise da su sve klase.
U TypeScriptu postoje dve kjlucne reci
extends i
implements, i to nas dovodi do:
- klasa moze da nasledi klasu, ali ne moze interface
- klasa moze da implementira interface
- klasa moze da "implementira" klasu
- interface moze da nasledi interface
- interface moze da "nasledi" klasu
i code:
Code:
interface ICanWash
{
void Wash();
bool IsDirty;
}
class Person {
string Name;
Date Born;
}
class Car {
string Model;
int Year;
}
ako pokusamo da implementiramo
Code:
class Person implements ICanWash {
string Name;
Date Born;
bool IsDirty;
Wash() {
// do some common functionality
IsDirty = false;
}
}
class Car implements ICanWash {
string Model;
int Year;
bool IsDirty;
Wash() {
// do some common functionality
IsDirty = false;
}
}
ako pokusamo da nasledimo:
Code:
class CanWash {
bool IsDirty;
Wash() {
// do some common functionality
IsDirty = false;
}
}
class Person extends CanWash {
string Name;
Date Born;
}
class Car extends CanWash {
string Model;
int Year;
}
Voila! Ali smo prekrsili onaj koncept sa SO, mi smo nesto
nasledili, umesto implementirali ali TypeScript dozvoljava da se klasa impelementira, u kom slucaju, se ne nosi default implementacija, nego se nosi samo njen implicitni "interface":
Code:
class Person implements CanWash {
string Name;
Date Born;
bool IsDirty;
Wash() {
// do some common functionality
IsDirty = false;
}
}
class Car implements CanWash {
string Model;
int Year;
bool IsDirty;
Wash() {
// do some common functionality
IsDirty = false;
}
}
Sto ce reci, da je isto kao da imamo i interface, jer TypeScript, iako postoji klasa CanWash, gleda samo fasadu te klase (interface) ne i implementaciju.
E sad, TypeScript, bas kao i C# i Java ne podrzava MI, pa samim tim, onda je uvek safe bet da se ide na interface, ali da koji slucajem podrzava, onda bi interface-i bili totalno bespotrebni. TypeScript ima mixine zato, ali kada bi otisli korak dalje, mixini bi mogli da se implementiraju kroz
implements direktivu, tj. neku novu, kao sto sam spomenuo, "deep implements".
Code:
// Mora da se implementira CanWash, iako je CanWash klasa sa implementacijom,
// jer smo rekli da hocemo fasadu te klase
class Person implements CanWash {
string Name;
Date Born;
bool IsDirty;
Wash() {
// do some common functionality
IsDirty = false;
}
}
// ne mora nista da se implementira, vec je implementirano sa "deep implements",
// ovo je razlicito od "extends" extends nasledjuje, "deep implements" implementira,
// deluje pomalo smesno, kada se gleda code, samo su reci razlicite, ali u runtime
// to bi se moglo manifestovati tako da kada ispitamo:
// Persion is CanWash -> false
// Persion is implements CanWash -> true
//
class Person deep implements CanWash {
string Name;
Date Born;
}
Sorry na poduzem postu.
edit: typo, IsDirty
[Ovu poruku je menjao negyxo dana 07.12.2018. u 05:29 GMT+1]