Programować jak kaczka
Opublikowane przez Jarosław Zabiełło
“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.



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?
A tak w ogóle to: http://sjp.pwn.pl/haslo.php?id=27954 i http://sjp.pwn.pl/haslo.php?id=16391 . ;)
Warto też wspomnieć, że istnieje StringIO :)
Wg nowego modelu obiektowego Pythona każda klasa powinna jawnie dziedziczyć po object. Vide: http://www.python.org/doc/newstyle/
Powinno być: return fake.read() (I tutaj mamy piekna zalete jezykow z silnym typowaniem , oczywiscie z odpowiednim ide ;) )
MiaÅ‚o być “z silnym typowaniem statycznym”