开发者

Ruby design question

Overview

I am writing a Ruby program that uses data from mysql queries to create chart URLs. A new requirement has recently surfaced where we could need to created graphs with grouped bars in the future. So instead of having one set of data, I could have any number of sets. Right now the constructor for my BarChart object only takes a single array of data, and I am looking Ruby-like ways of allowing more than one array of data.

Current Constructor

    #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

My attempt

My initial thought was to use var args.

    #co开发者_如何学编程nstructor
    #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

Is this a decent idea? or is there a better way to go about it?

Thanks


Personally, when you have this many arguments, I would use an options hash.

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

So that you can construct your class like this:

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

I find this approach makes your method calls much more readable, so you don't have a constructor call which has a long sequence of number and string arguments for which the meaning may be difficult to determine. This also gives you a natural way to add an arbitrary amount of optional parameters with default values.


EDIT: Added ruby version dependencies

This is a decent idea but sadly it won't work in 1.8.7.

In 1.8.7. they arbitrary argument exploder (*data) only works at the end of the argument list. And in 1.9.2 only works if there are no optional parameters.

So something more ruby compliant like this might work,

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

However, why not simply keep data where it is and do some duck typing to see how much data is actually stored in the array. The method signature wouldn't change, but the call would slightly to,

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


Like Jeremy I use an option hash.

In Addition I define defaults and required keys. Depending on my requirements I log missing/additional keys or I raise an exception. My Testexample code below just writes a message on 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}>
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜