Оптимальное проектирование библиотеки структур данных Ruby - PullRequest
2 голосов
/ 06 июня 2011

Я работаю с основами структур данных в Ruby для обучения на втором курсе CS.

Мой вопрос: Учитывая следующий код, кто-нибудь видит какие-либо проблемы с дизайном при таком подходе к библиотеке структур данных в Ruby? Особенно Модуль # abstract_method. Можно ли делать это с точки зрения философии утиной печати? Делает ли это код понятнее для людей со статическими языками и дает некоторое подобие интерфейса?

class Module

  def abstract_method(symbol)
    module_eval <<-"end_eval"
      def #{symbol.id2name}(*args)
        raise MethodNotImplementedError
      end
    end_eval
  end

end

class AbstractObject < Object

  abstract_method :compare_to
  protected :compare_to

  class MethodNotImplementedError < StandardError; end

  def initialize
    super
  end

  include Comparable

  def <=>(other)
    if is_a?(other.class)
      return compare_to(other)
    elsif other.is_a?(self.class)
      return -other.compare_to(self)
    else
      return self.class <=> other.class
    end
  end

end

# methods for insertion/deletion should be provided by concrete implementations as this behavior
# is unique to the type of data structure. Also, concrete classes should override purge to discard
# all the contents of the container

class Container < AbstractObject

  include Enumerable

  def initialize
    super
    @count = 0
  end

  attr_reader :count
  alias :size :count

  # should return an iterator
  abstract_method :iter

  # depends on iterator object returned from iter method
  # layer of abstraction for how to iterate a structure
  def each
    i = iter
    while i.more?
      yield i.succ
    end
  end

  # a visitor provides another layer of abstraction for additional
  # extensible and re-usable traversal operations
  def accept(visitor)
    raise ArgumentError, "Argument must be a visitor" unless visitor.is_a?(Visitor)
    each do |obj|
      break if visitor.done?
      visitor.visit(obj)
    end
  end

  # expected to over-ride this in derived classes to clear container
  def purge
    @count = 0
  end

  def empty?
    count == 0
  end

  def full?
    false
  end

  def to_s
    s = ""
    each do |obj|
      s << ", " if not s.empty?
      s << obj.to_s
    end
    self.class + "{" + s + "}"
  end


end

class List < Container

  def initialize
    super
  end

  def compare_to(obj)
   "fix me"
  end

end

1 Ответ

3 голосов
/ 06 июня 2011

Несколько замечаний:

  • Определение метода, который вызывает только ошибку NotImplemented, несколько избыточно, так как Ruby сделает это в любом случае, если метод не существует. Код, который вы там написали, так же полезен, как и просто добавление комментария, говорящего: «Вы должны реализовать метод с именем compare_to». Фактически это то, что делает модуль Enumerable в стандартной библиотеке Ruby - в документации конкретно говорится, что для использования функциональности в Enumerable необходимо определить метод each ().

  • метод compare_to также является избыточным, поскольку именно для этого предназначен оператор <=>.

  • Использование реального объекта итератора в Ruby немного излишне, поскольку блоки имеют более элегантный и простой подход. То же самое относится и к шаблону посетителя - вам не нужно использовать посетителя для «расширяемых и повторно используемых операций обхода», когда вы можете просто передать блок методу обхода. Например, у вас есть много из них в Enumerable: each, each_with_index, map, inject, select, delete_if, partition и т. Д. Все они по-разному используют блок для обеспечения другого типа функциональности, и могут быть добавлены другие функциональные возможности. довольно простым и последовательным способом (особенно если у вас есть открытые классы).

  • Что касается интерфейсов, в Ruby (и почти любом другом динамическом языке, например, Python) люди обычно используют интерфейсы, неявные , что означает, что вы фактически не определяете интерфейс в коде , Вместо этого вы обычно полагаетесь на документацию и надлежащие тестовые наборы, чтобы гарантировать, что код хорошо работает вместе.

Я думаю, что ваш код может быть более согласованным с кем-то из мира Java, потому что он придерживается "Java-способа" ведения дел. Однако другим программистам на Ruby ваш код может показаться запутанным и трудным для работы, так как он на самом деле не придерживается "Ruby-способа" ведения дел. Например, реализация функции выбора с использованием объекта итератора:

it = my_list.iter
results = []

while it.has_next?
  obj = it.next

  results << obj if some_condition?
end

гораздо менее понятен программисту на Ruby, чем:

results = my_list.select do |obj|
  some_condition?
end

Если вы хотите увидеть пример библиотеки структур данных в Ruby, вы можете увидеть гем алгоритмов здесь: http://rubydoc.info/gems/algorithms/0.3.0/frames

Также взгляните на то, что предусмотрено по умолчанию в модуле Enumerable: http://www.ruby -doc.org / core / classes / Enumerable.html . Когда вы включаете Enumerable, вы получаете все эти функции бесплатно.

Надеюсь, это поможет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...