如何将类型限制为接口的 class 而不是该接口的实例?

How to limit a type to the class of a interface and not the instance of that interface?

给定模块中的以下接口

module Action
  abstract def perform
end

我想用它来实例化实现它的不同 classes:

class Run
  include Action

  def perform
    puts "run!"
  end
end

class Jump
  include Action

  def perform
    puts "jump!"
  end
end

我知道可以定义像 [] of Action 这样的数组并能够存储 Action 的实例,但我对 classes 而不是实例感兴趣。

我想知道如何定义类型限制,以便我可以存储对实现接口的 class 的引用,而不是特定实例。

我的 objective 能够实例化某些 class 的新实例并能够调用其中的 perform 方法。

此时可以编写如下代码:

actions = [Run, Jump]
actions.each do |klass|
  instance = klass.new.as(Action)
  instance.perform
end

事情会起作用,但是不可能将 classes 的列表存储到实例变量中,因为类型限制有点严格。

这种情况下的类型限制语法是什么?

想到的第一个想法是使用 [] of Action.class,但这行不通。也许这应该可行,但它需要在编译器中使用 change/enhancement。

在此期间您可以这样做:

module Action
  abstract def perform
end

class Run
  include Action

  def perform
    puts "run!"
  end
end

class Jump
  include Action

  def perform
    puts "jump!"
  end
end

module ActionFactory
  abstract def new : Action
end

struct GenericActionFactory(T)
  include ActionFactory

  def new
    T.new
  end
end

ary = [] of ActionFactory
ary << GenericActionFactory(Run).new
ary << GenericActionFactory(Jump).new

action = ary[0].new
p action

action = ary[1].new
p action

@asterite 提出的工厂建议的另一种方法是使用模块 extend 它在 class 上,但也用它来定义实例变量的类型:

module Action
  module Interface
  end

  macro included
    extend Interface
  end

  abstract def perform
end

class Run
  include Action

  def perform
    puts "run!"
  end
end

class Jump
  include Action

  def perform
    puts "jump!"
  end
end

list = [] of Action::Interface
list << Run
list << Jump

list.each do |klass|
  instance = klass.new
  instance.perform
end

这让我也可以使用 Action.class 作为类型要求,因此只能使用实现 Action 的 classes 并且使用 Action::Interface 无需执行任何对从数组中获得的元素进行转换:

class Worker
  @actions = [] of Action::Interface

  def add(action : Action.class)
    @actions << action
  end

  def perform
    @actions.each do |klass|
      instance = klass.new
      instance.perform
    end
  end
end

a = Worker.new
a.add Run
a.add Jump
a.add Jump
a.perform