Programmation orientée objet

Un article de Haypo.

Retour à la page précédente Retour aux articles de programmation

Avertissement

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.