Czy można zaimplementować programowanie „obiektowe” bez słowa kluczowego class?

29

Powiedzmy, że chcemy przedstawić abstrakcję „konta” w banku. Oto jedno podejście, używając functionobiektu w Pythonie:

def account():
    """Return a dispatch dictionary representing a bank account.

    >>> a = account()
    >>> a['deposit'](100)
    100
    >>> a['withdraw'](90)
    10
    >>> a['withdraw'](90)
    'Insufficient funds'
    >>> a['balance']
    10
    """
    def withdraw(amount):
        if amount > dispatch['balance']:
            return 'Insufficient funds'
        dispatch['balance'] -= amount
        return dispatch['balance']
    def deposit(amount):
        dispatch['balance'] += amount
        return dispatch['balance']
    dispatch = {'balance': 0,
                'withdraw': withdraw,
                'deposit': deposit}
    return dispatch

Oto inne podejście z wykorzystaniem abstrakcji typu (tj. classSłowa kluczowego w Pythonie):

class Account(object):
    """A bank account has a balance and an account holder.

    >>> a = Account('John')
    >>> a.deposit(100)
    100
    >>> a.withdraw(90)
    10
    >>> a.withdraw(90)
    'Insufficient funds'
    >>> a.balance
    10
    """



    def __init__(self, account_holder):
        self.balance = 0
        self.holder = account_holder

    def deposit(self, amount):
        """Add amount to balance."""
        self.balance = self.balance + amount
        return self.balance

    def withdraw(self, amount):
        """Subtract amount from balance if funds are available."""
        if amount > self.balance:
            return 'Insufficient funds'
        self.balance = self.balance - amount
        return self.balance

Mój nauczyciel rozpoczął temat „Programowanie obiektowe”, wprowadzając classsłowo kluczowe i pokazując nam następujące punkty:

Programowanie obiektowe

Metoda organizacji programów modułowych:

  • Bariery abstrakcji
  • Przekazywanie wiadomości
  • Łączenie informacji i powiązanych zachowań

Czy uważasz, że pierwsze podejście wystarczyłoby, aby spełnić powyższą definicję? Jeśli tak, dlaczego potrzebujemy classsłowa kluczowego do programowania obiektowego?

nadmierna wymiana
źródło
2
Cieszę się, że się zgadzasz. =) Chociaż nie znam wystarczająco dobrze Pythona, aby udzielić dokładnej odpowiedzi, być może zainteresuje Cię fakt, że w JavaScript typowy sposób wykonywania OOP jest podobny do opisanego przez ciebie „obiektu funkcyjnego” (chociaż mamy również dziedzictwo prototypowe, które pozwala obiektom na dzielenie się metodami zamiast posiadania osobnych kopii każdej metody na każdym obiekcie; zakładam, że Python classdokonuje podobnej optymalizacji).
Ixrec
Jeśli chcesz uzyskać szczegółową odpowiedź, powinieneś zadać kolejne pytanie lub dołączyć do pokoju rozmów, ale krótka odpowiedź brzmi (jeśli całkowicie zignorujesz dziedziczenie prototypowe, tablice itp.), To w zasadzie prawda; większość obiektów JS to nic innego jak słowniki kluczy łańcuchowych do dowolnych wartości. foo.bar()jest zwykle identyczna foo['bar'](), a w rzadkich przypadkach ta ostatnia składnia jest faktycznie przydatna.
Ixrec
8
To jest naprawdę ważne pytanie na drodze do podstawowego zrozumienia OOP. Jeśli jesteś zainteresowany, możesz przeczytać mój blog, w którym tworzę prosty system obiektowy w JavaScript bez polegania na żadnej z części OOP języka. Twój pierwszy przykład ma ważną wadę: w miejscu, w którym piszesz object['method'](args), obiekty Pythona faktycznie odpowiadają object['method'](object, args). Staje się to istotne, gdy klasa podstawowa wywołuje metody w klasie potomnej, np. We Wzorze Strategii.
amon
13
Jak zauważyli inni, jest to spostrzegawcze pytanie dotyczące OOP. Przy tej okazji zauważę jednak, że nie tak naprawdę prawdziwe banki reprezentują konta bankowe. Banki nie mają modyfikowalnego obiektu „konta”, który zmienia się w momencie obciążenia i uznania; mają listę transakcji tylko do zapisu, a następnie obliczają saldo z listy transakcji. Jako dobre ćwiczenie spróbuj wdrożyć ten mechanizm w różnych językach.
Eric Lippert

Odpowiedzi:

66

Gratulacje! Odkryłeś dobrze znany fakt, że orientacji obiektowej można dokonać bez obsługi określonego języka programowania. Jest to w zasadzie taki sam sposób wprowadzania obiektów w Schemacie w tej klasycznej książce . Zauważ, że Schemat nie ma classsłowa kluczowego ani innego odpowiednika, a obiekty można tworzyć bez równych klas.

Jednak paradygmat zorientowany obiektowo był tak skuteczny, że wiele języków - i Python nie jest wyjątkiem - zapewnia wbudowane wsparcie dla niego. Ma to na celu ułatwienie programistom korzystania z paradygmatu i zapewnienie standardowej formy orientacji obiektowej dla tego języka. Jest to zasadniczo ten sam powód, dla którego wiele języków udostępnia forpętlę, chociaż można ją emulować za pomocą whilepętli z jednym lub dwoma dodatkowymi wierszami kodu - po prostu łatwość użycia .

Doktor Brown
źródło
„aby zapewnić standardową formę orientacji obiektowej dla tego języka” Czy słyszę krytykę JavaScript? ;)
jpmc26
1
@ jpmc26: nie celowo. I wydaje się, że istnieją pewne powszechnie akceptowane standardy tworzenia obiektów w JavaScript.
Doc Brown
@overexchange: czy masz pytanie?
Doc Brown
1
@overexchange: Cóż, to, co oznacza OOP, jest dyskusyjne, istnieją różne szkoły myślenia, ale definicja SICP jest prawie taka sama jak w 3 punktatorach twojego pytania. Na pewno chodzi o budowanie abstrakcji, ale nie zapominaj o punktach 2 i 3. Tak, koncepcja OOP obejmuje „zmianę stanu”, ale pozwala także na koncepcję „niezmiennych obiektów” (takich jak klasa string w Javie lub C #, Python ma także zmienne i pewne niezmienne typy danych). I twój pierwszy przykład w twoim pytaniu potwierdza tę definicję, jak również twój drugi przykład.
Doc Brown
2
@overexchange: to sięga do definicji orientacji obiektowej Alaina Kaya (wynalazcy małego języka mówienia). Pełną odpowiedź znajdziesz w tym stackoverflow.com/questions/2347973/... poprzednim artykule SO. IMHO „komunikat przesyłany między obiektami” w sensie SICP oznacza po prostu brak bezpośredniego dostępu do wewnętrznych danych obiektu, tylko poprzez „zdefiniowany protokół komunikacyjny”. W językach OO, takich jak Python, może to po prostu oznaczać „wywołanie metody obiektu”.
Doc Brown
13

Zgadzam się, że pierwsza definicja odpowiada trzem punktom, które przedstawił twój nauczyciel. Nie sądzę, żebyśmy potrzebowali słowa kluczowego klasy do czegokolwiek. Czym innym jest obiekt, ale struktura danych z różnymi typami danych i funkcjami do pracy z danymi? Oczywiście funkcje są również danymi.

Chciałbym pójść jeszcze dalej i powiedzieć, że programowanie obiektowe nie zależy tak bardzo od słów kluczowych , jakie zapewnia Twój język, ale możesz programować obiektowo w C, jeśli chcesz! W rzeczywistości jądro Linuksa stosuje takie techniki.

Ze słowa kluczowego class można wywnioskować, że język zapewnia obsługę tego rodzaju konstrukcji od razu po wyjęciu z pudełka i nie trzeba przechodzić przez wszystkie czynności, aby samodzielnie wdrożyć tę funkcję (co jest dość zabawnym zadaniem w samo!). Nie wspominając już o całym cukrze syntaktycznym, który możesz dostać.

Zavior
źródło
co z dziedziczeniem? Czy mamy kluczowe znaczenie dla podtypów / nadtypów w czasie rzeczywistym? Moje pierwsze podejście może tego nie zabawiać !!
nadmierna wymiana
5
Dziedziczenie nie jest w żaden sposób wymagane dla OOP. Możesz wdrożyć dziedziczenie również w pierwszym przykładzie. Może nie być bardzo „czysty”, ale mimo to możliwy.
Zavior
3
@Zavior ten komentarz przywodzi mi na myśl VB6. Zorientowany obiektowo bez dziedziczenia rzeczywiście sprawia, że ​​kod jest mniej czysty, delikatnie mówiąc.
RubberDuck
1
@overexchange Kiedy się nad tym zastanowić, dziedziczenie polega na współużytkowaniu wspólnego kodu / zachowania między klasami. Nic nie stoi na przeszkodzie, aby cały czas powtarzać cały ten kod. Byłoby jednak bardzo okropne w utrzymaniu. Istnieje powód, dla którego istnieje dziedzictwo :)
Zavior
1
@Zavior W swojej najbardziej podstawowej formie „podklasa” jest abstrakcją, która mówi „zanim zwrócisz funkcję wysyłania i posiadania danych wyższego rzędu, którą tu definiuję (którą będziemy udawać, że jest„ klasą ”ha ha ha), utworzyć wystąpienie funkcji „nadklasy” o funkcji wysyłania i posiadania danych, o której mowa w ThisParentFoo ”. To naprawdę wszystko. Jeśli chodzi o naiwne wielokrotne dziedziczenie, to tak naprawdę jest nadal wszystko, ale z zastrzeżeniem, że wprowadzasz „problem diamentów”, dlatego wielokrotne dziedziczenie jest do bani.
zxq9
9

Oczywiście, że możesz!

Język samoprogramowania jest dynamicznym, obiektowym językiem zorientowanym na prototypy, w którym wszystko jest przedmiotem i nie ma poczucia klas ani w ogóle. Koncentruje się na idei prototypowych obiektów i klonowaniu ich zamiast klas jako szablonów tworzenia obiektów.

Aby uzyskać więcej informacji, sprawdź http://www.selflanguage.org/ Myślę, że jest to bardzo interesujące i jeśli lubisz OOP, dobrym pomysłem jest sprawdzenie czegoś, co nie jest tak powszechne.

DraQ
źródło
0

Nie zawsze: zależy od języka. Udowodniłeś, że potrafisz to zrobić w Pythonie, ale (jeśli twoje pytanie ma być niezależne od języka pomimo znacznika Python), nie wszystkie języki mogą to zrobić. Na przykład Java najczęściej nie może. Ignorując klasę zawierającą main, nie można zdefiniować dowolnych metod / pól w obiekcie zdefiniowanym w main bez słowa kluczowego class. Chociaż istnieją anonimowe klasy, wymagają one interfejsu i nie mogą mieć żadnych publicznych członków oprócz tych zdefiniowanych w interfejsie. Chociaż możliwe jest zdefiniowanie niestandardowych interfejsów, a następnie utworzenie dla nich anonimowych klas, jest to faktycznie takie samo (ale mniej wygodne) niż zwykłe użycie klasy.

Doc Brown ma świetną odpowiedź, ale próbuję wyjaśnić, że jestem pewien, że istnieje co najmniej jeden język, który w ogóle nie pozwoli na twoje rozwiązanie.

SkySpiral7
źródło
Jako początkujący, aby nauczyć się koncepcji programowania obiektowego, tak, staram się być agnostykiem językowym. Myślę, że „Doc Brown” udzielił odpowiedzi w tych samych liniach, powiedział mi, że przeczytałem sicp text-chap3, który nie ma nic wspólnego z żadną składnią języka.
nadmierna wymiana
Chciałbym wymienić nazwę języka, który absolutnie wymaga używania klas w celu potwierdzenia mojej odpowiedzi. Ale znam tylko kilka języków i niestety Java pozwala na obejście. C ++ ma struktury, a JavaScript pozwala na to, co zademonstrowałeś. Podejrzewam, że Smalltalk i Eiffel mogą wymagać zajęć, ponieważ słyszę, że są ściśle skonstruowane.
SkySpiral7
Jako doktor Brown, gdybym nauczył się korzystać ze schematu, nie zadałbym tego pytania. Niestety, wersja kursu SICP, której się uczę, używa języka Python.
nadmierna wymiana
1
Każdy prawidłowy program Java musi zawierać classsłowo kluczowe, więc nie jest to niespodzianką. Ale absolutnie możesz zaimplementować swój własny system obiektowy na systemie obiektowym Java, chociaż nie wiem, dlaczego chcesz to zrobić.
Brian Gordon,
1. Java jest naprawdę wyjątkowa pod tym względem, ponieważ po prostu odrzuciła wszystkie inne słowa kluczowe, które mogłyby zostać użyte do tworzenia niestandardowych struktur danych. Prawie wszystkie inne znane mi języki mają zapisy lub zamknięcia. 2. Nawet w Javie możesz programować w pamięci zbudowanej z tablicy. I można wdrożyć orientację na cel w tym, używając classsłowa kluczowego tylko dlatego, że język wymaga, aby umieścić swoje funkcje w klasach. Oczywiście jest to niezwykle teoretyczne, ale nawet w Javie możesz wykonywać Object Orientation bez wbudowanych klas!
cmaster
0

Definicja twojego nauczyciela całkowicie pomija najważniejszy punkt programowania obiektowego, co sprawia, że ​​jest on użyteczny i wyjątkowy. „Przekazywanie wiadomości” to bzdury wymyślone przez ludzi z Smalltalk, a wszędzie tam, gdzie próbowano, ponieśli porażkę. Prawdziwa moc OOP jest czymś znanym jako substytucja Liskowa i chociaż koncepcja jest dość prosta do opisania i zrozumienia, podstawowa implementacja jest na tyle złożona, że ​​w zasadzie nie da się zrobić dobrze bez wsparcia na poziomie językowym.

Idea podstawienia Liskowa polega na tym, że wszędzie tam, gdzie kod oczekuje zmiennej określonego typu, powinien być w stanie zaakceptować dowolny typ pochodzący z tego typu i nadal działać poprawnie bez konieczności znajomości szczegółów typu pochodnego.

Na przykład frameworki GUI używają wszędzie podstawienia Liskov. Zwykle mają Controlklasę podstawową, która może reprezentować „dowolną kontrolę”, która definiuje interfejs, który wie o podstawowych działaniach, takich jak rysowanie, zmiana rozmiaru i reagowanie na dane wejściowe użytkownika. Jeśli klikniesz formant, środowisko interfejsu użytkownika wywoła Clickmetodę na formancie bez konieczności dbania o to, jaki to jest typ kontroli, a następnie pozwól formancie obsługiwać kliknięcie w odpowiedni sposób dla własnej klasy. ButtonKontrola powinna zrobić coś zupełnie innego po kliknięciu niż TextBoxkontrolą, aby podać tylko jeden przykład.

Tak więc, możesz stworzyć coś podobnego do obiektów za pomocą sztuczki z zagnieżdżonymi funkcjami opisanej powyżej, ale ponieważ nie możesz w ten sposób uzyskać dziedziczenia i podstawienia Liskova, jest to bardzo ograniczony zamiennik prawdziwego OOP.

Mason Wheeler
źródło
Czy w języku C nie mogę powiedzieć „struct parent {}”, a następnie „struct child {struct parent * ptr;}”? Czy to nie jest dziedziczenie w składni języka non-oop?
nadmierna wymiana
@overexchange: To próba sfałszowania bez OO, ale kompilator nie pozwoli ci zastąpić jednego innym. (Nie można przekazać funkcji child*do funkcji, która przyjmuje parent*argument jako argument, przynajmniej nie bez rzutowania typu). Co gorsza, struktury C nie mogą mieć powiązanych z nimi metod i nie ma obsługi metod wirtualnych , które są co sprawia, że ​​magia substytucji Liskowa działa, więc trzeba ręcznie konstruować VMT, co jest skomplikowanym procesem, który łatwo spieprzyć.
Mason Wheeler
1
Jądro Linux wykorzystuje emulację kilku technik OO, które wszystkie muszą być kodowane ręcznie bez obsługi języka. Prowadzi to do wielu okazji do błędów, które, będąc Linuksem, są równoważone przez liberalne stosowanie prawa Linusa. Tak, można to zrobić - dowodzi to równoważność Turinga - ale moja uwaga, że ​​bardzo trudno jest uzyskać poprawkę bez wsparcia językowego, jest nadal aktualna. Ponadto, dlaczego wszystkie te pytania dotyczące C, gdy pytanie dotyczyło Pythona? W C nie można w ogóle wykonać sztuczki z zagnieżdżonymi funkcjami.
Mason Wheeler,
1
@overexchange Od kiedy Java jest „rajem dla programistów”?
Brandin
1
Przekazywanie wiadomości nie było niepowodzeniem w systemach OOP typu Smalltalk, Erlang, ani nawet w Javie, w których „komunikat” oznacza coś innego niż „wywołanie funkcji” (sygnały i sloty Qt z bezpieczną dla wątków kolejką VS stary marketing Java, używając terminu „komunikat” gdy oznacza to „wywołanie metody”). Wiadomości! = Wywołania funkcji. Prawdziwe przesyłanie wiadomości jest nie tylko udane, ale wydaje się, że jest to jedyny znany nam sposób pisania masowo współbieżnych i solidnych systemów. Jest to ortogonalne do implementacji OOP w stylu Java bez słowa kluczowego „class”. To może być zrobione. Nie zawsze jest to przydatne. Wiadomości są poza celem.
zxq9
-1

Szybka krótka odpowiedź

Tak, programiści mogą stosować programowanie obiektowe bez „klas”.

Długa nudna wyczerpująca odpowiedź opisowa

Istnieje kilka odmian „orientacji obiektowej”, jak sądzono, pierwszą koncepcją, która przychodzi na myśl wielu programistów, są „klasy”.

Tak, programiści mogą stosować programowanie obiektowe bez „klas”, ale są ograniczone do funkcji i ograniczeń każdego języka programowania.

Twój post jest oznaczony jako Python , dlatego tytuł pytania może przypominać „Jak zaimplementować programowanie obiektowe bez klas w Pythonie”.

Obecnie używam wyrażenia „Programowanie zorientowane obiektowo i klasowo”, aby zidentyfikować na podstawie innych odmian, takich jak „Prototypowanie” Javascript lub „Podstawowy” Visual Basic lub emulacja w „Pure C” za pomocą „funktorów”.

umlcat
źródło