2014-09-19 47 views
0

抽象类在Ruby代码中看起来像来自远程行星Java的外星人。我试图收集可以替代这种不需要的模式的Ruby技巧。如何避免Ruby中的抽象类模式?

让我们完全随机的例子:

class AbstractRace < Struct.new(:runner_count) 
    def go! 
    runner_count.times.map do |index| 
     Thread.new do 
     run(index) 
     end 
    end.each(&:join) 
    end 

    def run(index) 
    raise 'To be implemented in derivative classes' 
    end 
end 

class RunnerRace < AbstractRace 
    def run(index) 
    puts "I am runner number #{index}. I am running" 
    end 
end 

class CarRace < AbstractRace 
    def run(index) 
    puts "I am car number #{index}. I am accelerating" 
    end 
end 

RunnerRace.new(2).go! 
CarRace.new(2).go! 

如何改写呢?一种可能的方法是使用mixin,像这样:

require 'active_support/concern' 

    module Race 
    extend ActiveSupport::Concern 

    def go! 
     participant_count.times.map do |index| 
     Thread.new do 
      run(index) 
     end 
     end.each(&:join) 
    end 

    module ClassMethods 
     def configure_race(methods) 
     [:participant_count, :run].each do |method_symbol| 
      define_method method_symbol, methods[method_symbol] 
     end 
     end 
    end 
    end 

    class RunnerRace < Struct.new(:runner_count) 
    include Race 

    configure_race participant_count: ->() { runner_count }, 
     run: ->(index) { puts "I am runner number #{index}. I am running" } 
    end 

    class CarRace < Struct.new(:car_count) 
    include Race 

    configure_race participant_count: -> { car_count }, 
     run: ->(index) { puts "I am car number #{index}. I am going" } 
    end 

    RunnerRace.new(2).go! 
    CarRace.new(2).go! 

其他解决方案可能是什么?这种情况有没有常见的成语?

+1

这个问题似乎是脱离主题,因为它是关于重构工作代码,这是更适合http://codereview.stackexchange.com/ – 2014-09-25 05:36:52

回答

3

为什么不仅仅利用Ruby是一种动态语言呢?

class Race 
    attr_reader :participants 

    def initialize(participants) 
    @participants = participants 
    end 

    def go! 
    participants.each_with_index.map do |index,participant| 
     Thread.new do 
     participant.run(index) 
     end 
    end.each(&:join) 
    end 
end 

class CarEntry 
    def run(index) 
    puts "I am car number #{index}. I am going" 
    end 
end 

没有必要在'种族'中运行任何东西来扩展超类。在比赛中所有的任何事情都必须具备的能力,比如说有一个

run(index) 

做某事的方法。

0

你写的东西没有错,我不完全确定你不满意什么。

这就是说,这是Ruby,没有必要在你的AbstractRace类上定义run方法。如果这是困扰你的事情,那么就不要这样做。

你把它放在那里的原因是为了显示你自己和其他开发者可能正在处理的代码,应该有run方法。它正在界定界面。但它绝对不是必需的,但它是制定面向对象的类层次结构的“正确”方法。

0

我想你错过了抽象类

语言如Java使用抽象类的点,因为他们想:

  • 收集在一个地方派生类的通用代码(公共基类) ,并且
  • 防止实例化这个公共基类,因为它是基类。

如果你想实现同样的目标,你可以通过这样做:

  • 创建一个基类的通用代码
  • 给它一个私有的构造:

``` class AbstractRace private:

def initialize end end ```

人们使用抽象类的另一个原因与接口相同:他们要保证某个方法存在于派生类中。

不幸的是,没有像Ruby这样的结构;事实上,这是“非Ruby”。在Ruby中,我们依靠“鸭子打字”,这意味着“如果它嘎嘎声,那是鸭子!”注意:

class Car 
    def drive 
    return 'VROOOM!' 
    end 
end 

class Duck 
    def quack 
    return 'Quack quack!' 
    end 
end 

test_objs = [Car.new, Duck.new] 

test_objects.each do |some_obj| 
    if some_obj.respond_to?(:quack) 
    puts "#{some_obj} is a duck! #{some_obj.quack}" 
    else 
    puts 'Not a duck, sorry. 
    end 
end 

这将输出类似<Duck:0x123456> is a duck! Quack quack!

这是依靠“鸭打字,”通过使用前检查方法的存在。这是Ruby中最接近的成语。

1

在ruby中,我倾向于认为'AbstractRace'更像是一个班级扮演的角色。角色最好封装在模块中(正如你在第一个响应中所建议的那样)。

但是,如果您试图想出一个通用的ruby解决方案,我会建议删除对ActiveSupport :: Concern的引用。这个模块来自Rails,可能并不适用于所有的ruby环境。