【问题标题】:How to create a "class cluster" (factory class with instances of concrete subclasses) in Ruby?如何在 Ruby 中创建“类集群”(具有具体子类实例的工厂类)?
【发布时间】:2010-10-27 12:58:21
【问题描述】:

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

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

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

我会得到 FooSomething 实例。

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

obj = a_class.new(arg)

其中一个解决方案是为工厂方法“new”使用另一个名称。不过,我想坚持方便,并保持抽象超类工厂方法名为“新”。

解决这个问题最干净的正确方法是什么?

【问题讨论】:

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


    【解决方案1】:

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

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

    Array#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
    

    【讨论】:

      【解决方案2】:

      真正的问题是你需要这种行为是为了什么?看来您来自像 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
      

      如果你真的想使用抽象类,你可以这样做:

      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!"
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多