Article View: pl.comp.objects
Article #13581Re: Czy interfejs to to samo co klasa czysto abstrakcyjna ?
From: Sektor van Skijlen
Date: Sun, 20 Nov 2005 17:10
Date: Sun, 20 Nov 2005 17:10
277 lines
14159 bytes
14159 bytes
Dnia Sun, 20 Nov 2005 14:24:21 +0100, Filip Sielimowicz skrobie:
> W związku z tym, że tamta dyskusja się dłuuuży pod innym zupełnie tytułem,
> kilka słów w nowym wątku. Mam nadzieje, że to odrobinę uporządkuje.
> Najpierw link na skromną definicję w wikipedii:
> http://pl.wikipedia.org/wiki/Dziedziczenie_(programowanie_obiektowe)
> (patrz ostatni punkt - "Wielokrotne dziedziczenie a interfejsy").
> Z powyższego artykułu wynika, że z punktu widzenia zjawiska
> wielodziedziczenia są to pojęcia bardzo zbliżone.
> Sprowadzajac jednak dyskusję na poziom konkretnych realizacji
> w Javie (interfejsy) i C++(wielodziedziczenie z użyciem klas czysto
> abstrakcyjnych) pojawia się pewien niuans widoczny przy dziedziczeniu
> po wspólnych przodkach. Do zobrazowania problemu weźmy (szczęśliwie bądź
> też mniej - rzecz na inną dyskusję ...):
> interface IPojazd {
> getNazwa()
> }
> interfejs IPojazdPlywajacy extends IPojazd {
> getWypornosc()
> }
> interface IPojazdLatajacy extends IPojazd {
> getMaxUdzwig()
> }
> class Awionetka implements IPojazdPlywajacy, IPojazdLatajacy {
> getNazwa() <-- ?????
> ...
> }
> W przypadku C++ taka konstrukcja dziedziczenia klas abstrakcyjnych
> skutkuje tym, że metoda Awionetka.getNazwa() musi być implementowana
> DWA RAZY, ponieważ każdy przodek (implementowany interfejs) jest traktowany
> jako całkowicie odrębna, nowa jakość wprowadzana do klasy. Interfejsy
> nie wchodzą w żadną interakcję, nie nakładająsię na siebie.
Ech...
Spróbuj mnie umotywować jakoś, żeby nie zaczął przeklinać.
Tu już nie o to chodzi, że pleciesz niestworzone dyrdymały w ogóle nie
sprawdziwszy, na ile to się zgadza z prawdą. Chodzi o to, że pleciesz je mimo
tego, że ja już kiedyś całkiem niedawno WŁAŚNIE NA TEJ GRUPIE wyjaśniałem, że
w C++ wcale tak nie jest.
Żeby nie było wątpliwości, odtworzyłem twoją hierarchię, przedstawioną wyżej,
w C++; masz, weź sobie to skopiuj, skompiluj i odpal. A potem wyślij grzecznie
list z przeprosinami.
#include <iostream>
#include <string>
using namespace std;
struct IPojazd
{
virtual string getNazwa() = 0;
virtual ~IPojazd() {}
};
struct IPojazdPlywajacy: IPojazd
{
virtual double getWypornosc() = 0;
};
struct IPojazdLatajacy: IPojazd
{
virtual double getMaxUdzwig() = 0;
};
struct Awionetka: IPojazdPlywajacy, IPojazdLatajacy
{
string getNazwa() { return "Awionetka"; }
double getWypornosc() { return 9.76; }
double getMaxUdzwig() { return 774.52; }
};
int main( int argc, char** argv )
{
Awionetka* aw = new Awionetka;
IPojazdLatajacy* lat = aw;
IPojazdPlywajacy* plyw = aw;
cout << plyw->getNazwa() << endl;
cout << lat->getNazwa() << endl;
delete aw;
return 0;
}
> Jest to dość istotna różnica, aczkolwiek można się sprzeczać np. które
> podejście jest "bardziej intuicyjne", "właściwsze", a które
> "bezpieczniejsze".
No to już widzisz teraz, które jest bardziej intuicyjne, właściwsze,
bezpieczniejsze i w ogóle fajniejsze i lepsze.
> Zgodnie z definicją interfejsów "właściwsze" a takze IMHO bardziej
> intuicyjne
> jest to, co oferuje Java, natomiast bezpieczniejsze jest to, co oferuje C++,
> tym bardziej, że ta odmiennosćć jest łatwa do "obejścia" prostymi
> konstrukcjami
> programistycznymi (delegacja wywołań).
Tym bardziej, że oba języki oferują to samo (w tej kwestii).
> Nie da się jednak rzetelnie porównać idei interfejsu z ideą klasy
> abstrakcyjnej porównując jedynie niskopoziomowe realizacje tych konstrukcji
> w różnych językach. Różnice powiem są ideowe, koncepcyjne i dotyczą np.
> kontekstu użycia jednych i drugich, sposobu projektowania aplikacji z
> użyciem
> jednych i drugich.
Przestań bełkotać i zacznij mówić coś konkretnego. Na razie tylko potwierdzasz
tezę, że jeśli ktoś chce udowodnić że ma rację i czuje, że mu się to raczej
nie uda, to trzeba dobierać takie słowa, żeby rozmówca poczuł się jak idiota.
Oj, Brunner...
> Interfejs - zgdnie z samą nazwą - jest interfejsem, czyli
> czystą definicją "czegoś", szablonem, definicją protokołu komunikacyjnego,
> widokiem na COŚ, niekoniecznie na INSTANCJĘ KONKRETNEJ KLASY. To ostatnie
> chciałbym wyraźnie podkreślić.
Dopsze. Niech i tak będzie, ale jeśli rozważamy interfejs w kontekście
obiektowym, bo tylko w takim możemy rozważać porównanie go z klasą
abstrakcyjną, to skupmy się na interfejsie, którego intencją jest definiować
metody dostępne dla klasy, która ten interfejs implementuje.
> W Javie interfejsami można się posługiwać nie tworząc żadnych klas, które by
> je implementowały.
O, to już jest bardzo ciekawe.
> Można dynamicznie
> tworzyć obiekty rzutowalne na dowolne interfejsy bez jawnego deklarowania
> i definiowania klas implementujacych te interfejsy (obiekty te są
> instancjami
> nieokreślonych klas, czyli patrząc z odpowiedniej wysokości - nie są
> instancjami
> klas, w Javie nazywa sieje "dynamic proxy class").
Taaa... trochę wypasionej technologii i jeszcze wypasieńszego słownictwa i
można już zacząć mówić, że niby mamy interfejs i rzutujemy na ten interfejs,
ale klasa obiektu, który nas interesuje nie implemetuje tego interfejsu. Tzn.
niby implementuje (kurwa, pewnie że implementuje, bo inaczej byś nie
zrzutował!), ale nie ma definicji tej klasy. Tzn. niby jest (kurwa, pewnie że
jest, bo gdyby nie było klasy, to by nie było i obiektu), ale jej nie widać.
Tzn. niby widać (kurwa, nie będę kończył, bo i tak wiadomo o co chodzi)...
> Określając na sztywno interfejs, który stanowi wspólny protokół komunikacji
> między jakimiś elementami (funkcjami) systemu możemy sobie bardzo daleko
> pofantazjować w jaki sposób dany interfejs zrealizujemy, w jaki sposób
> dostarczymy do obiektu za nim ukrytego wszystkie zadeklarowane własności.
> Szczególy techniczne:
> http://java.sun.com/j2se/1.3/docs/guide/reflection/proxy.html
Nie wątpię, że właściwości jakie dano klasom w Javie znacznie wykraczają ponad
to, co dano klasom w C++.
Ale to naprawdę NIE MA NIC DO RZECZY. Jestem w stanie sobie wyobrazić
bibliotekę, która z klasami robi takie obroty, co Java. Zresztą próbował to
robić COM i ATL. Rzutowanie na konkretny interfejs odbywało się poprzez
sprawdzenie m.in. UUID-ów interfejsu, który został zapisany w danych obiektu,
przy czym oczywiście obiekt charakterystyczny klasy wyglądał już zupełnie
inaczej, niż w zwykłym C++-owym obiekcie klasy polimorficznej. Zupełnie
inaczej wyglądała też składnia, choć odpowiedni lukier składniowy dało się
dorobić (to, czego moim zdaniem w tym lukrze, typu CComPtr, brakowało, to
powiązanie interfejsu z UUID-em tak, żeby nie trzeba było podawać jednego i
drugiego razem jawnie).
Oczywiście realizacja interfejsów w czymś takim była "standardowa", czyli
interfejs to klasa czysto abstrakcyjna.
> Ta cecha już bardzo istotnie wykracza poza standardową definicję klasy i
> przekłada się to bezpośrednio na sprawy niskopoziomowe: w C++ nie jest
> wbudowany żaden mechanizm wspierajacy podobne użycie klas abstrakcyjnych
> (czyli nie ma możliwości "dynamicznej implementacji metod").
To nie jest żadna dynamiczna implementacja metod. Dynamiczna implementacja
metod polega na możliwości podmiany metody w trakcie działania programu. A to,
o czym pisałeś tutaj, to może być co najwyżej "dynamic dispatch".
> Podsumowując: z punktu widzenia C++ być może, abu zrozumieć interfejsy,
> dobrze jest je porównać do klas czysto abstrajcyjnych. Natomiast punkt
> widzenia Javy jest inny.
To wszystko to jest jeden wielki bełkot. Pozwolisz, że wypowiedź nt. tego,
czego może się programista Javy spodziewać w C++, wytnę, bo jeszcze parę
takich wypowiedzi i chyba mnie trafi.
Dyskutowaliśmy nt. tego, czy interfejsem może być klasa czysto abstrakcyjna.
To była dyskusja czysto teoretyczna i odpowiedź tak samo powinna nadejść w
kategoriach abstrakcji. Rzecz jasna bez konkretnych przykładów rozmawiać się
nie da, ale przykłady mają naprowadzać na właściwe rozwiązanie, a nie je
uzasadniać. Jeśli je uzasadniają, to znaczy że rozwiązanie nie mieści się w
granicach abstrakcji i jest zasadne tylko dla konkretnego specyficznego
przykładu. Do ogólnego twierdzenia to ma się wszakoż nijak: każdy sobie w
każdym konkretnym języku może nazwać klasą, obiektem, czy interfejsem, co też
mu się żywnie podoba, a zasady, jakimi się rządzą owe pojęcia i tak będą dla
tych języków w najlepszym razie pod kątem 30 stopni.
Na uzasadnienie, dlaczego dylemat, czy interfejsem może być klasa czysto
abstrakcyjna, jest zasadny, przytaczasz sposób realizacji interfejsów w C++.
Na dowód, dlaczego tak być nie może, przytaczasz właściwości dynamicznego
rzutowania obiektów w oparciu o interfejsy, dostępnego w Javie. De facto w
dupie masz, co te pojęcia w rzeczywistości oznaczają i jakie posiadają
właściwości i zależności; interesuje cię tylko sposób ich realizacji w
konkretnym języku programowania. No oczywiście, że jeśli za definicję
interfejsu uważasz to, co się interfejsem nazywa w Javie, to jesteś w stanie
na podobnej zasadzie udowodnić dowolną głupotę.
A żeby to wszystko było jeszcze bardziej śmieszne, to na samym początku
zastrzegasz, że interfejs niekoniecznie musi być pojęciem z dziedziny
obiektowości. Po czym uzasadniasz to dynamicznym rzutowaniem w Javie. Mniej
więcej tak, jakbyś na końcu zapomniał, o czym mówiłeś na początku.
Tymczasem dylemat, czy interfejsem może być klasa czysto abstrakcyjna dotyczy
zupełnie innych kategorii i kontekstów.
Po pierwsze, jeśli już bierzemy pod uwagę dziedzinę wtórnej obiektowości, to
jak najbardziej interfejs i klasa są pojęciami różniącymi się od siebie. Ale w
zasadzie na tym stwierdzeniu cała sprawa się kończy.
Różnica jednak bierze się nie z faktu, że różnią się one sposobem realizacji,
tylko stąd, że różnią się one intencjami, dla których zostały one stworzone.
To prawda, że klasy abstrakcyjne nie mogą i tak tworzyć obiektów i nadają się
tylko do tego, by tworzyć inne klasy. W takim podejściu nie określa się
szczególności klasy czysto abstrakcyjnej jako szczególnego przypadku klasy
abstrakcyjnej, dopóki ktoś nie wpadnie na pomysł, że z tej szczególności można
w jakiś sposób skorzystać. Dopóki jednak mówimy o klasie, to nie mamy na myśli
nic innego, jak pojęcie uczestniczące w hierarchii, tworzące inne klasy oraz
obiekty będące ich instancjami.
Jest tutaj tylko jeden problem polegający na tym, że zasadniczo wielorakie
dziedziczenie jest niemożliwe. Hierarchia klas powinna dawać odpowiedź na
pytanie, kto jest czyim przodkiem oraz czy obiekt jest instancją danej klasy.
Jeśli mamy klasy, powiedzmy, Stream i dziedziczący po nim File, to w takim
razie obiekt otworzony przez klasę File jest instancją klasy File oraz klasy
Stream. Ale jeśli zachodzi taka sytuacja, że obiekt jest instancją dwóch
oddzielnych klas, to w takim razie jedna musi być przodkiem drugiej
(niekoniecznie bezpośrednim). Taka jest zasada, której musi podlegać klasa,
biorąc pod uwagę, że dziedziczenie jest specjalizowaniem właściwości klasy.
Gdybyśmy zatem dopuścili wielorakie dziedziczenie, mogłaby powstać sytuacja,
że dwie klasy, których instancją jest określony obiekt, to są "siblings",
czyli zostaje złamana zasada, że jeden jest przodkiem drugiej.
I to jest właśnie najważniejszy powód, dla którego klasa czysto abstrakcyjna w
pojęciu czysto obiektowym interfejsem być nie może.
Po drugie, dylemat dotyczył głównie języka C++, ale w ogólności dotyczył
języków, w którym istnieje wielorakie dziedziczenie. I tutaj mamy taką właśnie
sytuację:
Ponieważ wielorakie dziedziczenie jest sprzeczne z obiektowością, więc jeśli
język dostarcza wielorakie dziedziczenie, to efektywnie pojęcie klasy jest w
tym języku jedynie REALIZOWANE za pomocą odpowiedniej konstrukcji językowej,
być może zwanej klasą. Inaczej mówiąc, jeśli stosujemy jednokrotne
dziedziczenie, to realizujemy pojęcie hierarchię klas. Jeśli stosujemy
dziedziczenie wielorakie, to nie realizujemy pojęcia hierarchii klas. Proste.
Jeśli język zaś nie dostarcza nam mechanizmu jawnie zwanego interfejsami, ale
dostarcza nam mechanizm mniej-więcej klas (w tym klas czysto abstrakcyjnych)
wraz z wielorakim dziedziczeniem, to możemy realizować zarówno klasy jak i
interfejsy w oparciu o te mechanizmy. I to wszystko.
--
// _ ___ Michal "Sektor" Malecki <sektor(whirl)kis.p.lodz.pl>
\\ L_ |/ `| /^\ ,() <ethourhs(O)wp.pl>
// \_ |\ \/ \_/ /\ C++ bez cholesterolu: http://www.intercon.pl/~sektor/cbx
"I am allergic to Java because programming in Java reminds me casting spells"
Technical Details
Message-ID:
<dlqam1$711$1@kujawiak.man.lodz.pl>Path:
mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!newsfeed.gazeta.pl!news.task.gda.pl!news.man.lodz.pl!not-for-mailReferences:
<dlpte4$ol9$1@news.onet.pl>Show Full Headers
Complete Article Headers
Path: mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!newsfeed.gazeta.pl!news.task.gda.pl!news.man.lodz.pl!not-for-mail
From: Sektor van Skijlen <ethouris@pl.wp.spamu.lubie.nie.invalid>
Newsgroups: pl.comp.objects
Subject: Re: Czy interfejs to to samo co klasa czysto abstrakcyjna ?
Date: Sun, 20 Nov 2005 17:10:25 +0000 (UTC)
Organization: LODMAN - Metropolitan Area Network in LODZ, Poland
Lines: 277
Sender: Michal Malecki <sektor@zly.kis.p.lodz.pl>
Message-ID: <dlqam1$711$1@kujawiak.man.lodz.pl>
References: <dlpte4$ol9$1@news.onet.pl>
NNTP-Posting-Host: zly.kis.p.lodz.pl
Mime-Version: 1.0
X-Trace: kujawiak.man.lodz.pl 1132506625 7201 212.191.89.2 (20 Nov 2005 17:10:25 GMT)
X-Complaints-To: abuse@man.lodz.pl
NNTP-Posting-Date: Sun, 20 Nov 2005 17:10:25 +0000 (UTC)
User-Agent: tin/1.6.2-20030910 ("Pabbay") (UNIX) (SunOS/5.9 (sun4u))
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit