2011-08-02 5 views
1

AperçuRuby Question design

Je vous écris un programme Ruby qui utilise des données de requêtes MySQL pour créer des URL de graphique. Une nouvelle exigence a récemment fait surface où nous pourrions avoir besoin de créer des graphiques avec des barres groupées à l'avenir. Donc, au lieu d'avoir un ensemble de données, je pourrais avoir n'importe quel nombre d'ensembles. À l'heure actuelle, le constructeur de mon objet BarChart ne prend qu'un seul tableau de données, et je cherche des moyens Ruby-like pour permettre plus d'un tableau de données.

Constructor actuel

#constructor 
    #title   The title of the graph 
    #data   The data that will go in the bar chart 
    #labels   The labels that match the data 
    #x_axis_label The label for the x axis 
    #y_axis_label The label for the y axis 
    def initialize(title, data, labels, x_axis_label, y_axis_label) 
     @title, @data1, @labels, @x_axis_label, @y_axis_label = 
      title, data, labels, x_axis_label, y_axis_label 

     super(@title, @@type, @@size) 
     @url = to_url() 
    end 

Ma tentative

Ma première pensée était d'utiliser args var.

#constructor 
    #title   The title of the graph 
    #data   The data that will go in the bar chart 
    #labels   The labels that match the data 
    #x_axis_label The label for the x axis 
    #y_axis_label The label for the y axis 
    def initialize(title, *data, labels, x_axis_label, y_axis_label) 
     ..... 
    end 

Est-ce une bonne idée? ou y a-t-il une meilleure façon de s'y prendre?

Merci

Répondre

7

Personnellement, quand vous avez ce beaucoup d'arguments, j'utiliser un hachage d'options.

def initialize(options = {}) 
    options = { default options here, if applicable }.merge(options) 
    ... 
end 

Alors que vous pouvez construire votre classe comme ceci:

MyClass.new(:title => "Awesome Graph", :data => [[1,2,3], [4,5,6]], ...) 

Je trouve cette approche rend votre méthode appelle beaucoup plus lisible, de sorte que vous ne disposez pas d'un appel du constructeur qui a une longue séquence des arguments de nombre et de chaîne dont la signification peut être difficile à déterminer. Cela vous donne également un moyen naturel d'ajouter une quantité arbitraire de paramètres optionnels avec des valeurs par défaut.

+0

Avec fusion! vous remplacez le paramètre par votre valeur par défaut. Vous pouvez le tester avec: p ({: a => 1} .merge! ({: A => 2})) – knut

+0

Oups.Je voulais utiliser 'reverse_merge! 'D'ActiveSupport (il est assez facile de le faire si vous ne l'utilisez pas). Mais je vais le réparer. –

1

EDIT: Ajout de dépendances version rubis

C'est une idée décente, mais malheureusement, il ne fonctionnera pas en 1.8.7.

En 1.8.7. ils n'exploitent l'argument arbitraire (*data) ne fonctionne qu'à la fin de la liste des arguments. Et en 1.9.2 ne fonctionne que s'il n'y a pas de paramètres optionnels.

donc quelque chose de plus conforme à ruby ​​comme cela pourrait fonctionner,

def initialize(title, labels, x_axis_label, y_axis_label, *data) 
    ..... 
end 

Cependant, pourquoi garder non seulement les données où il est et faire un peu de frappe de canard pour voir la quantité de données est stocké dans le tableau. La signature de la méthode ne changerait pas, mais l'appel serait légèrement à,

Object.new(title, [data1, data2, data3], labels, ...) 
+0

Hmm Je viens de lire l'article de Wikipédia sur Duck Typing et je ne sais pas si c'est ce que je cherche. Toutes les données sont du même type, juste au lieu de n'avoir qu'un seul tableau de données j'en ai 6 par exemple. Donc j'essayais d'utiliser '* data' à la place si je devais faire' data1, data2, data3, data4, data5 ... 'Merci pour votre commentaire, je ne savais pas ce que le typage du canard était :) –

+0

Ce n'est pas vrai. Il ne peut y avoir aucun paramètre * optional * après les paramètres splat, mais les paramètres obligatoires sont parfaitement corrects. La grammaire est essentiellement: 0 ou plusieurs paramètres obligatoires suivis de 0 ou plus de paramètres optionnels suivis de 0 ou 1 paramètre splat suivi à nouveau par 0 ou plusieurs paramètres obligatoires. Les arguments sont liés d'abord à gauche à tous les paramètres obligatoires de gauche, puis de droite à tous les paramètres obligatoires de droite; les arguments restants sont liés de gauche à droite aux paramètres facultatifs et les autres sont liés au paramètre splat. ... –

+0

... S'il reste des arguments, ou s'il y a des paramètres non liés, c'est un 'ArgumentError'. –

1

Comme Jeremy j'utilise une option de hachage. En outre, je définis les valeurs par défaut et les clés requises. En fonction de mes besoins, j'enregistre les clés manquantes/supplémentaires ou je déclenche une exception. Mon code Testexample ci-dessous écrit juste un message sur stdout.

class X 
    DEFAULTS = { 
    p1: :default1, 
    p2: :default2 
    } 
    OBLIGATORY_PARAMETERS = [:p1] 
#Parameters: 
# p1 
# p2 
    def initialize(options = {}) 
    (OBLIGATORY_PARAMETERS - options.keys).each{|key| 
     puts "Missing key #{key}" 
    } 
    (options.keys - OBLIGATORY_PARAMETERS - DEFAULTS.keys).each{|key| 
     puts "Undefined key #{key}" 
    } 
    @options = DEFAULTS.merge(options) 
    end 
end 

#Ok 
X.new(p1: 1)#<X:0xc8c760 @options={:p1=>1, :p2=>:default2}> 
#Missing key warnings 
X.new() ##<X:0xc8c960 @options={:p1=>:default1, :p2=>:default2}> 
X.new(p2: 2)#<X:0xc8c5c0 @options={:p1=>:default1, :p2=>2}> 
#Undefined parameter -> Exception 
p X.new(p3: 3)#<X:0xc8c5c0 @options={:p1=>:default1, :p2=>2}>