0

我正在创建一个支持 API 网络的库。该库的中心当前是每个 API 客户端子类的客户端类。由于我是编写所有 API 的人,因此它们的功能都类似(restful、通过 access_token 进行授权等)。

然而,与其他 ruby​​ API 客户端库(Twitter等)不同,客户端类不应直接实例化。这是因为该库不限于单个 API。相反,每个 API 客户端都将继承 Client 类。我的问题如下:

有没有办法要求 Ruby 类只通过子类初始化?

此外,在阅读这个问题时,我认为一个类比一个 mixin 更好。

对于那些想要代码的人,这里有一个例子:

class A
    def initialize(options = {})
        #what goes on in here doesn't really matter for the purpose of this question
        #I just don't want it to be initialized directly
        options.each do |k,v|
            instance_variable_set("@#{k}",v) unless v.nil?
        end
    end
end

class B < A
    attr_accessor :class_specific_opt
    def initialize( options = {} )
        @class_specific_opt = "val" unless options[:class_specific_opt].nil?
        super(options)
    end
end

有什么想法吗?

4

5 回答 5

2

你可以这样做:

class A
  def self.initialize_allowed?
    raise "You cannot initialize instances of this class."
  end

  def self.allow_initialize
    class << self
      def initialize_allowed?
        true
      end
    end
  end

  def initialize(options = {})
    self.class.initialize_allowed?
    options.each do |k,v|
      instance_variable_set("@#{k}", v)
    end
  end
end

调用A.new会引发 RuntimeError 并停止该initialize方法。然后,您可以initialized_allowed?通过调用子类来覆盖子类中的方法allow initialize。(也许这是矫枉过正,但我​​认为allow_initialize比阅读更容易def self.initialize_allowed?;end):

class B < A
  allow_initialize
end

B.new #=> #<B:0x00000102837d10>
于 2012-11-26T18:49:24.763 回答
2

这里有一个关于抽象类的答案:How to implement an abstract class in ruby​​? , 尽管您最好将 A 作为模块提供并将其包含在其实现者中。

于 2012-11-26T17:56:37.953 回答
1

如果您不想A.new被调用,只需执行以下操作:

class <<A
  undef :new
end

然后,您将无法调用它:

A.new #=> NoMethodError: undefined method `new' for A:Class
于 2012-11-26T19:07:10.287 回答
0

添加

private_class_method :new

A类,和

public_class_method :new

到 B 级。

于 2012-11-26T19:15:15.253 回答
0

我认为最好的方法是制作新方法private。这正是 rubySingleton​​ 模块所做的。单例模块文档

class A
  private_class_method :new
end

class B < A
  public_class_method :new
end
于 2012-11-26T19:19:40.600 回答