Programmation orientée objet
Un article de Haypo.
Retour aux articles de programmation
Cet article est en cours de rédaction. Sa qualité est pauvre et son intérêt limité. Revenez un peu plus tard (ça peut être long), et lisez un autre article en attendant ;-) Si vous êtes impatient de lire la suite, secouez un peu son auteur :o)
Sommaire |
[modifier] Exemple : transformer un ensemble de fonctions en une classe
[modifier] Présentation du code à améliorer
Prenons cette grosse fonction :
def doImport(dir, base_url, only_last, namespaces=range(0,16)):
if base_url[-1] != "/": base_url = base_url + "/"
if not os.access(dir, s.F_OK): os.mkdir(dir)
for namespace in namespaces:
print "Namespace %s" % namespace
pages = getAllpages(namespace)
args = {"namespace": namespace, "pages": "\n".join(pages)}
if only_last: args["curonly"] = "on"
page = urllib.urlopen(base_url+"Special:Export", args)
f = open(os.path.join(dir, "%s.xml" % namespace))
f.write(page.read())
f.close()
page.close()
Le problème est que cette fonction est très compact, et qu'il est difficile de la faire évoluer.
[modifier] Découpage en sous-fonctions
Commençons par découper le code en sous-fonctions pour aérer le code :
def savePage(dir, namespace, page):
f = open(os.path.join(dir, "%s.xml" % namespace))
f.write(page.read())
f.close()
page.close()
def importNamespace(dir, base_url, only_last, namespace):
print "Namespace %s" % namespace
pages = getAllpages(namespace)
args = {"namespace": namespace, "pages": "\n".join(pages)}
if only_last:
args["curonly"] = "on"
page = urllib.urlopen(base_url+"Special:Export", args)
savePage(dir, namespace, page)
def doImport(dir, base_url, only_last, namespaces=range(0,16)):
if base_url[-1] != "/":
base_url = base_url + "/"
if not os.access(dir, s.F_OK):
os.mkdir(dir)
for namespace in namespaces:
importNamespace(dir, base_url, only_last, namespace)
Hum, c'est déjà un poil plus clair nan ? Vous remarquerez que j'ai ajouté un retour à la ligne pour les if. Cela rend la code plus lisible.
[modifier] Création d'une classe
Les défauts sont que nous passons chaque fois les même arguments pour les fonctions (à quelques détails près), et cela risque d'empirer si on ajoute de nouvelles fonctionnalités. Voici donc une traduction en une classe :
class Importer:
def __init__(self, base_url):
self.dir = "pages"
if base_url[-1] != "/":
base_url = base_url + "/"
self.base_url = base_url
self.only_last = True
self.namespaces = range(0,16)
def savePage(self, namespace, page):
filename = os.path.join(self.dir, "%s.xml" % namespace)
f = open(filename)
f.write(page.read())
f.close()
page.close()
def importNamespace(self, namespace):
print "Namespace %s" % namespace
pages = getAllpages(namespace)
args = {"namespace": namespace, "pages": "\n".join(pages)}
if self.only_last:
args["curonly"] = "on"
page = urllib.urlopen(self.base_url+"Special:Export", args)
savePage(namespace, page)
def doImport(self):
if not os.access(self.dir, s.F_OK):
os.mkdir(self.dir)
for namespace in self.namespaces:
importNamespace(namespace)
Bon, forcément, le code est plus gros (en hauteur), mais je trouve surtout qu'il est plus lisible. On voit des "self." apparaitre partout, mais ceci est une spécificité du Python qui rend sa présence obligatoire.
L'avantage est qu'on peut maintenant très facilement faire évoluer le code sans avoir à changer le prototype de chaque fonction. Remarque, on aurait pu également créer une structure/classe "contexte" qui contient les variables, et la passe en première argument de chaque fonction ... c'est exactement le cas en fait, ce qui se traduit par l'argument "self" en Python (encore une fois, en C++ par exemple il est implicite). Mais personnellement, je préfère grouper données et fonctions.

