Kwadrat dziedziczy po prostokącie - pytanie

0
zkubinski napisał(a):

@Pebal Odpada więc możliwość rozbudowy istniejących klas

dlaczego ? Uważam, że nie. Bo wirtualne składowe służą do przesłaniania ich własnymi wersjami. Więc jak uznasz za stosowne, to w jakiejś klasie możesz napisać swoją wersję jakiejś metody i tyle. Przykładu nie podam, bo na chwilę obecną nic mi do głowy nie przychodzi.

Dlatego, że Liskov zabrania. Odnoszę czasem wrażenie, że programiści zwyczajnie lubią sobie kompilować życie.

0

@Pebal Zdecyduj się. Albo można tak dziedziczyć, bo cele szkolenia, zobrazowanie możliwości języka, itp., albo nie można bo wyobraźnia ponosi.

nie umiesz wyważyć co jest ok, a co nie jest ok - jeżeli przykład pomaga dydaktycznie w zrozumieniu "idei" OO to czemu miałby on nie być wykorzystany do przekazania zrozumienia dziedziczenia i metod wirtualnych ?

@Pebal Dlatego, że Liskov zabrania. Odnoszę czasem wrażenie, że programiści zwyczajnie lubią sobie kompilować życie.

nie znam jakiegoś liska i nie wiem kto to. Ale ja czerpałem wiedzę z innych źródeł. Chociażby z Qt gdzie jest dużo możliwości nadpisywania metod wirtualnych które istnieją w klasach abstrakcyjnych.

0
zkubinski napisał(a):

nie umiesz wyważyć co jest ok, a co nie jest ok - jeżeli przykład pomaga dydaktycznie w zrozumieniu "idei" OO to czemu miałby on nie być wykorzystany do przekazania zrozumienia dziedziczenia i metod wirtualnych ?

Ależ ja nic nie mam do dydaktycznych przykładów.

@Pebal Dlatego, że Liskov zabrania. Odnoszę czasem wrażenie, że programiści zwyczajnie lubią sobie kompilować życie.

nie znam jakiegoś liska i nie wiem kto to. Ale ja czerpałem wiedzę z innych źródeł. Chociażby z Qt gdzie jest dużo możliwości nadpisywania metod wirtualnych które istnieją w klasach abstrakcyjnych.

Liskov to kobieta, która w 1988 określiła zasadę LSP: "If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T."

0

ogólnie z ciekawości czytam o tej zasadzie Liskov w polskiej wiki i mam wrażenie, że kobiecina się zapętliła albo ja czegoś nie rozumiem... biorąc za przykład dziedziczenia prostokąt<-kwadrat

to siłą rzeczy dojdzie się do poniższej konkluzji

W takim przypadku, oprogramowanie musiałoby sprawdzać typ obiektu który zmienia, żeby wiedzieć w jaki sposób obiekt będzie się zachowywał po wprowadzeniu zmian. Taki styl programowania wprowadza dodatkowe zależności w kodzie i utrudnia jego późniejsze utrzymanie i rozwijanie.

ostatnie zdanie wszystko tłumaczy o tej jej zasadzie... - o ile dobrze zrozumiałem tą "jej zasadę". No chyba, że przyjmiemy tak jak matematycznie powinno być, że kwadrat to inna figura, które ma swoje własności które są niezależne od prostokąta... jak już wspomniałem kilka postów wyżej czemu kwadrat jest "szczególnym wypadkiem prostokąta"

3

LSP mówi tyle, że wszystkie klasy dziedziczące z klasy bazowej muszą spełniać kontrakt klasy bazowej. Czyli jeśli klasa prostokąt wymaga aby dało się obliczyć pole, to podklasa nie może sobie tej metody pokryć metodą zwracającą null albo rzucającą wyjątek. Czyli nie można DOWOLNIE pokrywać metod. Można jedynie w zakresie dozwolonym przez kontrakt.

Nie wiem, czy LSP tego też dotyczy, ale zwykle wymaga się jeszcze silniejszej zasady - wywołania metod odziedziczonych z klasy bazowej nie mogą naruszać kontraktu ani klasy bazowej ani pochodnej.

Dlatego nie da się zgodnie z LSP odziedziczyć kwadratu z mutowalnego prostokąta - bo mutowalny prostokąt pozwala zmieniać długość każdego boku niezależnie, a kwadrat nie.

0
Krolik napisał(a):

Nie wiem, czy LSP tego też dotyczy, ale zwykle wymaga się jeszcze silniejszej zasady - wywołania metod odziedziczonych z klasy bazowej nie mogą naruszać kontraktu ani klasy bazowej ani pochodnej.

Dlatego nie da się zgodnie z LSP odziedziczyć kwadratu z mutowalnego prostokąta - bo mutowalny prostokąt pozwala zmieniać długość każdego boku niezależnie, a kwadrat nie.

Ależ da się, wystarczy osłabić kontrakt w klasie bazowej. Cała sprawa rozbija się o to, że nazwa Rectangle sugeruje siłę kontraktu. Tyle tylko, że to nie nazwy klas decydują o sile kontraktów.

3

A powinny — bo jak masz coś, co robi jedno, a nazywa się jak coś innego, to to jest proszenie się o błędy…

0
Althorion napisał(a):

A powinny — bo jak masz coś, co robi jedno, a nazywa się jak coś innego, to to jest proszenie się o błędy…

Czy obiekt klasy Rectangle nie jest kiedyś prostokątem? Oczywiście, że zawsze jest prostokątem. Czy jak mam metodę Save, to ona może nie zapisać danych? Może ich nie zapisać, choć jej nazwa sugeruje coś innego. Czy metoda setSize może działać analogicznie jak Save? Oczywiście, że tak, bo żaden standard tego nie zabrania. Jak widać, wszystko rozbija się o przyjęte założenia.

3

Oczywiście że „wszystko rozbija się o przyjęte założenia”: żadne prawa fizyki nie wymuszają rozsądnego kodu, trzeba o to zadbać samemu — mieć te założenia sensowne, a nie z kapelusza wyjęte.

Jak Cię najdzie ochota, to sobie możesz „przyjąć założenie”, że klasy nazywasz po prezydentach USA — ale chyba nie o to chodzi, żeby coś robić, bo można, tylko żeby ułatwiać życie sobie i innym…

0
Althorion napisał(a):

Oczywiście że „wszystko rozbija się o przyjęte założenia”: żadne prawa fizyki nie wymuszają rozsądnego kodu, trzeba o to zadbać samemu — mieć te założenia sensowne, a nie z kapelusza wyjęte.

Jak Cię najdzie ochota, to sobie możesz „przyjąć założenie”, że klasy nazywasz po prezydentach USA — ale chyba nie o to chodzi, żeby coś robić, bo można, tylko żeby ułatwiać życie sobie i innym…

Kod, nie jest lepszy tylko dlatego, że ma ograniczoną funkcjonalność. Pełne kontrolowanie parametrów obiektów z zewnątrz to jedna z możliwych metod a nie jedyna. W dodatku nie zawsze najlepsza. Samolot może nie mieć skrzydła i komenda wychylenia lotki w brakującym skrzydle się nie powiedzie. Kto powinien o tym decydować, samolot, czy operator lotek?

[Edit]

I jeszcze jedno pytanie. Jak się ma powyższe do języków deklaratywnych? W QML mogę napisać tak:

Rectangle {
    height: width
}	

by sprawić, aby prostokąt stał się kwadratem. Za taką funkcjonalność ludzie kochają ten język, ale pewnie dlatego, że nikt im nie powiedział, że tak jest źle.

3

Kto powinien o tym decydować, samolot, czy operator lotek?

Kompilator. Kod próbujący sterować lotkami w nieistniejącym skrzydle nie powinien się nigdy skompilować.

2

Wracam po kilku dniach żeby zobaczyć czy coś ciekawego wyszło z tych dyskusji a tu nadal @Pebal udowadnia wszystkim na forum że się mylą.
Widac wszyscy na forum mają źłe ale takie same rozumowanie do zasady liskova ;)
Teraz nie dziwię się że niektórzy tak bardzo nie lubią OOP jak dostają takie miny w kodzie ;p

Z tą kontrolką TextView i PaswordTextView to nie rozumiem w czym problem?

  1. Jeżeli stworzysz PasswordTextView dziedziczące po TextView to jedyne co zmodyfikujesz to warstwe prezentacji. Kontrolka dalej będzie zachowywać się tak samo jeżeli przekażesz ją do metod np walidacji. Przecież "getText"/ "setText" dla jednej i drugiej zachowa się tak samo. No chyba że getText() dla password miał by zwracać gwiazdki :D

  2. Android nie rozdziela tych kontrolek na dwie osobne, masz TextView z metodą input i od input zalezy metoda wprowadzania, wiec tutaj zamiast dziedziczenia można powiedzieć że jest wstrzykiwanie "strategi" i po problemie

  3. I wreszcie ostatnia sprawa to jeżeli text view miało by być na prawdę secure, a nie tylko gwiazdkować to nie mogło by w ogóle dziedziczyć po textView PasswordTextView

1
Krolik napisał(a):

Kto powinien o tym decydować, samolot, czy operator lotek?

Kompilator. Kod próbujący sterować lotkami w nieistniejącym skrzydle nie powinien się nigdy skompilować.

Że co? Nie rób sobie proszę jaj, skrzydło traci się dynamicznie.

4

@Pebal: Uciekasz w metafory tak daleko, że tracisz kontakt z docelowym zagadnieniem. Samolot może stracić skrzydło w wyniku niemożliwych do przewidzenia i zapobieżenia czynników, ale niemal zawsze nie ma to miejsca w przypadku obiektów — obiekt nie przestanie nagle i samoistnie potrafić próbować łączyć się z bazą danych (co najwyżej może mu się to przestać udawać, ale nie zatraci tej metody); nie straci wiedzy, ile wartości przechowuje; pozostanie sensowne odpytywanie o uprawnienia danego użytkownika…

Rzadko ma sens tworzenie klasy Samolot, która może nie mieć skrzydeł — to nakłada na użytkownika niepotrzebny narzut mentalny, wymusza każdorazowe sprawdzanie, czy tym Samolotem można zrobić to, co się zamierzyło. To tak, jakbyś chciał wynająć ten samolot, żeby gdzieś polecieć, firma wynajmująca ci podsunie umowę do podpisania i radośnie wręczy „kluczyki” do takiego bez skrzydeł — no co, w końcu to dalej jest samolot, a że bezużyteczny?
W Twoim interesie, jako osoby projektującej jakąś klasę, jest dostarczenie klasy, która robi to, co sugeruje jej nazwa. Jeśli wiesz, że czasem nie jesteś, to nie zwracaj Samolotu, tylko Maybe<Samolot>, czy jaką tam masz konstrukcję w języku — w każdym razie, wyraźnie zasugeruj, że możesz otrzymać coś, co nie spełnia zamierzeń.

Bo w przeciwnym wypadku będziesz operował na mocno niepewnych obiektach — zatem naklepiesz bardzo dużo kodu, który się co chwila upewnia, że z instancjami wszystko dalej OK, a napisany „oczywisty” kod, który tego nie robi, będzie błędny.

No i wreszcie — odwracasz kota ogonem. Nie ma nic złego w tym, że Prostokąt będzie kwadratem. Jest natomiast sporo złego, gdy nagle Kwadrat okazuje się być Prostokątem… Nikt Ci nie mówi, że „każda instancja powinna być obiektem klasy tak nisko w łańcuchu dziedziczenia, jak się da”. Zasada Liskow to nie to, tylko „gdy klasa potomna jest traktowana jako klasa nadrzędna, wszystkie jej metody i atrybuty muszą mieć sens dla osoby, która nie wie, do jakiej klasy należą”.
Czyli jak masz coś, co jest instancją klasy, która dziedziczy po Prostokąt, to jej długość musi oznaczać długość, szerokość — szerokość, a .pole() ma liczyć pole. Nie interesuje cię w tym momencie, czy to jest kwadrat czy nie, tylko czy można z klas potomnych korzystać jak z klas bazowych, a nie okaże się, że nagle .pole() mi liczy obwód…

0
xxx_xx_x napisał(a):

Wracam po kilku dniach żeby zobaczyć czy coś ciekawego wyszło z tych dyskusji a tu nadal @Pebal udowadnia wszystkim na forum że się mylą.
Widac wszyscy na forum mają źłe ale takie same rozumowanie do zasady liskova ;)

Nie mam już ochoty walczyć z wiatrakami. Liskov nie pozwala łamać kontraktu, ale kontrakt się ustala. Jeżeli tego nie rozumiesz to Ty masz problem, nie ja. Napisz do twórców Qt, że QML jest oparty na złych założeniach, bo jego kontrakty nie spełniają Twoich założeń. Zostańmy przy swoim i skupmy się na tworzeniu czegoś pożytecznego.

Wesołych Świąt Wszystkim!

2

Po pierwsze, Twój przykład nijak nie łamie zasady Liskov — ani żadnej innej sensownej zasady, o której bym wiedział…
Po drugie, za przestrzeganie zasad projektowych — i w ogóle tworzenie sensownego projektu — odpowiada tylko i wyłącznie projektant, a nie język, którego używa. Nie jest niczym nagannym to, że w jakimś języku można stworzyć potworka, nie jest też zadaniem języka przestrzeganie Cię przed tym, żebyś klas nie nazywał w idiotyczny sposób czy miał nonsensowne dziedziczenie. Język wręcz nie powinien tego robić, by nie ograniczać użytkowników tam, gdzie faktycznie może im to być do czegoś potrzebne.

1 użytkowników online, w tym zalogowanych: 0, gości: 1