Как провести рефакторинг одноэлементных методов в Ruby? - PullRequest
2 голосов
/ 11 января 2010

В настоящее время у меня есть код, подобный следующему (несколько упрощенный). Со временем я добавил все больше и больше новых классов, таких как D1 / D2, и я думаю, что пришло время провести некоторый рефакторинг, чтобы сделать его более элегантным. Цель курса - сделать так, чтобы добавление нового класса Dx использовало как можно меньше дублирующегося кода. По крайней мере, дублирующие части вызова FileImporter.import внутри синглтон-метода Dx.import должны быть исключены.

module FileImporter
  def self.import(main_window, viewers)
    ...
    importer = yield file  # delegate to D1/D2 for preparing the importer object
    ...
  end
end

class D1
  def self.import(main_window)
    viewers = [:V11, ]  # D1 specific viewers
    FileImporter.import(main_window, viewers) do |file|
      importer = self.new(file)
      ...  # D1 specific handling of importer
      return importer
    end
  end
end

class D2
  def self.import(main_window)
    viewers = [:V21,:v22, ]  # D2 specific viewers
    FileImporter.import(main_window, viewers) do |file|
      importer = self.new(file)
      ...  # D2 specific handling of importer
      return importer
    end
  end
end

# client code calls D1.import(...) or D2.import(...)

По существу, FileImporter.import - это общая часть, а Dx.import - это вариация. Я не уверен, как реорганизовать эти одноэлементные методы. Каков обычный Ruby способ сделать это?

Обновление : (некоторые комментарии были добавлены в вышеприведенный код, надеюсь, прояснить мою интенцию ...)

Первоначально я пропустил код, который я считал несущественным, чтобы избежать путаницы. Я должен был упомянуть, что приведенный выше код был также результатом рефакторинга классов D1 и D2 (путем перемещения общей части в модуль FileImporter). Цель D1.import и D2.import состояла главным образом в создании объектов соответствующего класса (и, возможно, после некоторой обработки, специфичной для класса, перед возвратом из блока). FileImporter.import - это, в основном, общая логика, в рамках которой в некоторый момент может быть получен определенный класс для генерации объекта импортера.

Я чувствую, что классы D1 и D2 выглядят действительно одинаково, и должна быть возможность их дальнейшего рефакторинга. Например, они оба вызывают FileImporter.import для предоставления блока, внутри которого оба создают свой объект.

Решение : Изначально я не осознавал, что можно вызывать одноэлементные методы базового класса, просто вызывая super из соответствующих одноэлементных методов производного класса. Это была действительно главная проблема, с которой я столкнулся и не смог пойти по этому пути. Поэтому я принял ответ @makevoid, поскольку он действительно облегчает создание новых производных классов.

Использование общего базового класса - элегантное решение для рефакторинга, но одной из проблем является то, что все новые производные классы уже используют одну квоту базового класса. Я пришел к этому макро-методу класса, который дает еще более краткие результаты с точки зрения производного класса.

module FileImporter
  def self.included(mod)
    mod.extend ClassMethods
  end

  module ClassMethods
    def importer_viewer(*viewers, &blk)
      @viewers = viewers
      @blk = blk

      class << self
        def import(main_window)
          if @blk.nil?
            FileImporter.import(main_window, @viewers) do |file|
              self.new(file)
            end
          else
            FileImporter.import(main_window, @viewers, &@blk)
          end      
        end
      end
    end
  end

  def self.import(main_window, viewers, multi=true)
    ...
    importer = yield file  # delegate to D1/D2 for preparing the importer object
    ...
  end
end

class D1
  include FileImporter
  importer_viewer [:V11, ] do
    ...  # D1 specific handling of importer
  end
end

class D2
  include FileImporter
  importer_viewer [:V21,:v22, ] do
    ...  # D2 specific handling of importer
  end
end

Ответы [ 3 ]

1 голос
/ 11 января 2010

Кажется, что создание модуля было бы элегантным решением. Однако трудно сказать с неопределенным представлением о том, для чего предназначен код. Пример:

module Importer
  def import
    self.whatever # self should be D1 or D2 as the case may be
    # ...
  end
end

class D1
  include Importer
end

class D2
  include Importer
end
1 голос
/ 11 января 2010

Возможно, это не лучшее решение, но на первый взгляд кажется, что классы Dx имеют одинаковое поведение, поэтому подклассы их класса C, который имеет метод self.import, использующий блок для принятия какого-либо другого кода, могут работать. Или это можно сделать, включив модуль тоже.

Так или иначе, что-то вроде этого должно работать (извините за более короткие имена, но они были хороши для создания прототипов). Также обратите внимание, что я изменил имя метода FileImporter.import на другое, чтобы избежать недоразумений и обратите внимание, что я не проверял код:)

module F
  def self.fimport(something)
    yield "file"
  end
end


class C   
  include F 

  def initialize(f)

  end

  def self.import(something, &block)
    F.fimport(something) { |f|
      d = self.new(f)
      block.call
      d
    }
  end
end


class D1 < C
  def self.import(something)
    super(something){
      puts something
    }
  end
end


class D2 < C
  def self.import(something)
    super(something){
      puts something
    }
  end
end

p D1.import("a")
p D2.import("b")

#=> a
#=> #<D1:0x100163068>
#=> b
#=> #<D2:0x100162e88>
0 голосов
/ 11 января 2010

Учитывая ограниченный код и контекст, я подозреваю, что что-то вроде следующего будет работать для вас. Если ничего другого, вы можете получить представление о том, как использовать Модули, чтобы раскрыть общую функциональность.

module FileImporter
  def self.internal_import(main_window, viewers)
    ...
    importer = yield file
    ...
  end
  private :self.internal_import
end

class D1
    include FileImporter
    def self.import(main_window)
      self.internal_import(main_window, [:V1, ])
    end
end

class D2
    include FileImporter
    def self.import(main_window)
      self.internal_import(main_window, [:V2, ])
    end
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...