¿Pueden los objetos construidos a partir de la misma clase tener definiciones de método únicas?

14

Sé que esto parece una pregunta extraña, ya que el hecho de que dos o más objetos compartan la misma clase es que su comportamiento es el mismo, es decir, sus métodos son idénticos.

Sin embargo, tengo curiosidad por saber si hay algún lenguaje OOP que te permita redefinir los métodos de los objetos de la misma manera que puedes asignar diferentes valores para sus campos. El resultado sería que los objetos construidos a partir de la misma clase ya no exhiban exactamente el mismo comportamiento.

Si no me equivoco, ¿puedes hacer este JavaScript? Junto con esta pregunta, pregunto, ¿por qué alguien querría hacer esto?

    
pregunta Niko Bellic 10.11.2014 - 16:24

10 respuestas

9

Los métodos en la mayoría de los lenguajes OOP (basados en clase) se fijan por tipo.

JavaScript está basado en prototipos, no en clases, por lo que puede anular los métodos en una base por instancia porque no hay una distinción clara entre "clase" y objeto; en realidad, una "clase" en JavaScript es un objeto que es como una plantilla de cómo deberían funcionar las instancias.

Cualquier idioma que permita funciones de primera clase, Scala, Java 8, C # (a través de delegados), etc., puede actuar como si tuviera anulaciones de métodos por instancia; tendría que definir un campo con un tipo de función y luego anularlo en cada instancia.

Scala tiene otra posibilidad; En Scala, puede crear singletons de objetos (utilizando la palabra clave de objeto en lugar de la palabra clave de clase), de modo que puede ampliar su clase y anular los métodos, dando como resultado una nueva instancia de esa clase base con anulaciones.

¿Por qué alguien haría esto? Podría haber docenas de razones. Puede ser que el comportamiento me necesite más definido que lo que permitiría el uso de varias combinaciones de campos. También podría mantener el código desacoplado y organizado mejor. En general, sin embargo, creo que estos casos son más raros y, a menudo, hay una solución más directa que utiliza valores de campo.

    
respondido por el eques 10.11.2014 - 16:38
6

Es difícil adivinar la motivación de tu pregunta, por lo que algunas respuestas posibles podrían o no abordar tu interés real.

Incluso en algunos idiomas que no son prototipos, es posible aproximar este efecto.

En Java, por ejemplo, una clase interna anónima es muy similar a lo que está describiendo: puede crear e instanciar una subclase del original, anulando solo el método o los métodos que desee. La clase resultante será un instanceof la clase original, pero no será la misma clase .

¿Por qué querrías hacer esto? Con las expresiones lambda de Java 8, creo que muchos de los mejores casos de uso desaparecen. Con versiones anteriores de Java, al menos, esto puede evitar una proliferación de clases triviales y de uso limitado. Es decir, cuando tiene una gran cantidad de casos de uso relacionados, que se diferencian en una pequeña forma funcional, puede crearlos casi sobre la marcha (casi), con la diferencia de comportamiento inyectada en el momento que lo necesite.

Dicho esto, incluso antes de J8, esto a menudo se puede modificar para cambiar la diferencia a un campo o tres, e inyectarlos en el constructor. Con J8, por supuesto, el método en sí mismo puede inyectarse en la clase, aunque puede haber una tentación de hacerlo cuando otra refactorización puede ser más limpia (si no es tan genial).

    
respondido por el schnitz 10.11.2014 - 18:52
6

Usted solicitó cualquier idioma que proporcione métodos por instancia. Ya existe una respuesta para Javascript, así que veamos cómo se hace en Common Lisp, donde puedes usar los especialistas en EQL:

;; define a class
(defclass some-class () ())

;; declare a generic method
(defgeneric some-method (x))

;; specialize the method for SOME-CLASS
(defmethod some-method ((x some-class)) 'default-result)

;; create an instance named *MY-OBJECT* of SOME-CLASS
(defparameter *my-object* (make-instance 'some-class))

;; specialize SOME-METHOD for that specific instance
(defmethod some-method ((x (eql *my-object*))) 'specific-result)

;; Call the method on that instance
(some-method *my-object*)
=> SPECIFIC-RESULT

;; Call the method on a new instance
(some-method (make-instance 'some-class))
=> DEFAULT-RESULT

¿Por qué?

EQL-specializers es útil cuando se supone que el argumento sujeto al envío tiene un tipo para el que eql tiene sentido: un número, un símbolo, etc. En general, no lo necesita y simplemente tiene para definir tantas subclases como sea necesario por su problema. Pero a veces, solo debe enviarse de acuerdo con un parámetro que sea, por ejemplo, un símbolo: una expresión case se limitaría a los casos conocidos en la función de envío, mientras que los métodos se pueden agregar y eliminar en cualquier momento.

Además, la especialización en instancias es útil para fines de depuración, cuando desea inspeccionar temporalmente lo que sucede con un objeto específico en su aplicación en ejecución.

    
respondido por el coredump 10.11.2014 - 17:02
5

También puedes hacer esto en Ruby usando objetos singleton:

class A
  def do_something
    puts "Hello!"
  end
end

obj = A.new
obj.do_something

def obj.do_something
  puts "Hello world!"
end

obj.do_something

Produce:

Hello!
Hello world!

En cuanto a los usos, esta es realmente la forma en que Ruby realiza los métodos de clase y módulo. Por ejemplo:

def SomeClass
  def self.hello
    puts "Hello!"
  end
end

En realidad, está definiendo un método singleton hello en el Class object SomeClass .

    
respondido por el Linuxios 10.11.2014 - 18:15
5

Puedes pensar que los métodos por instancia te permiten armar tu propia clase en tiempo de ejecución. Esto puede eliminar una gran cantidad de código de pegamento, ese código que no tiene otro propósito que el de juntar dos clases para hablar entre sí. Mixins son una solución algo más estructurada para el mismo tipo de problemas.

Estás sufriendo un poco por la blub paradox , básicamente porque es difícil ver el valor de una función de idioma hasta que hayas usado esa característica en un programa real. Así que busque oportunidades donde crea que podrían funcionar, pruébelo y vea qué sucede.

Busque en su código grupos de clases que difieran solo por un método. Busque clases cuyo único propósito sea combinar otras dos clases en diferentes combinaciones. Busque métodos que no hagan nada más que pasar una llamada a otro objeto. Busque las clases que se crean instancias utilizando patrones de creación complejos. Esos son todos los posibles candidatos que se reemplazarán por métodos por instancia.

    
respondido por el Karl Bielefeldt 10.11.2014 - 18:31
1

Otras respuestas han mostrado cómo esta es una característica común de los lenguajes dinámicos orientados a objetos, y cómo se puede emular de forma trivial en un lenguaje estático que tiene objetos de funciones de primera clase (por ejemplo, delegados en c #, objetos que anulan al operador () en c ++). En los lenguajes estáticos que carecen de tal función es más difícil, pero aún se puede lograr usando una combinación del patrón de la Estrategia y un método que simplemente delegue su implementación a la estrategia. En efecto, esto es lo mismo que harías en c # con delegados, pero la sintaxis es un poco más complicada.

    
respondido por el Jules 11.11.2014 - 12:03
0

Puedes hacer algo como esto en C # y en la mayoría de los otros idiomas similares.

public class MyClass{
    public Func<A,B> MyABFunc {get;set;}
    public Action<B> MyBAction {get;set;}
    public MyClass(){
        //todo assign MyAFunc and MyBAction
    }
}
    
respondido por el Esben Skov Pedersen 10.11.2014 - 19:53
0

Conceptualmente, aunque en un lenguaje como Java, todas las instancias de una clase deben tener los mismos métodos, es posible hacer que parezca que no lo hacen agregando una capa adicional de direccionamiento indirecto, posiblemente en combinación con clases anidadas .

Por ejemplo, si una clase Foo puede definir una clase anidada abstracta estática QuackerBase que contiene un método quack(Foo) , así como varias otras clases anidadas estáticas derivadas de QuackerBase , cada una con su propia definición de quack(Foo) , entonces si la clase externa tiene un campo quacker de tipo QuackerBase , entonces puede configurar ese campo para identificar una instancia (posiblemente singleton) de cualquiera de sus clases anidadas. Después de hacerlo, invocar quacker.quack(this) ejecutará el método quack de la clase cuya instancia se ha asignado a ese campo.

Debido a que este es un patrón bastante común, Java incluye mecanismos para declarar automáticamente los tipos apropiados. Dichos mecanismos realmente no hacen nada que no se pudiera hacer simplemente usando métodos virtuales y clases estáticas opcionalmente anidadas, pero reducen en gran medida la cantidad de repetitivo necesaria para producir una clase cuyo único propósito es ejecutar un solo método en nombre de otra clase.

    
respondido por el supercat 10.11.2014 - 21:14
0

Creo que esa es la definición de un lenguaje "dinámico" como ruby, groovy & Javascript (y muchos muchos otros). Dynamic se refiere (al menos en parte) a la capacidad de redifinar dinámicamente cómo podría comportarse una instancia de clase sobre la marcha.

No es una buena práctica de OO en general, pero para muchos programadores de lenguaje dinámico los principios de OO no son su máxima prioridad.

Simplifica algunas operaciones complicadas como Monkey-Patching, donde puede modificar una instancia de la clase para permitirle interactuar con una biblioteca cerrada de una manera que no vieron.

    
respondido por el Bill K 10.11.2014 - 23:54
0

No estoy diciendo que sea algo bueno, pero esto es trivialmente posible en Python. No puedo sacar un buen caso de uso de la cabeza, pero estoy seguro de que existen.

    class Foo(object):
        def __init__(self, thing):
            if thing == "Foo":
                def print_something():
                    print "Foo"
            else:
                def print_something():
                    print "Bar"
            self.print_something = print_something

    Foo(thing="Foo").print_something()
    Foo(thing="Bar").print_something()
    
respondido por el Singletoned 11.11.2014 - 22:48

Lea otras preguntas en las etiquetas