2017-01-04 1 views
0

den folgenden Schnittstelle in einem Modul Gegeben:Wie kann ein Typ auf die Klasse einer Schnittstelle und nicht auf die Instanz dieser Schnittstelle beschränkt werden?

module Action 
    abstract def perform 
end 

Ich mag es verwenden, um verschiedene Klassen zu instanziiert, die sie implementieren:

class Run 
    include Action 

    def perform 
    puts "run!" 
    end 
end 

class Jump 
    include Action 

    def perform 
    puts "jump!" 
    end 
end 

ich weiß, sind möglich, ein Array wie [] of Action zu definieren und in der Lage sein, Instanzen von Action zu speichern, aber ich interessiere mich für die Klassen anstelle der Instanzen.

Ich würde gerne wissen, wie die Typeinschränkung zu definieren, so dass ich einen Verweis auf die Klasse speichern kann, die die Schnittstelle und nicht eine bestimmte Instanz implementiert.

Mein Ziel ist in der Lage, eine neue Instanz einer bestimmten Klasse instanziieren und in der Lage sein, die perform Methode aufzurufen.

Zu diesem Zeitpunkt ist es möglich, den folgenden Code zu schreiben:

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

Und die Dinge funktionieren wird, aber es wird nicht möglich sein, dass die Liste der Klassen in eine Instanzvariable zu speichern, da die Typeinschränkungen a sind etwas strenger.

Was wäre die Typeinschränkungssyntax für diesen Fall?

+0

Was ist der Grund, jedes Mal eine neue Instanz zu instanziieren, anstatt die gleiche Instanz zu verwenden? – asterite

+0

@asterite Ein Dev könnte die Instanzvariablen in der Funktion ändern. Wenn dies über mehrere Fasern/Threads hinweg aufgerufen wird, kann dies zu Problemen führen. AFAIK eine neue Instanz ist das beste Szenario. –

Antwort

3

Die erste Idee, die in den Sinn kommt, ist die Verwendung [] of Action.class, aber das funktioniert nicht. Vielleicht sollte das funktionieren, aber es wird eine Änderung/Verbesserung im Compiler brauchen.

In der Zwischenzeit können Sie dies tun:

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

Würde in diesem Fall der Compiler nicht 'T' als' Jump | Laufen? Wäre das nicht ein bisschen schwer, wenn Aktionen in der Größenordnung von 100+ liegen könnten? –

+1

Der Compiler stellt intern alle Modulimplementierungen als eine Vereinigung dar, also wäre es dasselbe. Zumindest glaube ich nicht, dass es einen Unterschied machen wird. Auf lange Sicht könnten wir dies verbessern. – asterite

1

Ein alternativer Ansatz zur Anregung von @asterite gemacht Fabrik ist die Verwendung eines Moduls und extend auf der Klasse es aber auch die Art zu definieren, verwenden die Instanzvariablen:

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 

die erlaubte mir auch Anforderung Action.class als Typs so nur Klassen zu verwenden, implementieren Action verwendet werden kann, und das verwenden Action::Interface die Notwendigkeit entfernt auszuführen jedes Casting auf dem Element aus dem Array erhalten:

Verwandte Themen