[ Wajda.W @ 06.10.2011. 13:04 ] @
Razmisljao sam nesto, ako bi imali jezik u kojem nije moguce imati nasledjivanje kao relaciju izmedju klasa. Nego samo izmedju interfejsa.
Cini mi se da bi time bilo koji framework bio dosta primoran da se pise na nacin da je mnogo fleksibilniji, agilniji, laksi za testiranje.

Interesuju me vasa misljenja o mogucim ogranicenjima, problemima koji se mogu javiti...
[ mmix @ 06.10.2011. 14:16 ] @
Ne vidim kako bi bio agilniji, sem ako posmatras samo hriznotalnu separaciju na potpuno razlicite funkcionalnosti. Uzmi scenario da imas recimo baznu klasu koja servira osnovno procesiranje za 10 difereciranih pod klasa, i imas zahtev da preomenis odredjeno ponasanje tih 10 klasa na osnovu tih zajednickih oepracija. U OOP menjas samo baznu kalsu u tvom scenariju moras da promenis svih 10 klasa. Nista ne fali OOPu za reusability, problem je sto ljudi preteruju sa time.
[ Wajda.W @ 06.10.2011. 16:29 ] @
Ne bi morao u ovom slucaju sto sam ja predlozio da menjas svih 10 klasa. Nego bi imao dependancy na tu osnovnu klasu u svakoj i onda bi samo opet trebao da promenis samo tu klasu koja je kompozicija u svih 10.

Znam da ne fali, tj da moze da se sve to realizuje i kroz trenutni OOP, ali bi ovako ljude primorao boljem dizajnu, tj kad kazem bolji mislim fleksibilniji i pogodniji za testiranje i odrzavanje.
[ mmix @ 06.10.2011. 16:42 ] @
Pa to ti se svodi na isto, zar ne.

Po definicij "potpis" klase je njen implicitni interfejs, nasledjivanje nije nista drugo do implicitne kompozije nad baznom klasom (tvoja i bazna kalsa dele "this", ali svaka ima svoj deo v-table i metod koji nije overriden se poziva direktno u baznoj klasi). U osnovi morao bi rucno da radis sve stvari koje ti implicitno omogucava nasledjivanje, tj morao bi da za svaku subklasu implementiras identican ili uvecan interfejs gde bi vecina metoda bilo nista drugo do poziv u denendency base.

Ne znam, mozda ja ne vidim iz tvog ugla zasto je nasledjivanje nefleksibilno i tesko za testiranje i odrzavanje? Naravno, opet se ogradjujem, kad se ne preteruje (mada mozes da preteras i sa interfejsima i kompozicijom ) Ili je rezon da je simuliranje nasledjivanje kroz kompoziciju pain pa ce te to naterati da pises manje klase
[ Wajda.W @ 06.10.2011. 16:52 ] @
Citat:
... morao bi da za svaku subklasu implementiras identican ili uvecan interfejs gde bi vecina metoda bilo nista drugo do poziv u denendency base.


Da, ovo sam i ja skontao da bi bio problem, tj bilo bi dosta nepotrebnog pisanja trivijalne implementacije.

Citat:
Ne znam, mozda ja ne vidim iz tvog ugla zasto je nasledjivanje nefleksibilno i tesko za testiranje i odrzavanje?


Ja sam evo na primer to uocio kod primene Dependancy Injection paterna, ili kod MVP paterna. Cesto se desava da GUI jednostavno nije implementiran tako da koristi interfejse i onda se mora raditi wrap oko postojece implementacije koja ce usput da implementira neki interfejs koji ces ti koristiti u definiciji view interfejsa. Naravno, ovo se moglo uraditi i sa trenutnim jezicima, ali nije jer niko ne primorava interfejse pa mogu da se i izbegnu. U slucaju gde nema hijerarhije izmedju klasa svaka klasa bi imala neki interfejs ili vise njih koje je implementirala.
[ Dragi Tata @ 27.10.2011. 03:58 ] @
Postoje takvi jezici. Od popularnih, npr VB6, ili Googleov novi jezik Go.
[ negyxo @ 27.10.2011. 08:50 ] @
Sto rece mmix, problem je sa ljudima, i iz mog iskustva mogu ti reci da je apsolutno u pravu - itekako je problem sa ljudima. Mislim da sam ja bas pre jedno dve, tri godine imao taj problem sto nisam mogao da svarim interfejse. Secam se da sam i jednu temu otvorio ovde.

Elem, problem sa interfejsima je sto ljudi preteruju, recimo ja licno smatram da se interfejs ne treba nikad ni definisati dok god ti se ne ukaze potreba za istom funkcionalnosti (tacnije, fasadom funkcionalnosti :)) na jos jednom mestu. Inace, mislim da su interfejsi totalno zlo, ne vidim zasto se ne bi se sve resilo kroz klase. Sa interfjesima samo se povecava code jer ces uvek morati da pises implementaciju tamo gde koristis interfejs.
[ Boris_ZR @ 18.06.2012. 21:52 ] @
negyxo, grešiš
[ negyxo @ 19.06.2012. 00:32 ] @
Sad, kad bi rekao i sta gresim... bas nisi mogao manje reci da napises :-)
[ Mihajlo Cvetanović @ 19.06.2012. 09:31 ] @
Dobri su interfejsi. Koristim ih u C# i pomažu mi da pravilnije razmišljam kad razmišljam o tome kako da rešim problem.
[ negyxo @ 19.06.2012. 09:57 ] @
Ako mene pitas, interfejsi su dobri taman za toliko koliko je C# osakacen. Posto nema visesturkog nasledjivanja izmedju kalasa ti si prisiljen da koristis interfejse, iz tog razloga interfejsi jesu korisni, kako bi imao bar kakav takav objektno orijentisan code, ali kao sto rekoh, sve je to posledica osakacenosti jednog jezika.
[ Mihajlo Cvetanović @ 19.06.2012. 10:04 ] @
Koristim ja i C++, i to mi je prvi, da kažem, produkcioni jezik. Nikad u svom radu nije mi se ukazala potreba za višestrukim nasleđivanjem, a kad sam i imao višestruko nasleđivanje u kodu, to je zapravo bilo nasleđivanje apstraktnih klasa (to jest interfejsa).
[ negyxo @ 19.06.2012. 10:53 ] @
Vidis, ako pises sto kompozitniji code, a da je pritom objektno orijentisan, potreba za visestrukim nasledjivanjem ima. Ono sto je problem kada poredis C++ i C# nije u njihovim feature-ima nego u upotrebi, C++ ce vise radi za sistemsko prgramiranje, dok C# uglvnom za neke business aplikacije, bas zbog toga se onda i stvara drukcija potreba izmedju koriscenja nekih feature.

Ja sam recimo milion puta morao da koristim INotifyPropertyChanged interfejs, i milion puta sam morao da pisem uvek istu implementaciju, iako trivijalna, morao sam da je napisem. Smatram da ko god se bavi programiranjem i radi copy-paste metodu za resavanje problema, taj il' je pocetnik ili mu mesto nije u programiranju.
[ Nedeljko @ 19.06.2012. 12:44 ] @
Meni je u trenutnom C++ projektu zatrebalo višestruko nasleđivanje i mogao sam da se slikam sa tom mogućnošću C++ jezika zato što bibliotečka klasa nije izvedena virtuelno iz svoje osnovne klase, pa sam problem morao da rešavam na sasvim drugačiji način.
[ Shadowed @ 19.06.2012. 12:44 ] @
Kako bi visestruko nasledjivanje pomoglo kod koriscenja INotifyPropertyChanged?
[ negyxo @ 19.06.2012. 13:00 ] @
Manje bi pisao code. Ajde, da ne ispadne samo da sam zapeo za taj intefejs, posto je prilicno prost, ali uzmi bilo koji drugi i videces ako ti treba na vise mesta ista implementacija cista je ludost to kopirati i stalno pisati iznova.

Elem, a propo INotifProertyChanged interfejsa

Code:


public class Class1 : BaseClass1, INotifyPropertyChanged
{
        public event PropertyChangedEventHandler PropertyChanged;

         private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
}

public class Class2 : BaseClass2, INotifyPropertyChanged
{
         public event PropertyChangedEventHandler PropertyChanged;

         private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
}


Evo, Class1 i Class2 imaju identicne implementacije za PropertyChanged, mi po nekom standaradu za svaki event dodamo metod On_EventName_(). Da kojim slucajem postoji visestruko nasledjivanje, sve sto bi napisao je:

Code:

public class Class1 : BaseClass1, NotifyPropertyChanged
{
...
}



No, ovo je samo jedan interfejs, koji je prost, za iole kompleksnije stvari ovo je jos grdje.
[ Nedeljko @ 19.06.2012. 13:29 ] @
Za to ti služi pretprocesor.
[ Shadowed @ 19.06.2012. 14:53 ] @
@negyxo, napravis klasu koja implementira interfejs pa te dve BaseClass1 i 2 nasledis iz nje pa ove dve iz BaseClass. Mada, ok, ima situacija kada bi ovo moglo biti nezgodnije nego u tvom primeru.
[ negyxo @ 19.06.2012. 15:54 ] @
Pa u tome je i fora, sto ne mozes uvek da implementiras u baznu klasu, nego vec imas koju moras da nasledis.

Nedeljko, moze mali primer na sta mislis.
[ Nedeljko @ 19.06.2012. 18:02 ] @
Code:
#define IMPLEMENT_NOTIFY_PROPERTY_CHANGED \
public event PropertyChangedEventHandler PropertyChanged; \
\
private void OnPropertyChanged(string propertyName) \
{ \
    if (PropertyChanged != null) \
    { \
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); \
    } \
}

public class Class1 : BaseClass1, INotifyPropertyChanged
{
    IMPLMENT_NOTIFY_PROPERTY_CHANGED
}

public class Class2 : BaseClass2, INotifyPropertyChanged
{
    IMPLMENT_NOTIFY_PROPERTY_CHANGED
}


Nisam siguran da li sam pomešao C++ i C# sintaksu, ali na ovo sam mislio.
[ negyxo @ 19.06.2012. 18:09 ] @
OK, za C++ bi moglo, za C# ne, bar koliko ja znam C# :-)

Ali inace, ja ne bi ni u C++-u pisao takav code, ne vidim zasto da pisem dodatno i tu jednu liniju code-a ako ne moram. Ako si napisao da si nasledio od odredjene klase nesto, i ako ti ta bazna funkcionalnost zadovoljava slucaj, ne zelim da pisem jos dodatno nikakav code.
[ Nedeljko @ 19.06.2012. 18:44 ] @
A šta, C# nema makroe?

Što se te jedne linije tiče, ona nije problem. Problem je ako imaš istu funkcionalnost na više mesta u izvornom kodu.

Ma, znaš šta, ja bi bacio sve te klase i interfejse i programirao u računu SK kombinatora. Svašta!
[ negyxo @ 19.06.2012. 19:04 ] @
Ne u C# nije moguce tako nesto.

Moram da citiram ovaj deo:

Citat:

Problem je ako imaš istu funkcionalnost na više mesta u izvornom kodu.


Da, isto ovo treba da vazi i za definicije, u tvom primeru ti imas ponavljane definicije za INotifyPropertyChanged interfejsa. Mislim, zasto je problem pisati manje za isti rezultat? Pa razlika izmedju klasa i interfejsa je upravo samo u implementaciji, sve sto mozes da uradis sa interfejsom mozes i sa klasom, obrnuto ne vazi. Znaci u jednom slucaju imas vise opcija, u drugom manje, i sad je bolji ovaj drugi slucaj, koji te ogranicava da pises sto manje redundantan code.

[ Shadowed @ 19.06.2012. 20:22 ] @
Pa i u tvojoj varijanti moras svaki put da navodis da se nasledjuje (i) ta klasa sa implementacijom :) Nesto pisanja svaki put moras imati.

Nego, kako je u c++ reseno ako obe klase koje se nasledjuju funkciju sa istim imenom i potpisom?
[ negyxo @ 19.06.2012. 20:43 ] @
Citat:
Shadowed: Pa i u tvojoj varijanti moras svaki put da navodis da se nasledjuje (i) ta klasa sa implementacijom :) Nesto pisanja svaki put moras imati.


Ali samo to moras i da napises, nemas nista vise, u tome i jeste razlika.

Pazi, mozda sam ja cepidlaka, ali po meni, sto manje code-a pises tim bolje, namesti paralelu sa jezikom, videces da se nepotreben stvari ne govore, implicitno se podrazumevaju i nema potrebe za ponavljanjem osim ako se ne razlikuje od tog implicitnog podrazumevanog :-)

A : "Pogledaj ovaj i3 kako je dobar"
B : "Sta ti je i3?"
A : "To ti je novi Bmw model, ima okrugle tockove"

Ako znas da je Bmw auto, onda je totalno suvisna informacija da su mu tockovi okrugli, jer to vec znas na osnovu toga sto je auto.



[ Shadowed @ 19.06.2012. 23:20 ] @
Zar je velika razlika izmedju njegovog "IMPLEMENT_NOTIFY_PROPERTY_CHANGED" i tvog ", NotifyPropertyChanged"? :)

Mislim, nemam ja nista protiv visestrukog nasledjivanja i nimalo mi ne bi smetalo da ga uvedu u C# u sledecoj verziji (ukoliko je to moguce bez previse budzenja), al' izmedju ova dva slucaja, gore napisana, nema bas mnogo razlike u kolicini koda (mada meni licno tvoja varijanta deluje elegantnije, ono drugo je samo zaobilazni nacin da se ponavlja kod).
[ negyxo @ 19.06.2012. 23:50 ] @
Shadowed, nije velika razlika, ali ipak pises dodatno za sta ja smatram da je nepotrebno (cisto ako nisi primetio, i u Nedeljkovom primeru mora da se navede interfejs koji se nasledjuje i plus da se jos doda macro unutar definicije).
[ Nedeljko @ 19.06.2012. 23:52 ] @
Shadowed, je li stvarno C# nema makroe ili nexygo jednostavno ne zna za njih?

nexygo, ne izbegava se ponavljanje koda da bi se smanjilo kucanje (bar ne prevashodno), već da bi izmena jedne funkcionalnosti na jednom mestu bila izmena u celom programu.

Citat:
Shadowed: Nego, kako je u c++ reseno ako obe klase koje se nasledjuju funkciju sa istim imenom i potpisom?


E, vidi se da si pozaboravljao C++.

Ako klase A i B nasleđuju klasu C, a klasa D nasleđuje klase A i B i pritom klasa C ima metodu void foo(), onda će klasa D imati metode void A::foo() i void B::foo(). Ako je pak nasleđivanje klasa A i B iz klase C bilo virtuelno, onda će klasa D imati samo jednu metodu void foo().

E, sad, to vodi do niza problema zbog kojih su uvođeni interfejsi. Implementacija može biti takva da na primer pokazivač na klasu C pokazuje na prvi atribut klase C, a da toj adresi prethodi pokazivač na tabelu virtuelnih metoda (koji postoji i ulazi u veličinu podatka akko je potreban, tj. klasa ima virtuelnih metoda). Za klase A i B važiće isto to, tj. pokazivač na objekat će pokazivati na prvi atribut klase C, a nakon svih njih će slediti atributi izvedene klase. Dakle u klasi A idu prvo atributi klase C, pa atributi klase A i slično za B. E, sad dolazimo do klase D. Tu će ako nasleđivanje nije virtuelno ići atrubuti klase A (uključujući nasleđene atribute klase C), pa klase B (uključujući nasleđene atribute klase C), pa klase D.

Međutim, gde da smestimo pokazivač na tabelu virtuelnih metoda (recimo da ih klasa C ima)? Klasa D mora da bude konvertibilna u svoje bazne klase A i B. Ako je smestimo pre atributa klase A, imaćemo problem sa time što su ofseti atributa klase B pri konverziji D* u B*. Zapravo, svaka od tih klasa ima svoje metode, tj. u klasi D imamo duplikate članova od C (nasleđene kroz A i nasleđene kroz B), pa će zapravo objekat biti sačinjen od pokazivača na tabelu virtuelnih metoda od A, pa atributa od A (zajedno sa nasleđenim atributima iz C), pa pokazivača na tabelu virtuelnih metoda od B, pa atributa od B (zajedno sa nasleđenim atributima od C), pa atributa od D. Može klasa D da ima svoje virtuelne metode, ali oni su smešteni u tabele od A i B, koje nisu na istom mestu u memoriji kao obične tabele od A i B, pa se ne može tek tako poređenjem pokazivača na tabelu virtuelnih metoda utvrditi da li su izvedene iz iste klase, pa je dynamic_cast utoliko sporiji.

U slučaju virtuelnog izvođenja klasa A i B iz C moramo u primerku klase D imati samo jednu kopiju atributa klase C, a da D i dalje bude konvertibilna u A i B. Kako kada obe klase nasleđuju C, a ofset atributa od A i B u odnosu a C ne može da bude isti. Čim je klasama A i B rečeno da su virtuelno nasleđene iz C, one pre svojih atributa imaju pokazivač na tabelu virtuelnih atributa, tj. ofset od C. E, onda D može da se sklopi sa samo jednim primerkom C.

No, kod višestrukog nasleđivanja prilikom konverzije pokazivača dolazi do promene vrednosti pokazivača u smislu apsolutne adrese, tako da računar mora sa sigurnošću da zna kako to da uradi. Ako se za neku konverziju može zaključiti da je moguća i kako je izvesti u fazi prevođenja, onda se koristi brzi static_cast (tipično za konveryiju iz izvedene u osnovnu klasu), a u suprotnom je static_cast sintaksna greška. Ukoliko se u fazi prevođenja može zaključiti kako u fazi izvršavanja da se odredi da li je konverzija moguća i kako i da je u bar nekim slučajevima moguća, onda se može koristiti spori dynamic_cast. No, on može u fazi izvršavanja da ne uspe.

No, tu nismo ni dotakli problem brisanja objekata. Ako imam pokazivač na klasu A koji pokazuje na primerak od D, pa ga brišemo, on mora da bude brisan kao primerak od D, tj. objekat, mada je posmatran kao primerak od A mora imati svest o tome da je zapravo objekat tipa D.

Sve to vodi do komplikovanih pravila, koja se značajno pojednostavljuju ako je nasleđivanje jednostruko, osim u slučaju interfejsa.
[ negyxo @ 20.06.2012. 07:17 ] @
Nedeljko evo ti link za C# define: http://msdn.microsoft.com/en-us/library/yt3yck0x(v=vs.71).aspx

Citat:

nexygo, ne izbegava se ponavljanje koda da bi se smanjilo kucanje (bar ne prevashodno), već da bi izmena jedne funkcionalnosti na jednom mestu bila izmena u celom programu.


Da, ali isto tako se i izbacuje bilo kakva suvisna logika, bas da bi kucao manje, i da bi ti bilo lakse da citas code, a ne da glumis sve vreme "human filter", koji prvo poizbacuje suvisne informacije pa onda obradjuje ostatak.

Sto se tice visestrukog nasledjivanja, mislim da si usao u detalje previse, to sto kazes da je komplikovano, to je vise za dizajnere kompajlera, u C++ bar koliko vidim, ambiguity se resava tako sto navedes sta zelis da referenciras, isti problem i sa interfejsima, isti problem i sa jezikom... ovaj mu dodje kao http://www.youtube.com/watch?v=NYJ2w82WifU :-)

[ negyxo @ 20.06.2012. 07:29 ] @
Samo da dopunim sebe:

Citat:

Sto se tice visestrukog nasledjivanja, mislim da si usao u detalje previse, to sto kazes da je komplikovano, to je vise za dizajnere kompajlera


Taj deo oko skladistenja virtualnih tabela, na to sam mislio da je za dizajnere kompajlera, cak sta vise, to jednom kad se namesti, posle radi u proizvoljnoj dubokoj hierarhiji, tako da ta komplikovanost dalje se moze samo pripisati onom ko pise neki program, tj. resava probleme sa multi inheritance u amibiguity maniru :-)

Inace, ne znam zasto, ali ovaj (Diamon) problem se uvek navodi kao neka mana multi inheritance-a, no, kako se to onda resava sa single inheritance, ono, kad je bas zapelo da mora da budu ista imena? (nesto mi govori da je tu tek code komplikovan :))

[ Shadowed @ 20.06.2012. 08:39 ] @
Nedeljko, hvala na detaljnom opisu :)
Nema C# makroe.
[ lelorinel @ 20.06.2012. 16:29 ] @
Citat:
negyxo:
Sad, kad bi rekao i sta gresim... bas nisi mogao manje reci da napises :-)
 



sve moze da se postigne klasama, i interfejsi nisu neophodni, ali ne koriscenjem istih krsis vise principa programiranja izmedju ostalih i dependency inversion od SOLID principa.
samim ne koriscnjem interfejsa tvoj kod postaje mnogo tezi za testiranje, mnogo tezi za odrazvanje i nije sklon promenama, teze evoluira tj. vrlo tesko ces promeniti ili dodati novu funkcionalnost ukoliko se ukaze potreba.
na primeru unit testinga, kako ces da se oslobodis dependencija koje tvoj SUT ima ako su isti klase a ne interfejsi?
koriscenje mocking frameworka sigurno nije opcija, vec bi morao da radis sve rucno, pravljenjem virtuelnih metoda dependency klase i nasledjivanjem iste od strane tvoje test klase itd itd.
[ negyxo @ 20.06.2012. 16:57 ] @
Izvini sto nemam nameru da sad obrazlazem kako i zasto, ali da ti mozda prvo das primer sta moze da se uradi sa interfejsima a da sa klasama ne moze, nekako, nije mi bas najjasniji taj deo: " kako ces da se oslobodis dependencija koje tvoj SUT ima ako su isti klase a ne interfejsi".
Sta te sprecava da nasledjujes baznu klasu, tj. sta te sprecava da koristis klasu kao interfejs? Jel mozda smeta empty body metode, ili cak ne moras ni da ga deklarises, stavis abstract i vozi...
[ mmix @ 20.06.2012. 17:13 ] @
@lelorinel, ne loze se svi na unit testing, a kamoli na TDD, niti se loze na DI kao mirodjiju za svaku corbu. Sama ideja da nasledjivanje treba izbaciti zato sto MVC ekipa ne zna sta bi radila sa njim je malo egocentricno i kontraproduktivno jer niko od "oop ekipe" ne sprecava "mvc ekipu" da koristi interfejse. To each its own, iliti sto bi rekli englezi, postoji vise nacina da se odere macka
[ Nedeljko @ 20.06.2012. 18:03 ] @
Citat:
negyxo: da ti mozda prvo das primer sta moze da se uradi sa interfejsima a da sa klasama ne moze

A da ti daš primer šta može da se uradi sa private, a ne može sa public. Možda previše slobode može da smeta.
[ negyxo @ 20.06.2012. 19:52 ] @
Nedeljko, iskreno ne kontam tvoje pitanje.
[ Boris_ZR @ 20.06.2012. 20:10 ] @
negyxo, ne znam što si zapeo toliko protiv interfejsa, ali poštujem tvoju borbenost. :)
Pošto sam ja samo junior programer i možda ću promeniti mišljenje još 100 puta u karijeri, u odbranu interfejsa citiraću one koji su očevi softverskog inženjerstva danas:

http://pragmaticjava.blogspot....erface-not-implementation.html

Ako njihovi saveti ne znače baš ništa, svaka dalja komunikacija na ovu temu je suvišna.
[ mmix @ 20.06.2012. 20:25 ] @
Khm, dobro, da se ne zalecemo mnogo sa tim ocenastvom i slicnim zitijama.


Jedna veoma vazna stvar koju zaboravljas a koju sam vec izneo onomad na pocetku ove teme je da svaka klasa ima svoj implicitni interfejs. Cak mozes komotno da napravis DI mehanizam koji faktorise objekte i vraca isntance baznih klasa. I to moze da ide do te mere da celokupna stvar moze da bude mockable i testable.

[ negyxo @ 20.06.2012. 20:41 ] @
@Boris_ZR
Pazi, nemam ja nista protiv interfejsa, bukvalno na dnevnoj bazi ih koristim, sto samo implementiram, sto neke i pravim. Ono sto je meni zasmetalo oko interfejsa je to visestruko nasledjivanje, ne zelim da pisem isti code, no taj deo jos i progutam nekako. Drugi deo je vezan za guranje interfejsa svuda, i to iz nekog razloga koji je meni nejasan. Ja sam imao situaciju da se zahteva da se za svaku klasu definise interfejs, samo zato da bi code bio "highly decoupled", iako se interfejs nigde ne koristi! Znam ja da ovo problem do onoga ko sve to zahteva i da interfejsi nisu krivi, ali problem je bas u tome, da kojim slucajem postoje samo klase, valjda bi nekom dunulo u glavu da je apsurdno definisati klasu pa zatim jos jednu isti takvu samo apstraktnu klasu. Ovako kada imas interfejs, to onda nije to, ti uvek mozes da kazes "ali ovo je samo interfejs ovde nemas body metoda, mora tako".
[ Nedeljko @ 20.06.2012. 21:05 ] @
Citat:
negyxo: Nedeljko, iskreno ne kontam tvoje pitanje.

Pa, zameni u bilo kom ispravnom programu sva pojavljivanja private sa public i radiće i dalje.

Čemu služi private?
[ negyxo @ 20.06.2012. 22:35 ] @
Nisam mislio da si bukvalno na to mislio, ali kontam te sad na sta ciljas, ali mi nije jasno kako si uspeo takvu paralelu da namestis. Ti na private gledas kao na nesto pozitivno (sto u zavisnosti od konteksta i jeste), jer povecavas "constraint programa", da se tako izrazim, ali bas u tome i jeste problem, u ovom jednom slucaju (C#) ti imas samo jedan nacin, pa bi to bilo kao da kazes "imas izbor da pises ili private ili private" :-)
[ Nedeljko @ 15.07.2012. 14:15 ] @
U suštini, nalseđivanje samo između interfejsa služi da nametne dependency inversion kao stil programiranja, pa makar to bilo protiv lean principa. Ne znam kako scrum-ovci gledaju na to. Možda je DI OK, ali kao stvar izbora.

Čuo sam skoro za još jednu glupost - da nigde u programi ne sme da se ima if osim za proveru da li je pokazivač nula. Osećam da pucaju na polimorfizam, ali on ne može u celini da zameni if. Da mi je znati kako će onda da naprave algoritam sortiranja ili traženja minimuma/maksimuma niza, indeksiranih kontejnera itd. Ja ne znam nijedan način bez if-a.
[ mmix @ 15.07.2012. 16:58 ] @
Ali sto if za to, siguran sam da nesto moze da se uradi preko coallesce operatora.

Boze kakvih sve nalozenih pacijenata ima u bransi danas....

[ Nedeljko @ 15.07.2012. 17:19 ] @
Ma, to sam samo naveo kao primer nečega gde definitivno treba if (taj coalesce je implementiran preko if-a). Drugi primer - treba mi intenzitet vektorskog proizvoda dva vektora. Matematika kaže da je
.
Naravno, izraz pod korenom nikada ne može biti negativan, ali u matematici. U približnom računu na računaru može, pa bi ovo trebalo računati sa
Code (cpp):
struct vector {
    double x, y, z;
};

double operator*(vector a, vector b) {
    return a.x*b.x + a.y*b.y + a.z*b.z;
}

double intensity(vector a, vector b)
{
    double aa = a*a;
    double bb = b*b;
    double ab = a*b;
    double res2 = aa*bb-ab*ab;

    if (res2 <= 0) {
        return 0;
    }

    return sqrt(res2);
}

Ovo je naravno samo ilustracija if-a. Nećemo sad da opterećujemo kod još i prekoračenjima.
[ mmix @ 16.07.2012. 08:46 ] @
Pa sve apstraktne tehnike su uvek implementirane preko primitivnih. Sta je implementacija interfejsa nego indirektni pozivi function pointera iz tabele? Ako si dovoljno zaludan mozes u C-u komotno da napravis klasu sa sve virtuelnim metodama tako sto ces strukuri dodati listu/niz function pointera, imati "konstruktor" koji ce da je inicijalizuje i i onda svaki poziv "metoda" zapravo raditi kao poziv function pointera. I na kraju se sve to svodi na CALL varijante asemblera. Zlo je kad se ljudi zaborave u svoj apstrakciji pa ni ne znaju sta kako radi, ja sam za razliku od tebe ubedjen da ima ljudi koji veruju da coallesce operator magicno pretvara null u nesto.
[ tdusko @ 16.07.2012. 09:18 ] @
Ja sam na primer naviko' da uvek koristim if jer mi je citljivije, a kolega u firmi kad radimo code review uzme pa mi sam svuda promeni "proste" if-ove u skraceni zapis.

Code:
if(o.Member != null) 
                tb_MyTextBox.Text = o.Member;
         else 
               tb_MyTextBox.Text = "default";


Code:
tb_MyTextBox.Text = o.Member != null ? o.Member : "default";


Jel radite vas dvojica recimo to? Delujete mi kao neko ko je imao prilike da radi kao dev leader u timu pa bas rekoh da vas pitam jel to normalno posto mene strasno nervira :)
[ Mihajlo Cvetanović @ 16.07.2012. 09:34 ] @
Meni je ovaj slučaj čitljiv, i ja bih to isto tako napisao. Zapravo, ako je ovo C# u ovom specijalnom slučaju ja bih ga još više skratio koristeći takozvani null-coalesce operator (??, dva spojena upitnika), i linija bi izgledala ovako:

Code:
tb_MyTextBox.Text = o.Member ?? "default";


Ako je slučaj malo komplikovaniji onda ipak koristim if-else. Međutim ne verujem da bih ispravljao tuđ kod, jer kod ne sme da bude napredniji od onog ko ga piše i održava. Ako još nisi spreman za ternarni operator ?: onda jbg nisi spreman, i sve što ti je u kodu strano će te samo ometati i usporavati.
[ mmix @ 16.07.2012. 09:36 ] @
Ja ti ne bih promenio (generalno ne menjam ljudima kod ako radi i ako nije sampionski tipa if a> b return true else return false ) ali bih ti preporucio conditional operator. U osnovi radi istu stvar i kompajler ga optimizuje na isti nacin ali bi trebao da steknes naviku da koristis taj operator zbog drugih prilika u kojima je nezaobilazan, npr u anon class inicijalizaciji

Code:

from x in list
select new {
   broj = x,
   klasa = x<0 ? "-" : "+"
}

[ deerbeer @ 16.07.2012. 09:36 ] @
Radim i jedno i drugo . Ovu skracenu varijantu pisem kad tih uslova ima mnogo i kad znam da ce 1 if imati jednu liniju koda bez neke mogucnosti da se kod u true ili false grani prosiruje .
Pregledniji je kod kad imas mnogo takvih uslova a uglavnom je to u UI-u gde popunjavas neke kontrole pa ispitujes na NULL ...
[ Shadowed @ 16.07.2012. 09:47 ] @
Citat:
mmix: Ja ti ne bih promenio (generalno ne menjam ljudima kod ako radi i ako nije sampionski tipa if a> b return true else return false 8)) ali bih ti preporucio conditional operator.


Same here. Ne menjam tek tako nekome kod vec bih pre dao sugestiju ili, zavisno od situacije, dao njemu da menja (ako ima mnogo ili neki kao ovaj pomenuti sampion) :)

Edit: Inace, koristim obe varijante zavisno od procenjene citljivosti. Ako bi linija bila preduga, onda if/else, ako je nesto kratko onda conditional operator.
[ tdusko @ 16.07.2012. 10:17 ] @
Tu se izgleda radi o lenjosti :) Ja kada sam siguran da ce if da bude "prost" napisem ga skraceno, ali nekad nisam siguran pa na kraju ispadne tako, a budem lenj da refaktorisem kod. Sa druge strane njega izgleda mrzi da mi to predoci pa onda izmeni sam, a ja se osecam da me neko vuce za usi, a nisam kriv :)
[ Nedeljko @ 16.07.2012. 10:37 ] @
Recimo da imam program koji radi sa grafikom i da prikaz zavisi od nekih parametara. Ako treba da se neki od tih parametara postavi na neku novu vrednost, može se pitati da li je ta "nova" vrednost jednaka postojećoj, pa ako jeste, ne raditi ništa.

Baš me zanima šta je zamena za takav if.
[ Mihajlo Cvetanović @ 16.07.2012. 10:47 ] @
Citat:
tdusko:Sa druge strane njega izgleda mrzi da mi to predoci pa onda izmeni sam, a ja se osecam da me neko vuce za usi, a nisam kriv :)


E pa, ako ne voliš da te ispravljaju onda im uskrati to tako što ćeš se ispraviti sam. Ako te više mrzi nego što te iritira onda verovatno i nemaš neki problem.
[ mmix @ 16.07.2012. 10:57 ] @
Citat:
Nedeljko: Recimo da imam program koji radi sa grafikom i da prikaz zavisi od nekih parametara. Ako treba da se neki od tih parametara postavi na neku novu vrednost, može se pitati da li je ta "nova" vrednost jednaka postojećoj, pa ako jeste, ne raditi ništa.

Baš me zanima šta je zamena za takav if.


Trenutna "konvencionalna" logika je da ti svejedno postavis vrednost a da je na UI komponenti da (na neki magican nacin koji ne koristi if ) proveri da li treba da osvezi svoj prikaz, po principu "muko moja predji na drugoga". Zanimljivo je da bar .NET component dizajneri primenjuju tu tehniku (doduse preko if, ali pravicemo se da to ne znamo ), narocito od kad je INotifyPropertyChanged postao popularan.

Citat:
Mihajlo Cvetanović: E pa, ako ne voliš da te ispravljaju onda im uskrati to tako što ćeš se ispraviti sam. Ako te više mrzi nego što te iritira onda verovatno i nemaš neki problem.


meni zapravo ponasanje njegovog tim leadera deluje kao klasicno passive-agresive ponasanje techija koji je promovisan u lower management, pa ne ume sa ljudima ali je okurazen novopronadjenom moci Ni ja nikad nisam podnosio takve likove dok sam bio junior, poenta imanja tim lidera i seniora je da valjda nesto i naucis od njega/nje, ispravljanje tudjeg koda bez obrazlozenja je po meni teski faux pas.
[ Nedeljko @ 16.07.2012. 15:34 ] @
mmix

Ne govorim ja o standardnim komponentama kao što je button, koje imaju svoju logiku, nego o nečemu što ja treba da crtam na osnovu nekih podataka.

U Qt-u redefinišem paintEvent metod. Dakle, nije to nešto što su Qt dizajneri (ili .NET dizajneri u slučaju .NET-a) odradili, već nešto što ja definitivno moram da odradim.
[ mmix @ 16.07.2012. 16:03 ] @
To je samo zato sto nisi otisao dovoljno duboko u apstrakciju, danas je 2012 ko jos pravi svoje graficke komponente (zezam se naravno, jos od prvog vagona) Poenta je da imho ljudi koji sebe (a i svoje timove) opterecuju takvim glupostima imaju previse slobodnog vremena na racun firme.
[ Nedeljko @ 16.07.2012. 16:23 ] @
Ne, reč je o komponentama koje moraju da se prave. Takav je projekat. Ne postoje standardne komponente za tu specifičnu svrhu.

No, nije bitno ni to, nego da prilikom postavljanja neke vrednosti treba nešto uraditi ako je ta vrednost različita od trenutne.

Ajmo dalje,
Code (cpp):

class String
{
    char *buffer;
    int size;

public:
// ...
    String& operator=(const String &other) {
        if (this == &other) {
            return *this;
        }

        delete [] buffer;

        size = other.size;

        if (other.buffer == 0) {
            buffer = 0;
        } else {
            buffer = new char[size];
            memcpy(buffer, other.buffer, size);
        }

        return *this;
    }
// ...
};
[ tosa @ 18.07.2012. 06:31 ] @
Citat:
mmix:
Ja ti ne bih promenio (generalno ne menjam ljudima kod ako radi i ako nije sampionski tipa if a> b return true else return false 8)) ali bih ti preporucio conditional operator. U osnovi radi istu stvar i kompajler ga optimizuje na isti nacin ali bi trebao da steknes naviku da koristis taj operator zbog drugih prilika u kojima je nezaobilazan, npr u anon class inicijalizaciji

Ne rade uvek istu stvar, u zavisnosti od nivoa optimizacije i/ili kompajlera. Conditional operator ce generisati conditional move instrukciju dok ce if/else par da generise compare/jump par - ovo drugo je dosta losije.
[ Mihajlo Cvetanović @ 18.07.2012. 09:22 ] @
Par desetina nanosekundi viška ja ne bih nazvao dosta lošije. Pustite kompajler da radi svoj posao, i skoncentrišite se na ono što je u kodu bitno, a to je da radi bez grešaka, i da se lako razume i menja.
[ Shadowed @ 18.07.2012. 09:27 ] @
Zavisi koliko puta se izvrsava :) Ali da, uglavnom nije bitno. A mislim da se mmix-ov post odnosio na C#.
[ mmix @ 18.07.2012. 11:07 ] @
Da, C#.

Inace, ne znam gde si to video sem u debug modu (a to se ne racuna jer je optimizacija iskljucena zbog line-by-line debuga)

U release bildu posle JITovanja kod je skoro potpuno identican, razlika moze da bude samo u redosledu dodela. U primeru koji sam upravo uradio cek je i kompletan metod inline-ovan u pozivaoca pa je jos lakse uporediti. Bottom line, da li ce biti conditional move ili je/jne branching zavisi od sr/dest kombinacije ali ako ?: operator JITuje u conditional move onda postoji i ekvivalentan if/then/else konstrukt koji ce uraditi to isto.

Code:


    public class Class1
    {
        public int x;
        public int y = 123;

        public void IfWay()
        {
            if (x == 0) x = 10; else x = y;
        }

        public void CondWay()
        {
            x = x == 0 ? 10 : y;
        }

    }

            Class1 c = new Class1();
00000000  push        ebp 
00000001  mov         ebp,esp 
00000003  mov         ecx,5B25E0h 
00000008  call        FFD2FBE0 
0000000d  mov         edx,eax 
0000000f  mov         dword ptr [edx+8],7Bh 
            c.x = 0;
00000016  xor         ecx,ecx 
00000018  mov         dword ptr [edx+4],ecx 
            c.IfWay();
0000001d  cmp         dword ptr [edx+8],0Fh 
00000021  jne         0000002A 
00000023  xor         ecx,ecx 
00000025  mov         dword ptr [edx+4],ecx 
00000028  jmp         00000030 
0000002a  mov         eax,dword ptr [edx+8] 
0000002d  mov         dword ptr [edx+4],eax 
            c.CondWay();
00000030  cmp         dword ptr [edx+8],0Fh 
00000034  je          0000003B 
00000036  mov         eax,dword ptr [edx+8] 
00000039  jmp         0000003D 
0000003b  xor         eax,eax 
0000003d  mov         dword ptr [edx+4],eax 
00000040  pop         ebp 

[ tosa @ 19.07.2012. 02:15 ] @
Citat:
Mihajlo Cvetanović:
Par desetina nanosekundi viška ja ne bih nazvao dosta lošije. Pustite kompajler da radi svoj posao, i skoncentrišite se na ono što je u kodu bitno, a to je da radi bez grešaka, i da se lako razume i menja.

Kod sa branch instrukcijama bih svakako nazvao dosta losijim od branch free koda.

@mmix
Video sam to na vise platformi/kompajlera. Ovaj C# jit kod je katastrofa - nigde nema conditional move instrukcija, obe verzije su podjedanako lose ;)
[ mmix @ 19.07.2012. 11:06 ] @
Ne bih se slozio satvojom konstatacijom, CMOV je blok operacija koja nije predictable, a velika vecina jxx brancheva jeste predictable i izvrsava se dosta brze na modernim procesorima narocito u multithreaded scenarijima. Jedino sto realno dobijas sa cmovxx je manje bajtova u kodu sto je danas zanemarljiva kategorija za veliku vecinu primena.

I sam intel potvrdjuje tu pricu:

Citat:
Use the setcc and cmov instructions to eliminate unpredictable conditional branches where possible. Do not do this for predictable branches. Do not use these instructions to eliminate all unpredictable conditional branches, because using these instructions will incur execution overhead due to executing both paths of a conditional branch. In addition, converting conditional branches to cmov or setcc trades control-flow dependence for data dependence and restricts the capability of the out-of-order engine. When tuning, note that all Intel® processors have very high branch-prediction rates. Consistently mispredicted branches are rare. Use these instructions only if the increase in computation time is less than the expected cost of a mispredicted branch


i cak je i Linus rekao po koju na tu temu: http://yarchive.net/comp/linux/cmov.html

Ako jos uzmes u obzira da je od Core2 na ovamo mispredicted branching drasticno "pojeftinio" i tih 5% koliko obicno promasi je sada jos manje od overheada cmova. Mnogo stosta moze da se kaze za .NET ali JIT mu je (bar MSov, ne racunamo pokusaj zvano Mono) definitivno na nivou zadatka.
[ Ivan Dimkovic @ 19.07.2012. 19:44 ] @
Pretpostavljam da tosa nije mislio na Intel procesore iz poslednjih nekoliko generacija, vec mozda neke embedded arhitekture, koliko se secam tosa je (bar bio) u konzolnom (gaming) biznisu ako ne gresim?

Mada... covek je tu, glupo je da pricam za njega..
[ Nedeljko @ 19.07.2012. 20:33 ] @
Pravo da ti kažem, ja mogu svašta da očekujem od nekoga ko je us tanju da napiše nešto ovako:
Citat:
tosa: ko voli da pise kilometre boilerplate koda da bi na kraju dosao i do same implementacije svog problema - taj nek uziva u Javi. Ima dosta firmi koje placaju za to besmisleno trosenje vremena...
[ tosa @ 20.07.2012. 02:05 ] @
Citat:
Ivan Dimkovic:
Pretpostavljam da tosa nije mislio na Intel procesore iz poslednjih nekoliko generacija, vec mozda neke embedded arhitekture, koliko se secam tosa je (bar bio) u konzolnom (gaming) biznisu ako ne gresim?

Ne gresis, procesora ima raznih a branch instrukcije su svuda lose, negde (dosta) vise negde manje :)
[ mmix @ 20.07.2012. 06:51 ] @
Gut feeling ili je to necim potkovano?
[ tosa @ 20.07.2012. 08:09 ] @
Citat:
mmix:
Gut feeling ili je to necim potkovano?

Dovoljan hint bi trebalo da ti bude priroda AVX, VMX, SSE i slicnih instrukcija gde veliki broj njih omogucava izbegavanje 'branchy' koda - pored toga sto rade na vise podataka paralelno.
[ Ivan Dimkovic @ 20.07.2012. 09:57 ] @
Hmm ali ako vec hoces da iskoristis pun potencijal koji nude SSE/AVX instrukcije (tipa VBLENDVPS) i ovako i onako ces morati da zasuces rukave i spustis se na ASM nivo... a za trivijalne if() statemente posten kompajler ne bi trebao da ima problem da uoci da moze da iskoristi odgovarajucu kondicionalnu instrukciju.
[ tosa @ 20.07.2012. 10:35 ] @
Citat:
Ivan Dimkovic:
Hmm ali ako vec hoces da iskoristis pun potencijal koji nude SSE/AVX instrukcije (tipa VBLENDVPS) i ovako i onako ces morati da zasuces rukave i spustis se na ASM nivo... a za trivijalne if() statemente posten kompajler ne bi trebao da ima problem da uoci da moze da iskoristi odgovarajucu kondicionalnu instrukciju.

Jos kad bi MSVC dozvoljavao ASM za x64... Poenta je bila da se problem branchy koda reflektovao u dizajnu tih instrukcija zbog cega sam i rekao da bi to bio dobar hint.
Ovo je vec prilicno odlutalo od originalne teme, ali ?: operator mi je skroz ok ;)
[ Ivan Dimkovic @ 20.07.2012. 10:47 ] @
Citat:
tosa
Jos kad bi MSVC dozvoljavao ASM za x64...


Off-topic, pa dobro, _mm256_blendv_ps() ce raditi posao takodje ;-)
[ mmix @ 20.07.2012. 11:10 ] @
Pa i jesmo, ja sam odgovorio na tvoju tvrdnju da je .NET JIT sh*t jer je izabrao branching umesto cmov i ja sam ti dao rezon i objasnjenje zasto je bolje i brze i potkrepio tamo nekim linkovima a kao odgovor dobio "ja znam bolje, vidi AVX". Fine, whateva.
[ tosa @ 20.07.2012. 15:33 ] @
Citat:
Ivan Dimkovic:Off-topic, pa dobro, _mm256_blendv_ps() ce raditi posao takodje ;-)

Off topic: Hoce ;) Nego mi je skroz bezveze sto ne mogu da pisem inline asm za x64 nego moram da se cimam sa ekternim fajlovima (MSVC).

@mmix
Nisu svi procesori out of order, vecina game konzola, mobilnih telefona, tableta i tako dalje ima in order procesore. Niza potrosnja je u pitanju tako da izbegavanje branchy koda i te kako ima smisla.
Jedan branch verovatno nece napraviti veliku razliku (ako bude merljiva) ali ako imas rutinu koja je u loop-u i ima par if-ova tu je cesto kraj sa dobrim performansama.