【问题标题】:How to limit a type to the class of a interface and not the instance of that interface?如何将类型限制为接口的类而不是该接口的实例?
【发布时间】:2017-02-05 23:43:43
【问题描述】:

给定一个模块中的以下接口

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

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

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

我的目标是能够实例化某个类的新实例并能够在其中调用perform 方法。

此时可以编写如下代码:

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

一切都会奏效,但是由于类型限制更加严格,因此无法将该类列表存储到实例变量中。

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

【问题讨论】:

  • 每次都实例化一个新实例而不是使用同一个实例的原因是什么?
  • @asterite 开发人员可能正在更改执行中的实例变量。跨多个光纤/线程调用它可能会导致问题。 AFAIK 一个新的实例是最好的方案。

标签: type-inference crystal-lang


【解决方案1】:

想到的第一个想法是使用[] of Action.class,但这不起作用。也许这应该可行,但它需要在编译器中进行更改/增强。

与此同时,您可以这样做:

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

【讨论】:

  • 在这种情况下编译器不会将T 推断为Jump | Run 吗?当动作可以达到 100+ 的数量级时,这会不会有点沉重?
  • 编译器在内部将所有模块实现表示为一个联合,因此它是相同的。至少目前我认为这不会有什么不同。从长远来看,我们可能会改进这一点。
【解决方案2】:

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

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 的类才能使用,并且使用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

【讨论】:

    猜你喜欢
    • 2023-03-11
    • 2013-01-15
    • 1970-01-01
    • 2021-10-11
    • 2017-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多