如何在Ruby中创建“类集群”(带有具体子类实例的工厂类)?

时间:2010-10-27 12:58:21

标签: ruby abstract-class factory-method class-cluster

我想创建一个抽象类,它将根据初始化参数创建具体实例。例如:

class SomethingGeneric
def self.new(type, arg)

    class_name = "#{type.capitalize}Something"

    if obj.const_defined?(class_name)
        a_class = obj.const_get(class_name)
    else
        raise ArgumentError, "Concrete something '#{type}' was not found"
    end

    obj = a_class.new(arg)

    return obj
end
end # class

然后我想拥有FooSomething< SomethingGeneric,BarSomething< SomethingGeneric等等。然后当我这样做时:

obj = SomethingGeneric.new("foo", arg)

我会得到FooSomething实例。

我的问题是“新”方法。我已经定义了SomethingGeneric.new,但是FooSomething和BarSomething是SomethingGeneric的子类,因此它们继承了在这里使用错误参数调用的“new”方法:

obj = a_class.new(arg)

其中一个解决方案是为工厂方法'new'使用另一个名称。但是我想坚持方便并保留名为'new'的抽象超类工厂方法。

解决此问题最简洁的正确方法是什么?

2 个答案:

答案 0 :(得分:3)

你的新方法应该采用一个参数:* args

你想要从数组中取出第一个项目作为你的类型var,然后从数组中删除该项目,这样你就可以将其余的args传递给下一个新的调用。

数组#shift将为您提供第一项,然后将其删除。

class SomethingGeneric
  def self.new(*args)

    type = args.shift
    class_name = "#{type.capitalize}Something"

    if obj.const_defined?(class_name)
        a_class = obj.const_get(class_name)
    else
        raise ArgumentError, "Concrete something '#{type}' was not found"
    end

    obj = a_class.new(*args)

    return obj
  end
end # class

答案 1 :(得分:0)

真正的问题是你需要这种行为?看起来你来自像Java这样的语言,其中工厂等是常态。您是否需要此行为以便您知道该对象将响应您将要使用的特定方法?使用界面怎么样?

类似的东西:

class GenericThing
  def name # Interface method
     # Return a String of the name of the GenericThing.
  end
end

class GenericSomething
  def name
    # ...
   end
 end

 class Whatever
   def self.method_that_uses_the_generic_interface(generic)
     if generic.respond_to?(:name) # Check for interface compliance
       generic.name
     else
        raise "Generic must implement the name method."
     end
   end
 end

如果您真的想使用Abstract类,可以执行以下操作:

class AbstractGeneric
  def name
    raise "You must override name in subclasses!"
  end

 class GenericThing < AbstractGeneric
   def name
     "GenericThing"
    end
 end

 class GenericSomething < AbstractGeneric
   # ...
 end

 class Whatever
   def self.method_that_uses_the_generic_interface(generic)
     if generic.kind_of? AbstractGeneric
       generic.name
       # Will raise if the interface method hasn't been overridden in subclass.
     else
       raise "Must be a instance of a subclass of AbstractGeneric!"
     end
   end
 end

在这种情况下,行为将是这样的:

generic_thing = GenericThing.new
Whatever.method_that_uses_the_generic_interface(generic_thing)
=> "GenericThing"

generic_something = GenericSomething.new
Whatever.method_that_uses_the_generic_interface(generic_something)
# Will raise "You must override name in subclass!"

object = Object.new
Whatever.method_that_uses_the_generic_interface(object)
# Will raise "Must be an instance of a subclass of AbstractGeneric!"