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

发布于 2024-09-29 06:57:42 字数 845 浏览 1 评论 0原文

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

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 <一些通用的东西等等。然后当我这样做时:

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

我会得到 FooSomething 实例。

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

obj = a_class.new(arg)

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

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

I would like to create an abstract class which will create concrete instances depending on initialization parameter. Example:

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

Then I would like to have FooSomething < SomethingGeneric, BarSomething < SomethingGeneric and more. Then when I do:

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

I would get FooSomething instance.

My problem here is the "new" method. I have defined SomethingGeneric.new, however FooSomething and BarSomething are subclasses of SomethingGeneric therefore they inherit the "new" method which is called with wrong arguments here:

obj = a_class.new(arg)

One of the solution would be to use another name for the factory method 'new'. However I would like to stick with convenience and keep the abstract superclass factory method named 'new'.

What is the cleanest correct way to solve this problem?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

薄暮涼年 2024-10-06 06:57:42

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

你需要从数组中获取第一个项目作为你的类型var,然后从数组中删除该项目,这样你就可以将其余的参数传递给下一个新的参数称呼。

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

your new method should take one param: *args

you'll want to grab the first item out of the array as your type var, and then remove that item from the array so you can pass the rest of the args down to the next new call.

Array#shift will give you the first item and then remove it.

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
墨落成白 2024-10-06 06:57:42

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

The real question is what do you need this behavior for? It appears you're coming from a language like Java where Factories and the like are the norm. Do you need this behavior so that you know that the object will respond to specific methods you are going to use? How about using an interface?

Something like:

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

If you really want to use an Abstract class you could do something like:

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

In this case the behavior would be something like this:

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!"
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文