Résumé des patrons de conception expliqués dans le livre Design Patterns in Ruby
View the Project on GitHub armandfardeau/design-patterns-in-ruby
Nous avons un peu de code complexe, mais quelque part au milieu, il y a un peu de code qui doit varier.
L’idée générale de la méthode des modèles est de construire une classe de base abstraite avec une méthode squelettique, qui conduit le bit du traitement qui doit varier en faisant des appels à des méthodes abstraites, qui sont ensuite fournies par les sous-classes concrètes. Ainsi, la classe de base abstraite contrôle le traitement de niveau supérieur et les sous-classes remplissent simplement les détails.
Nous devons générer un rapport HTML, donc nous arrivons à quelque chose comme ceci :
class Report
def initialize
@title = 'Monthly Report'
@text = ['Things are going', 'really, really well.']
end
def output_report
puts('<html>')
puts(' <head>')
puts("<title>#{@title}</title>")
puts(' </head>')
puts(' <body>')
@text.each do |line|
puts("<p>#{line}</p>")
end
puts(' </body>')
puts('</html>')
end
end
Plus tard, on se rend compte qu’il faut ajouter un nouveau format : le texte brut. Facile, nous pouvons passer le format comme paramètre et décider ce qu’il faut afficher en fonction de celui-ci :
class Report
def initialize
@title = 'Monthly Report'
@text = ['Things are going', 'really, really well.']
end
def output_report(format)
if format == :plain
puts("*** #{@title} ***")
elsif format == :html
puts('<html>')
puts(' <head>')
puts("<title>#{@title}</title>")
puts(' </head>')
puts(' <body>')
else
raise "Unknown format: #{format}"
end
@text.each do |line|
if format == :plain
puts(line)
else
puts("<p>#{line}</p>")
end
end
if format == :html
puts(' </body>')
puts('</html>')
end
end
end
C’est un peu désordonné, le code qui gère les deux formats est emmêlé et, pire encore, il n’est pas extensible du tout (et si on veut ajouter un nouveau format ?). Refacturons le code en cherchant ce qui reste le même. Dans la plupart des rapports, le flux de base est le même, quel que soit le format : en-tête de sortie, titre de sortie, sortie de chaque ligne du rapport et sortie de tous les éléments de suivi requis par le format. Nous pourrions créer une classe de base abstraite qui exécute toutes ces étapes mais laisse les détails à une sous-classe :
class Report
def initialize
@title = 'Monthly Report'
@text = ['Things are going', 'really, really well.']
end
def output_report
output_start
output_head
output_body_start
output_body
output_body_end
output_end
end
def output_body
@text.each do |line|
output_line(line)
end
end
def output_start
raise 'Called abstract method: output_start'
end
def output_head
raise 'Called abstract method: output_head'
end
def output_body_start
raise 'Called abstract method: output_body_start'
end
def output_line(line)
raise 'Called abstract method: output_line'
end
def output_body_end
raise 'Called abstract method: output_body_end'
end
def output_end
raise 'Called abstract method: output_end'
end
end
Nous pouvons maintenant définir une sous-classe qui implémente les détails :
class PlainTextReport < Report
def output_start
end
def output_head
puts("**** #{@title} ****")
end
def output_body_start
end
def output_line(line)
puts(line)
end
def output_body_end
end
def output_end
end
end
report = PlainTextReport.new
report.output_report