una que polimorfismo objetos metodos manejo importar clases clase abstractas ruby abstract-class

polimorfismo - que es una clase en ruby



¿Cómo implementar una clase abstracta en ruby? (16)

Sé que no existe un concepto de clase abstracta en ruby. Pero si es necesario implementarlo, ¿cómo hacerlo? Intenté algo como ...

class A def self.new raise ''Doh! You are trying to write Java in Ruby!'' end end class B < A ... ... end

Pero cuando intento crear una instancia de B, internamente va a llamar a A.new que va a generar la excepción.

Además, los módulos no se pueden crear instancias, pero tampoco se pueden heredar. hacer que el nuevo método sea privado tampoco funcionará. ¿Alguna sugerencia?


¿Para qué intentas servir con una clase abstracta? Probablemente haya una mejor manera de hacerlo en ruby, pero no proporcionó ningún detalle.

Mi puntero es esto; usa un mixin no herencia.


En los últimos 6 1/2 años de programación de Ruby, no he necesitado una clase abstracta una vez.

Si estás pensando que necesitas una clase abstracta, estás pensando demasiado en un lenguaje que los proporciona / requiere, no en Ruby como tal.

Como otros han sugerido, un mixin es más apropiado para cosas que se supone que son interfaces (como Java las define), y repensar su diseño es más apropiado para cosas que "necesitan" clases abstractas de otros lenguajes como C ++.



Lo hice de esta manera, por lo que redefine la clase nueva en el niño para encontrar una nueva en la clase no abstracta. Todavía no veo ninguna práctica en el uso de clases abstractas en ruby.

puts ''test inheritance'' module Abstract def new throw ''abstract!'' end def inherited(child) @abstract = true puts ''inherited'' non_abstract_parent = self.superclass; while non_abstract_parent.instance_eval {@abstract} non_abstract_parent = non_abstract_parent.superclass end puts "Non abstract superclass is #{non_abstract_parent}" (class << child;self;end).instance_eval do define_method :new, non_abstract_parent.method(''new'') # # Or this can be done in this style: # define_method :new do |*args,&block| # non_abstract_parent.method(''new'').unbind.bind(self).call(*args,&block) # end end end end class AbstractParent extend Abstract def initialize puts ''parent initializer'' end end class Child < AbstractParent def initialize puts ''child initializer'' super end end # AbstractParent.new puts Child.new class AbstractChild < AbstractParent extend Abstract end class Child2 < AbstractChild end puts Child2.new


Mi 2 ¢: opto por una mezcla DSL simple y liviana:

module Abstract extend ActiveSupport::Concern included do # Interface for declaratively indicating that one or more methods are to be # treated as abstract methods, only to be implemented in child classes. # # Arguments: # - methods (Symbol or Array) list of method names to be treated as # abstract base methods # def self.abstract_methods(*methods) methods.each do |method_name| define_method method_name do raise NotImplementedError, ''This is an abstract base method. Implement in your subclass.'' end end end end end # Usage: class AbstractBaseWidget include Abstract abstract_methods :widgetify end class SpecialWidget < AbstractBaseWidget end SpecialWidget.new.widgetify # <= raises NotImplementedError

Y, por supuesto, agregar otro error para inicializar la clase base sería trivial en este caso.


No hay nada malo con su enfoque. Generar un error al inicializar parece estar bien, siempre que todas las subclases anuladas se inicialicen, por supuesto. Pero no quieres definir self.new así. Esto es lo que haría.

class A class AbstractClassInstiationError < RuntimeError; end def initialize raise AbstractClassInstiationError, "Cannot instantiate this class directly, etc..." end end

Otro enfoque sería poner toda esa funcionalidad en un módulo, que como usted mencionó nunca puede ser instigado. Luego incluya el módulo en sus clases en lugar de heredar de otra clase. Sin embargo, esto rompería cosas como súper.

Entonces depende de cómo quieras estructurarlo. Aunque los módulos parecen una solución más limpia para resolver el problema de "¿Cómo escribo algunas cosas que está diseñado para otras clases?"


No me gusta usar clases abstractas en Ruby (casi siempre hay una mejor manera). Sin embargo, si realmente crees que es la mejor técnica para la situación, puedes usar el siguiente fragmento para ser más declarativo sobre qué métodos son abstractos:

module Abstract def abstract_methods(*args) args.each do |name| class_eval(<<-END, __FILE__, __LINE__) def #{name}(*args) raise NotImplementedError.new("You must implement #{name}.") end END # important that this END is capitalized, since it marks the end of <<-END end end end require ''rubygems'' require ''rspec'' describe "abstract methods" do before(:each) do @klass = Class.new do extend Abstract abstract_methods :foo, :bar end end it "raises NoMethodError" do proc { @klass.new.foo }.should raise_error(NoMethodError) end it "can be overridden" do subclass = Class.new(@klass) do def foo :overridden end end subclass.new.foo.should == :overridden end end

Básicamente, simplemente llama a abstract_methods con la lista de métodos que son abstractos, y cuando una instancia de la clase abstracta los NotImplementedError , se NotImplementedError una excepción NotImplementedError .


Otra respuesta:

module Abstract def self.append_features(klass) # access an object''s copy of its class''s methods & such metaclass = lambda { |obj| class << obj; self ; end } metaclass[klass].instance_eval do old_new = instance_method(:new) undef_method :new define_method(:inherited) do |subklass| metaclass[subklass].instance_eval do define_method(:new, old_new) end end end end end

Esto se basa en el #method_missing normal para informar métodos no implementados, pero evita que se implementen clases abstractas (incluso si tienen un método de inicialización)

class A include Abstract end class B < A end B.new #=> #<B:0x24ea0> A.new # raises #<NoMethodError: undefined method `new'' for A:Class>

Como han dicho los otros carteles, probablemente deberías estar usando mixin, en lugar de una clase abstracta.


Personalmente elevo NotImplementedError en métodos de clases abstractas. Pero es posible que desee dejarlo fuera del ''nuevo'' método, por las razones que ha mencionado.


Prueba esto:

class A def initialize raise ''Doh! You are trying to instantiate an abstract class!'' end end class B < A def initialize end end



Si desea ir con una clase que no se puede desinstalar, en su método A.new, verifique si self == A antes de arrojar el error.

Pero, en realidad, un módulo se parece más a lo que quiere aquí; por ejemplo, Enumerable es el tipo de cosa que podría ser una clase abstracta en otros idiomas. Técnicamente no puede subclasificarlos, pero llamar include SomeModule logra aproximadamente el mismo objetivo. ¿Hay alguna razón por la que esto no funcionará para ti?


Solo para sonar aquí tarde, creo que no hay razón para evitar que alguien cree una instancia de la clase abstracta, especialmente porque pueden agregar métodos sobre la marcha .

Los lenguajes de pato, como Ruby, usan la presencia / ausencia o el comportamiento de los métodos en tiempo de ejecución para determinar si deben llamarse o no. Por lo tanto, su pregunta, tal como se aplica a un método abstracto, tiene sentido

def get_db_name raise ''this method should be overriden and return the db name'' end

y eso debería ser sobre el final de la historia. La única razón para usar clases abstractas en Java es insistir en que ciertos métodos se "completan" mientras que otros tienen su comportamiento en la clase abstracta. En un lenguaje de pato, el foco está en los métodos, no en las clases / tipos, por lo que debe mover sus preocupaciones a ese nivel.

En su pregunta, básicamente está tratando de recrear la palabra clave abstract de Java, que es un olor de código para hacer Java en Ruby.


También existe esta pequeña gema de tipo abstract_type , que permite declarar clases abstractas y módulos de una manera discreta.

Ejemplo (del archivo README.md ):

class Foo include AbstractType # Declare abstract instance method abstract_method :bar # Declare abstract singleton method abstract_singleton_method :baz end Foo.new # raises NotImplementedError: Foo is an abstract type Foo.baz # raises NotImplementedError: Foo.baz is not implemented # Subclassing to allow instantiation class Baz < Foo; end object = Baz.new object.bar # raises NotImplementedError: Baz#bar is not implemented


para cualquier persona en el mundo de los rieles, la implementación de un modelo de ActiveRecord como clase abstracta se hace con esta declaración en el archivo de modelo:

self.abstract_class = true


class A private_class_method :new end class B < A public_class_method :new end