Reputation: 12923
I have been for the last couple of days working on this Factory pattern based class where you can register classes, give them unique names, then use them to create objects on the fly. My class looks as such:
module AisisWriter
class ClassFactory
class << self
undef_method :new
attr_accessor :registered_objects
def register(class_name, klass, params = nil)
if !params.nil? && !params.is_a?(Array)
raise ArgumentError, "Params must be an array."
end
if registered?(class_name)
raise ArgumentError, "Class name already registered."
end
@registered_object[class_name] = {:class_name => klass, :params => !params.nil? ? params.flatten : nil}
end
def registered?(class_name)
if @registered_object.nil?
return false
end
@registered_object.include? class_name
end
def create(class_name, params = nil)
if !params.nil? && !params.is_a?(Array)
raise ArgumentError, "Params must be an array."
end
if !registered?(class_name)
raise ArgumentError, "Class does not exist in the registered classes."
end
klass = @registered_object[class_name]
if !params.nil
klass[:class_name].new(params.flatten)
else
flass[:class_name].new(*klass[:params])
end
end
end
end
end
But I have some questions, which arose as I was writing tests.
register(...)
function I do:@registered_object[class_name] = {:class_name => klass, :params => !params.nil? ? params.flatten : nil}
This fails because @registered_object
is nil. How do I initialize it only once? In php I would write a getInstance()
method and say if the class instance is set, don't set it again, while we set the class instance also set other variables that only need to be set once. Would I have a get_instance
method here?
@registered_object
to {}
once, could I then do: AisisWriter::ClassFactory.get_instance.register(...)
Does ruby support that?The basis here is to make sure that when this class is called upon, we check if @register_object
is nil, and if so set it to a new instance of {}
If there is a easier solution I am all ears.
Upvotes: 1
Views: 864
Reputation: 62648
If you want an actual singleton factory, then Ruby has the Singleton module which helps with that!
You can define your class like this:
module AisisWriter
class ClassFactory
include Singleton
attr_accessor :registered_objects
def initialize
@registered_object = {}
end
def register(class_name, klass, params = nil)
raise ArgumentError, "Class name already registered." if registered? class_name
@registered_object[class_name] = {:class_name => klass, :params => params}
end
def registered?(class_name)
@registered_object.key? class_name
end
def create(class_name, params = nil)
raise ArgumentError, "Class does not exist in the registered classes." unless registered? class_name
klass = @registered_object[class_name]
klass[:class_name].new *Array(params || klass[:params])
end
end
end
This provides an #initialize
method where you can set up all your singleton state, but ::new
is uncallable as you'd expect. Instead, you use ::instance
.
And then you'd use it like:
AisisWriter::ClassFactory.instance.register(...)
AisisWriter::ClassFactory.instance.create(...)
The factory will be instantiated only once when #instance
is called for the first time.
Upvotes: 0
Reputation: 45943
You can do
def registered_object
@registered_object ||= {}
end
Then call registered_object
instead of @registered_object
.
This is a common Ruby idiom. It means:
If @registered_object is falsy, then set it to {}.
Upvotes: 5