2017-01-04 8 views
0

Dans le cas suivant Interface dans un module:Comment limiter un type à la classe d'une interface et non à l'instance de cette interface?

module Action 
    abstract def perform 
end 

Je voudrais l'utiliser pour instancier différentes classes qui mettent en œuvre:

class Run 
    include Action 

    def perform 
    puts "run!" 
    end 
end 

class Jump 
    include Action 

    def perform 
    puts "jump!" 
    end 
end 

Je sais est possible de définir un tableau comme [] of Action et être capable de stocker des instances de Action, mais je suis intéressé par les classes au lieu des instances.

Je voudrais savoir comment définir la restriction de type afin que je puisse stocker une référence à la classe qui implémente l'interface et non une instance spécifique.

Mon objectif est d'être capable d'instancier une nouvelle instance de certaine classe et de pouvoir appeler la méthode perform.

A cette époque, il est possible d'écrire le code suivant:

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

Et les choses fonctionnent, mais il ne sera pas possible de stocker cette liste des classes dans une variable d'instance étant donné que les restrictions de type sont un peu plus strict.

Quelle serait la syntaxe de restriction de type pour ce cas?

+0

Quelle est la raison pour instancier une nouvelle instance à chaque fois, au lieu d'utiliser la même instance? – asterite

+0

@asterite a dev peut modifier les variables d'instance dans le fichier perform. Le fait de faire appel à plusieurs fibres/threads peut entraîner des problèmes. AFAIK une nouvelle instance est le meilleur scénario. –

Répondre

3

La première idée qui vient à l'esprit est l'utilisation de [] of Action.class, mais cela ne fonctionne pas. Peut-être que cela devrait fonctionner, mais il faudra un changement/amélioration dans le compilateur.

En attendant, vous pouvez le faire:

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 
+0

Dans ce cas, le compilateur n'indiquerait pas 'T' comme' Jump | Run'? Ne serait-ce pas un peu lourd quand les actions peuvent être de l'ordre de 100+? –

+1

Le compilateur représente en interne toutes les implémentations de module sous la forme d'une union, donc ce serait la même chose. Au moins pour l'instant je ne pense pas que cela fera une différence. Dans le long terme, nous pourrions améliorer cela. – asterite

1

Une approche alternative à la suggestion faite par l'usine @asterite est l'utilisation d'un module et extend sur la classe mais aussi pour définir le type des variables d'instance:

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 

Cela m'a permis d'utiliser également Action.class comme exigence de type classes afin que qui implémentent Action peut être utilisé et en utilisant Action::Interface enlevé la nécessité d'effectuer tout moulage sur l'élément obtenu à partir du tableau:

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