【问题标题】:Is there a way to restrict Ruby Class initialization only to a subclass?有没有办法将 Ruby 类初始化仅限于子类?
【发布时间】:2012-11-14 05:56:31
【问题描述】:

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

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

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

此外,在阅读this question 时,我认为类比混合更好。

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

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

有什么想法吗?

【问题讨论】:

  • 如果你做B.new,那么B#initialize会被调用,它会调用A#initializeA#initialize 不会被直接调用,除非你调用A.new。它有什么问题?
  • 问题是理论上有人仍然可以调用 A#initialize
  • initialize 是一个私有方法。你不能用接收器来调用它。

标签: ruby


【解决方案1】:

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

【讨论】:

    【解决方案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 方法。然后,您可以通过在子类上调用 allow initialize 来覆盖子类中的 initialized_allowed? 方法。 (也许这有点矫枉过正,但我​​认为allow_initializedef self.initialize_allowed?;end 更容易阅读):

    class B < A
      allow_initialize
    end
    
    B.new #=> #<B:0x00000102837d10>
    

    【讨论】:

      【解决方案3】:

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

      class <<A
        undef :new
      end
      

      那么,你将无法调用它:

      A.new #=> NoMethodError: undefined method `new' for A:Class
      

      【讨论】:

        【解决方案4】:

        添加

        private_class_method :new
        

        到 A 级,并且

        public_class_method :new
        

        到 B 级。

        【讨论】:

          【解决方案5】:

          我认为最好的方法是制作新方法private。这正是 ruby​​ Singleton 模块所做的。 Singleton Module Documentation

          class A
            private_class_method :new
          end
          
          class B < A
            public_class_method :new
          end
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-01-01
            • 2015-12-07
            • 2016-07-10
            相关资源
            最近更新 更多