Programować jak kaczka

Opublikowane przez Jarosław Zabiełło Fri, 12 May 2006 16:12:00 GMT

“Jeśli coś chodzi jak kaczka, wygląda jak kaczka i kwacze jak kaczka, to z pewnością jest to… kaczka”

Powyższe zdanie jest żartobliwym ujęciem istoty pewnej praktyki programistycznej (popularnej szczególnie w wysokopoziomowych językach dynamicznych takich jak Python, Ruby czy Smalltalk). Najlepiej wyjaśnić to na przykładzie (zresztą wziętym z życia).

Tak się złożyło, że ktoś napisał pewną aplikację w Delphi która wypełniała jedno z pól bazy MSSQL w formacie… RTF. I wszystko było w porządku, do czasu, aż zapadła decyzja, aby te dane wykorzystywać na stronie internetowej. Przeglądarki nie potrafią wyświetlić poprawnie formatu RTF, te dane trzeba jakoś przekonwertować do HTML.

Oczywiście, do tego zadania wybrałem Pythona. Głównie dlatego, że cały system synchronizacji serwerów już wcześniej napisałem w Pythonie. Więc naturalnym było, że dla dodania dodatkowej opcji użyję tego samego języka.

Trzeba było trochę poszukać gotowego kodu do parsowania formatu RTF, bo komu by się chciało samemu pisać parser od podstaw? :) Na szczęście biblioteka się znalazła . Jak to najczęściej bywa z drobnymi projektami open-source: dokumentacji: niet. Ale na szczęście Python jest bardzo przejrzysty i posiada cudowną cechę dla leniwych programistów: docstringi. Chwila oglądania kodu i już widzę, że w środku jest jedna z klas o obiecującej nazwie: Rtf2Html. Patrzę na wywołanie:

if __name__ == '__main__':
    if len(sys.argv) != 2:
        print 'Usage : %s fichier.rtf' % sys.argv[0]
    else:
        finput = open(sys.argv[1],'r')
        foutput = open(finput.name + '.html','w')
        foutput.write('<html><body>')
        tester = Rtf2Html(foutput)
        # reszta kodu...

Widać, że jest tu jeden problem. Ja potrzebuję funkcji operującej na łańcuchach znaków, a tu kod operuje na … obiekcie plikowym. Nie chce mi się zmodyfikować tego kodu. Z pomocą przychodzi duck typing. Zamiast obiektu plikowego podsuniemy kontruktorowi klasy coś co będzie udawać plik. Dla pliku tak naprawdę najważniejsze są dwie metody: read() i write(). Stworzymy więc klasę, która będzie miała te dwie metody i… nic więcej. Skoro będzie kwakać jak kaczka i zachowywać się jak kaczka, to dla tego kodu niczym się nie będzie różnić od pełnej, pierzastej kaczki.

from rtf.Rtf2Html import Rtf2Html

class PseudoPlik(object):
    """Kwa, kwa, kwa ;)"""
    __content = ''
    def write(self, txt):
        self.__content += txt
    def read(self):
        return self.__content

def rtf2html(rtf):
    """RTF to HTML converter"""
    fake = PseudoPlik()
    r = Rtf2Html(fake)
    r.feed(rtf)
    return r.read()

Jak widać, nie trzeba było w ogóle modyfikować niczego w kodzie biblioteki. Wystarczyło tylko podsunąć jej coś, co wygląda jak plik. Reszta tu nie miała znaczenia, bo i tak wykorzystywane były tylko te dwie metody obiektu plikowego.

Posted in  | Tagi  | 6 comments

Comments

  1. Avatar Rafał W powiedział about 8 hours later:

    Ciągle ucze się pythona, i mam przy okazji tego kawałka kodu jedno pytnie. Dlaczego klasa “PseudoPlik” dziedziczy po “object” ? Czy jest to konieczne, czy to tylko tak na wszelki wypadek?

  2. Avatar Michał Kwiatkowski powiedział about 11 hours later:

    A tak w ogóle to: http://sjp.pwn.pl/haslo.php?id=27954 i http://sjp.pwn.pl/haslo.php?id=16391 . ;)

  3. Avatar sprae powiedział about 17 hours later:

    Warto też wspomnieć, że istnieje StringIO :)

  4. Avatar Jarosław Zabiełło powiedział about 22 hours later:

    Wg nowego modelu obiektowego Pythona każda klasa powinna jawnie dziedziczyć po object. Vide: http://www.python.org/doc/newstyle/

  5. Avatar Przemysław Cieszyński powiedział about 1 year later:

    Powinno być: return fake.read() (I tutaj mamy piekna zalete jezykow z silnym typowaniem , oczywiscie z odpowiednim ide ;) )

  6. Avatar Przemysław Cieszyński powiedział about 1 year later:

    Miało być “z silnym typowaniem statycznym”

(leave url/email »)

   Pomoc języka formatowania Obejrzyj komentarz