Czy zmiany wydajności naruszają zasadę substytucji Liskowa?

14

Powiedz, że mam:

interface Thing
{
    GetThing();
}

class FastThing : Thing 
{
    public int GetThing()
    {
        return 1;
    }
}

class SlowThing : Thing
{
    public int GetThing()
    {
        return GetThingFromDatabase();
    }
}

Czy jest to naruszenie zasady substytucji Liskowa?

ConditionRacer
źródło
GetThingFromDatabase()nie jest wystarczająco wolny, aby wywołać to kontrowersje. Factor4096BitPublicKey();return 1;sprawiłoby, że rzeczy byłyby trochę bardziej interesujące.
Patrick,
1
Jeśli zastąpi FastThingsię SlowThing, LSP nie ma zastosowania. Jeśli dodasz komentarz, Thing::GetThingktóry mówi „Jest bardzo szybki”, pytanie można przedyskutować.
Głowonóg

Odpowiedzi:

14

To naprawdę zależy. Niektóre interfejsy mają na przykład ograniczenia złożoności (oczywiście nie można ich egzekwować programowo). Najbardziej podstawowym przypadkiem jest „GetThing () daje int- tzn. Zatrzymuje się”, w którym to przypadku odpowiedź brzmiałaby „Nie” - obie wersje GetThing () zatrzymują się i zwracają wartość int.

Jednak wiele interfejsów sugeruje lub wyraźnie określa gwarancje wydajności, zarówno w złożoności, jak i w czasie wolnym. Na przykład w standardzie C ++ implementacja biblioteki za pomocą wywołania blokującego jest nielegalna, chyba że standard wyraźnie na to pozwala.

DeadMG
źródło
3
Wydajność nie jest czymś wymagającym kontroli typu. To obietnica implementatora / opiekuna biblioteki.
dietbuddha,
3
Tak wyraźnie powiedziałem w mojej odpowiedzi?
DeadMG,
1
Chodziło mi o to, że jak tylko włączysz do kryteriów cokolwiek innego niż pisanie, nie mówisz już o Liskovie, ponieważ jest to specyficzne dla pisania. Chociaż „praktyka” polegająca na nie poddawaniu obiektów o różnym działaniu może być dobra, sam Liskov nie ma o tym nic do powiedzenia.
dietbuddha
7
Liskov stwierdza, że ​​w przypadku Pochodnej powinna być użyteczna wszędzie tam, gdzie jest Baza. Może to nie być prawdą, jeśli Baza gwarantuje pewne właściwości lub cechy. Na przykład w przypadku bloków pochodnych może dojść do impasu.
DeadMG,
8

TL; DR: Nie

Zgodnie z „Subtypingem behawioralnym z wykorzystaniem niezmienników i ograniczeń” (sformalizowanie zasady) chodzi przede wszystkim o „bezpieczeństwo” właściwości typu obiektu. Właściwości regulujące zastępowalność tylko w kontekście informacji o typie. Typ obiektu jest prostopadły do ​​swojej wydajności. Dlatego różnica w wydajności nie stanowi naruszenia zasady zastępowania Liskowa.

dietbuddha
źródło
3
Rzuciłem okiem na ten artykuł, ale czy jesteś pewien, że ograniczenia czasowe nie mogą być formalnie udowodnione? I nawet jeśli Liskov nie miał tego na myśli, w tym ograniczenia czasowe mogą być postrzegane jako dobre rozszerzenie klasycznego LSP, które może być istotne dla programowania w świecie rzeczywistym.
Doc Brown,
@Doc Brown: to, czy czas jest przydatny do rozważenia zamiany obiektu, czy nie, jest prostopadłe do Liskova. Aby dodać go jako wskazówkę sferyczną, ale nie może i nigdy nie będzie częścią Liskova. To tak, jakby mieć logiczne równanie logiczne i powiedzieć: Fałsz można zastąpić Prawdą, jeśli jest wystarczająco szybki. Prędkość nie ma nic wspólnego z matematyką i logiką.
dietbuddha
Przeciwprzykład: ten kod jest wywoływany w EDT Javy lub w pętli zdarzeń Node. Radykalnie wolniejsza wydajność wolnej wersji na pół zepsuje oprogramowanie. Myślę, że poprawną odpowiedzią na to pytanie jest „prawdopodobnie nie, ale są wyjątki”.
user949300
6

Jakie gwarancje daje interfejs? Ponieważ GetThingnie daje żadnych gwarancji, podtypy nie muszą tego przestrzegać.

Jeśli interfejs byłby podobny GetThingInLinearTimelub jeśli typ podstawowy jest wirtualny, a domyślną implementacją jest jedna złożoność, to pogorszenie złożoności algorytmu naruszy LSP.

Telastyn
źródło
4

Wydajność oprogramowania nie ma nic wspólnego z zasadą substytucji Liskowa.

Zasada ta dotyczy substytucji podtypów i behawioralnego wpływu substytucji tego obiektu wyłącznie w kategoriach OOP.

Dane wejściowe i wyjściowe getThing()pozostają takie same w obu przypadkach, a zarówno powolne, jak i szybkie prawdopodobnie wprowadzają obiekty w ten sam stan.

Reactgular
źródło
1

Czy ma znaczenie to, co konkretnie mówi sama zasada substytucji Liskowa? Jeśli podtyp narusza oczekiwania konsumenta nadtypu, wydaje się to złą rzeczą, niezależnie od tego, czy LSP jest bardziej restrykcyjny.

Moim zdaniem więc, czy wszystkie rozsądne oczekiwania konsumenta abstrakcji są spełnione przez podtyp, wydaje się być dobrym uogólnieniem LSP.

Jednak w opublikowanym przykładzie i ogólnie interfejsach Java nie jest jasne, czy użytkownik Thinginterfejsu ma uzasadnione oczekiwania, czy powinien on działać szybko, czy wolno. Jeśli javadocs interfejsu miałyby zawierać język na temat tego, jakie operacje mają być szybkie, to może istnieć argument za problemem ze względu na wydajność. Ale konwencja Java z pewnością dotyczy różnych implementacji mających różne charakterystyki wydajności.

trptcolin
źródło
2
o ile mogę powiedzieć, opublikowanym przykładem nie jest Java
gnat
0

Wujek Bob odpowiedział na bardzo podobne pytanie, w którym stwierdził, że naruszenie LSP wymaga 3 stron:

Typ T, podtyp S i program P, który używa T, ale otrzymuje instancję S.

Zaryzykowałbym przypuszczenie, że to pytanie ma podobną strukturę jak to, na które odpowiedział, ponieważ nie wspomina o P, które używa T i jakie zachowanie P oczekuje.

Możesz znaleźć jego odpowiedź tutaj . (Musisz przewinąć w dół i poszukać odpowiedzi od użytkownika o nazwisku Robert Martin)

TMc
źródło
1
Jak to odpowiada na zadane pytanie?
komar
@gnat Ponieważ zadane pytanie jest niekompletne. Potwierdzenie naruszenia LSP wymaga 3 stron. Z czego dostarczył tylko 2 strony.
TMc