From: Mike Williams Date: 2004-01-14T13:04:24+09:00 Subject: What object-configuration approach to use? Consider a class that supports a number of configurable properties. As a concrete example, take a class that represents an HTML link. It requires that a "url" and link "content" be provided. It also supports two optional attributes: "title" and "target". Link url content ?title ?target I've seen a bunch of different ways of configuring such objects ... ---- 1. SettingAttributes The obvious way is just to create the instance, and then call writer-methods to set attributes, e.g. class Link def initialize(url, content) @url = url @content = content end attr_accessor :url, :content, :link, :target end link = Link.new(url, "here") link.title = "the whole story" ---- 2. OptionalArguments You can have initialize() support optional (positional) arguments, e.g. class Link def initialize(url, content, title = nil, target = nil) @url = url @content = content @title = title @target = target end attr_accessor :url, :content, :link, :target end link = Link.new(url, "here", "the whole story") but, this doesn't scale well: if you have a bunch of optional attributes, you have to start inserting "nil": link2 = Link.new(url, "here", nil, "helpFrame") ---- 3. OptionalArgumentMap Another alternative is pass in a map of named optional args, e.g. class Link def initialize(url, content, args = {}) @url = url @content = content @title = args[:title] @target = args[:target] end end link = Link.new("http://", "here", {:title => "the whole story"}) helplink = Link.new("/help", "help" {:target => "helpFrame"}) As I understand it, there's some syntax-sugar planned for Ruby-2.0, that would make this a bit cleaner: link = Link.new("http://", "here", title: "the whole story") Right? ---- 4. YieldSelf I've seen some people include a "yield self" in the initialize() method, which allows you to do something like this: class Link def initialize(url, content) @url = url @content = content yield self if block_given? end end link = Link.new(url, "here") { |l| l.title = "the whole story" } Hmmm. How does this help, exactly? I guess it could save you from having to use a temp variable, in some cases. ---- 5. InstanceEval Or, you can use instance_eval(): class Link def initialize(url, content, &config) @url = url @content = content instance_eval(&config) if (config) end end link = Link.new("http://", "here") { @title = "the whole story" } This is a nifty trick, though it does have the downside that you can set arbitrary instance-variables in the block, breaking class encapsulation. ---- In Summary Where am I going to with this? No idea. I guess I'm just wondering what y'all consider to be the benefits/problems of each approach. -- cheers, MikeW "He smelled as if he had just eaten a mustard-coated camel ..." -- Martin Amis, "London Fields"