【问题标题】:Ruby Interface, Collection of ObjectsRuby 接口,对象集合
【发布时间】:2016-08-25 08:50:00
【问题描述】:

我创建了一个名为Collection 的接口来保存我项目模型中的任何对象的集合。我想要这个集合而不是一个数组,因为我想要 Collection 中的其他字段。

    module Collection
      def self.included(klass)
        klass.attr_writer :list, type: Array
        klass.attr_writer :class_type,   type: Class
        # Other fields go in here, along with their validations
        klass.validate :validate_list

        def validate_list
          self.list.each { |o|
            if(!o.instance_of? self.class_type)
              klass.errors.add :list, 'Objects in list must be of the same type'
              return
            end
          }
        end
      end
    end

我想用这个Collection 来保存 Models::Company 的对象列表,除了我将来会添加到 Portfolio Model 的其他列表。我希望这份公司名单只是投资组合模型的一部分。

class Portfolio
  include Model::Collection

  @schema = {
      'type' => 'object',
      'properties' => {
          'id'                       => { 'type' => 'string' },
          'title'                    => { 'type' => 'string'  },
          'description'              => { 'type' => 'string'  },
          'companies_list'          =>  {'type' => '?'}, # 1. Should this be array or Collections?
      }
  }
  @modelName      = 'portfolios'
  @collectionName = 'portfolios'


  store_in collection: 'portfolios'

  field :title,                     type: String
  field :description,               type: String
  field :companies_list,            type: Array # 2. Should this be array or array of Collections?

  embeds_many :companies

end

感谢任何帮助。

【问题讨论】:

  • 在模块中使用“抽象”一词确实会让人感到困惑。这有一个特定的含义,它与类有关,尽管 Ruby 根本没有真正使用这个术语,因为没有表达抽象基类的标准方法。也不清楚这里的问题是什么。
  • @tadman:对不起,我应该把它改成class
  • 不,我的意思是它是一个 mixin 模块很好,但称它为“抽象”是一种误导。为什么不叫它Model::CollectionMethodsModel::CollectionValidations 或类似的名称?
  • 啊,明白了。完毕。我称它为collections,因为它既有方法又有字段。可以吗?

标签: ruby activerecord interface sinatra


【解决方案1】:

我看到您来自 Java 世界,我猜您想将 Java 的泛型引入 Ruby。但是,首先,为什么 Java 有泛型?让我们上历史课吧。

在早期的 Java 中(1.5 之前),没有泛型类型,所以程序员不得不写这样的代码:

List list = new ArrayList();
// add some strings to the list
list.add("foo");
list.add("bar");

// we have to iterate over each element as an Object
for (Object obj : list) {
  // and then cast it to String
  String str = (String) obj;
  // in order to call String methods on it.
  String uppercased = str.toUpperCase();

  // ...
}

这当然不是 DRY。为了减轻强制转换的痛苦,Java 1.5 引入了泛型。

List<String> list = new ArrayList<String>();
// add some strings to the list
list.add("foo");
list.add("bar");

// now we can iterate over the elements as strings
for (String str : list) {
  // no more casting, yay!
  String uppercased = str.toUpperCase();

  // ...
}

但是等等,非通用版本首先哪里出错了?

在 Java 中,变量类型决定了可以在对象上调用哪些方法,而不是对象本身。如果以更通用的类型(即超类)声明变量,则不能调用属于更特殊类型(即子类)的方法。如果你想调用这些方法,你必须强制转换。

但是如果对象本身可以决定可以调用哪些方法呢?突然泛型变得无用。 Ruby 和许多其他动态语言都遵循这种方式。 Rubyists 称其为 duck typing - 如果某物像鸭子一样走路并像鸭子一样嘎嘎叫,那么它就是鸭子。

list = ['foo', 'bar']
list.each do |str|
  # we don't care what type str is,
  # as long as it has the method upcase.
  str.upcase if str.respond_to?(:upcase)
end

所以 ruby​​ 专家通常不定义容器类,他们只使用数组。如果应该应用类型限制,它们只是在将对象添加到数组时应用它。

list = []
list << something if something.is_a? Portfolio

坚持使用数组的另一个原因是数组具有很棒的文字,例如 ['foo', 'bar']%w(foo bar)%i(foo bar),而自定义容器类型没有这些文字。

【讨论】:

  • 谢谢,这确实有助于理清很多概念!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-26
  • 1970-01-01
  • 2012-09-02
  • 1970-01-01
  • 2013-04-25
相关资源
最近更新 更多