2010-10-23 28 views
9

Sono nuovo di Ruby. Un semplice esempio, quello che mi serve:Alternative alle classi astratte in Ruby?

class Animal 
    abstract eat() 

class Cat < Animal 
    eat(): 
    implementation 

class Dog < Animal 
    eat(): 
    implementation 

In altre parole, il metodo mangiare() dovrebbe essere richiesta per tutti i rami che si estendono animale.

In JAVA vorrei solo usare una classe astratta, ma dopo aver fatto qualche ricerca ho scoperto che molte persone non la usano in Ruby e si consigliano invece mixin/moduli.

Tuttavia, non capisco, se i moduli possono fare di più che includere solo metodi di aggiunta. Per essere precisi, i moduli possono impostare i requisiti per le classi che metodi devono implementare (se sì, un esempio sarebbe apprezzato)?

Per riassumere, cosa dovrei usare in questo caso, quando voglio essere sicuro, che tutte le classi dello stesso tipo hanno metodi particolari e li implementano a modo loro?

+1

Qual è la frase su "NotImplementedError" e il carrello di atterraggio degli aeroplani? –

risposta

9

Ruby non offre questa funzionalità, no. Sei responsabile di assicurarti che le tue lezioni implementino ciò che dovrebbero implementare.

Parte del motivo per cui tale funzionalità è impossibile per Ruby è che è possibile riaprire le classi Ruby e Ruby supporta il caricamento di codice arbitrario in fase di runtime, quindi non possiamo sapere se una classe implementa una determinata interfaccia finché non proviamo a chiamare esso.

Supponendo un Animal deve avere un metodo eat, e faccio la seguente:

class Cat < Animal 
    def talk 
    puts "meow" 
    end 
end 

class Cat 
    def eat 
    puts "om nom nom" 
    end 
end 

Entro la fine di quel file, il Cat avrà il suo eat definizione, perché le classi di Ruby possono riaperto e modificato più volte . L'errore del codice dovrebbe uscire dopo la prima definizione perché eat non è stato ancora definito? Questa implementazione farebbe male più di quanto sarebbe di aiuto, poiché la riapertura delle lezioni è comune, anche se questo esempio è inventato. Dovrebbe verificarsi un errore quando viene chiamato il metodo eat e non esiste, quindi possiamo essere certi che è definito una volta che ne abbiamo bisogno? Bene, se mancasse il metodo, lo corrisponderebbe allo. L'interprete non può mai sapere se un'altra definizione di classe è in arrivo, quindi non potrà mai interromperla fino a quando non viene effettivamente chiamato il metodo.

In breve, le superclassi non possono semplicemente richiedere un metodo da definire in Ruby, perché la natura dinamica delle classi contraddice tale obiettivo.

Siamo spiacenti! Questo è un posto in cui i test di unità potrebbero rivelarsi utili, tuttavia, per garantire che le sottoclassi facciano ciò che dovrebbero fare, comunque.

+0

+1 per "om nom nom": D Hai visto [l'implementazione] (http://www.facebook.com/video/video.php?v=1055625287695)? – edgerunner

+0

Ti credo che non può essere fatto in Ruby, ma dubito che la compilazione abbia qualcosa a che fare con esso. Python ha un modello di progettazione di classe base astratto comune usando il metaclass "ABCMeta", e non richiede compilazione. –

+0

@SteveZelaznik: corretto, modificato :) Il bit importante è che supporta la riapertura della classe e il caricamento del codice in fase di runtime, che * sono * molto più semplici in un ambiente interpretato, ma, sì, non è impossibile. In ogni caso, Python non supporta la riapertura della classe, quindi è libero di implementare classi astratte :) – Matchu

1

Non c'è equivalente a classi astratte in Ruby. Principalmente questo perché Ruby è tipizzato dinamicamente, il che significa che il concetto di classi astratte non si applica realmente.

2

Yo può semplicemente utilizzare metodi vuoti o farli sollevare errori per forzare le sottoclassi a implementarle.

class Base 
    def abstract_method 
    end 

    def mandatory_abstract_method 
    raise NotImplementedError.new("You must implement this") 
    end 
end 

Il lato negativo è ciò che verrà applicato solo quando viene effettivamente chiamato il metodo. Ruby è un linguaggio dinamico e non ha alcun controllo in fase di compilazione.

19

Utilizzare un modulo che definisce i metodi che devono essere implementati.

module Animal 
    def eat 
    raise NotImplementedError 
    end 
end 

class Cat 
    include Animal 

    def eat 
    "Om nom nom" 
    end 
end 

class Dog 
    include Animal 
end 

c = Cat.new 
c.eat # => "Om nom nom" 

d = Dog.new 
d.eat # => NotImplementedError 
0

Se si desidera implementare l'astrazione, si utilizza la gemma dell'astrazione.

sudo gem install abstraction 

Esempio di riferimento Esempio da Abstraction gem WIKI.

class Car 
    abstract 

    def go_forward 
    # ... 
end 
end 

Car.new 
> AbstractClassError: Car is an abstract class and cannot be instantiated 

class Convertible < Car 
    def door_count 
    2 
    end 
end 

class Sedan < Car 
    def door_count 
    4 
    end 
end 

Convertible.new # => #<Convertible:0x8fdf4> 
+0

Questo impedisce solo l'istanza della classe astratta. In questo modo garantisce che i metodi astratti siano implementati nelle sottoclassi concrete. –

Problemi correlati