抽象类在Ruby代码中看起来像来自远程行星Java的外星人。我试图收集可以替代这种不需要的模式的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!
还有哪些其他解决方案?这种情况有共同的习惯用法吗?
答案 0 :(得分: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)
做某事的方法。
答案 1 :(得分:1)
在红宝石中,我倾向于想到&#39; AbstractRace&#39;更像是一个班级的角色。角色最好封装在模块中(正如您在第一次回复中所建议的那样)。
但是,如果您尝试提供一般的ruby解决方案,我建议删除对ActiveSupport :: Concern的引用。此模块来自Rails,可能并非在所有ruby环境中都可用。
答案 2 :(得分:0)
你在那里写的东西没有错,我不完全确定你不满意。
话虽如此,这是Ruby,并且无需在run
类上定义AbstractRace
方法。如果那是什么困扰你,那么就不要这样做。
您放在那里的原因是为了向您自己和其他开发人员展示可能正在处理Race
应该具有run
方法的代码。它定义了界面。它绝对不是必需的,但它是&#34;适当的&#34;制作面向对象的类层次结构的方法。
答案 3 :(得分:0)
我认为你错过了抽象类的重点
Java之类的语言使用抽象类,因为他们想要:
如果您想实现相同的目标,可以通过以下方式实现:
``` 类AbstractRace 私人:
def初始化 结束 结束 ```
人们使用抽象类的另一个原因与接口相同:他们希望保证派生类上存在某些方法。
不幸的是,没有像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中最接近的习语。