Un método en programación orientada a objetos (OOP) es un procedimiento asociado con un mensaje y un objeto. Un objeto consta de datos y comportamiento ; estos comprenden una interfaz, que especifica cómo el objeto puede ser utilizado por cualquiera de sus diversos consumidores.
Los datos se representan como propiedades del objeto y los comportamientos se representan como métodos. Por ejemplo, un Window
objeto podría tener métodos como open
y close
, mientras que su estado (ya sea abierto o cerrado en un momento determinado) sería una propiedad.
En la programación basada en clases, los métodos se definen dentro de una clase y los objetos son instancias de una clase determinada. Una de las capacidades más importantes que proporciona un método es la anulación del método : el mismo nombre (p. Ej., area
) Se puede usar para varios tipos diferentes de clases. Esto permite que los objetos emisores invoquen comportamientos y deleguen la implementación de esos comportamientos al objeto receptor. Un método en la programación Java establece el comportamiento de un objeto de clase. Por ejemplo, un objeto puede enviar un area
mensaje a otro objeto y la fórmula apropiada se invoca si el objeto receptor es un rectangle
, circle
, triangle
, etc.
Los métodos también proporcionan la interfaz que utilizan otras clases para acceder y modificar las propiedades de un objeto; esto se conoce como encapsulación. La encapsulación y la anulación son las dos características principales que distinguen a los métodos y las llamadas a procedimientos.
La invalidación y la sobrecarga de métodos son dos de las formas más importantes en las que un método se diferencia de un procedimiento convencional o una llamada a función. La anulación se refiere a una subclase que redefine la implementación de un método de su superclase. Por ejemplo, findArea
puede ser un método definido en una clase de forma triangle
, etc., cada uno definiría la fórmula apropiada para calcular su área. La idea es mirar los objetos como "cajas negras" para que los cambios en las partes internas del objeto se puedan realizar con un impacto mínimo en los otros objetos que lo utilizan. Esto se conoce como encapsulación y está destinado a facilitar el mantenimiento y la reutilización del código.
La sobrecarga de métodos, por otro lado, se refiere a diferenciar el código utilizado para manejar un mensaje en función de los parámetros del método. Si uno ve el objeto receptor como el primer parámetro en cualquier método, la anulación es solo un caso especial de sobrecarga donde la selección se basa solo en el primer argumento. El siguiente ejemplo simple de Java ilustra la diferencia:
Los métodos de acceso se utilizan para leer los valores de datos de un objeto. Los métodos mutadores se utilizan para modificar los datos de un objeto. Los métodos de administrador se utilizan para inicializar y destruir objetos de una clase, por ejemplo, constructores y destructores.
Estos métodos proporcionan una capa de abstracción que facilita la encapsulación y la modularidad. Por ejemplo, si una clase de cuenta bancaria proporciona un getBalance()
método de acceso para recuperar el saldo actual (en lugar de acceder directamente a los campos de datos del saldo), las revisiones posteriores del mismo código pueden implementar un mecanismo más complejo para la recuperación del saldo (por ejemplo, una base de datos fetch), sin necesidad de cambiar el código dependiente. Los conceptos de encapsulación y modularidad no son exclusivos de la programación orientada a objetos. De hecho, en muchos sentidos, el enfoque orientado a objetos es simplemente la extensión lógica de paradigmas anteriores, como los tipos de datos abstractos y la programación estructurada.
Un constructor es un método que se llama al comienzo de la vida útil de un objeto para crear e inicializar el objeto, un proceso llamado construcción (o instanciación). La inicialización puede incluir una adquisición de recursos. Los constructores pueden tener parámetros, pero normalmente no devuelven valores en la mayoría de los lenguajes. Vea el siguiente ejemplo en Java:
public class Main { String _name; int _roll; Main(String name, int roll) { // constructor method this._name = name; this._roll = roll; } }
Un destructor es un método que se llama automáticamente al final de la vida de un objeto, un proceso llamado destrucción. La destrucción en la mayoría de los lenguajes no permite argumentos del método destructor ni devuelve valores. La destrucción se puede implementar para realizar tareas de limpieza y otras tareas en la destrucción de objetos.
En lenguajes de recolección de basura, como Java, C # y Python, los destructores se conocen como finalizadores. Tienen un propósito y una función similares a los de los destructores, pero debido a las diferencias entre los lenguajes que utilizan la recolección de basura y los lenguajes con administración manual de memoria, la secuencia en la que se llaman es diferente.
Un método abstracto es uno con solo una firma y sin cuerpo de implementación. A menudo se utiliza para especificar que una subclase debe proporcionar una implementación del método. Los métodos abstractos se utilizan para especificar interfaces en algunos lenguajes de programación.
El siguiente código Java muestra una clase abstracta que debe ampliarse:
abstract class Shape { abstract int area(int h, int w); // abstract method signature }
La siguiente subclase amplía la clase principal:
public class Rectangle extends Shape { @Override int area(int h, int w) { return h * w; } }
Si una subclase proporciona una implementación para un método abstracto, otra subclase puede volver a hacerlo abstracto. A esto se le llama restracción.
En la práctica, esto rara vez se usa.
En C #, un método virtual se puede anular con un método abstracto. (Esto también se aplica a Java, donde todos los métodos no privados son virtuales).
class IA { public virtual void M() { } } abstract class IB: IA { public override abstract void M(); // allowed }
Los métodos predeterminados de las interfaces también se pueden restraer, requiriendo subclases para implementarlos. (Esto también se aplica a Java).
interface IA { void M() { } } interface IB: IA { abstract void IA.M(); } class C: IB { } // error: class 'C' does not implement 'IA.M'.
Los métodos de clase son métodos que se llaman en una clase en lugar de una instancia. Por lo general, se utilizan como parte de un metamodelo de objetos. Es decir, para cada clase definida se crea una instancia del objeto de clase en el metamodelo. Los protocolos de metamodelo permiten crear y eliminar clases. En este sentido, brindan la misma funcionalidad que los constructores y destructores descritos anteriormente. Pero en algunos lenguajes como Common Lisp Object System (CLOS), el metamodelo permite al desarrollador alterar dinámicamente el modelo de objetos en tiempo de ejecución: por ejemplo, para crear nuevas clases, redefinir la jerarquía de clases, modificar propiedades, etc.
Los métodos especiales son muy específicos del idioma y un idioma puede admitir ninguno, algunos o todos los métodos especiales definidos aquí. El compilador de un lenguaje puede generar automáticamente métodos especiales predeterminados o un programador puede tener la opción de definir métodos especiales. La mayoría de los métodos especiales no se pueden llamar directamente, sino que el compilador genera código para llamarlos en los momentos adecuados.
Los métodos estáticos están destinados a ser relevantes para todas las instancias de una clase en lugar de para cualquier instancia específica. Son similares a las variables estáticas en ese sentido. Un ejemplo sería un método estático para sumar los valores de todas las variables de cada instancia de una clase. Por ejemplo, si hubiera una Product
clase, podría tener un método estático para calcular el precio promedio de todos los productos.
En Java, un método estático de uso común es:
Math.max(double a, double b)
Este método estático no tiene ningún objeto propietario y no se ejecuta en una instancia. Recibe toda la información de sus argumentos.
Se puede invocar un método estático incluso si aún no existen instancias de la clase. Los métodos estáticos se denominan "estáticos" porque se resuelven en tiempo de compilación en función de la clase a la que se llaman y no de forma dinámica como en el caso de los métodos de instancia, que se resuelven polimórficamente en función del tipo de tiempo de ejecución del objeto.
Los operadores de asignación de copia definen las acciones que debe realizar el compilador cuando se asigna un objeto de clase a un objeto de clase del mismo tipo.
Los métodos de operador definen o redefinen los símbolos de operador y definen las operaciones a realizar con el símbolo y los parámetros del método asociados. Ejemplo de C ++:
#include lt;stringgt; class Data { public: bool operatorlt;(const Dataamp; data) const { return roll_ lt; data.roll_; } bool operator==(const Dataamp; data) const { return name_ == data.name_ amp;amp; roll_ == data.roll_; } private: std::string name_; int roll_; };
Algunos lenguajes de procedimiento se ampliaron con capacidades orientadas a objetos para aprovechar los grandes conjuntos de habilidades y el código heredado para esos lenguajes, pero aún así proporcionan los beneficios del desarrollo orientado a objetos. Quizás el ejemplo más conocido sea C ++, una extensión orientada a objetos del lenguaje de programación C. Debido a los requisitos de diseño para agregar el paradigma orientado a objetos a un lenguaje de procedimientos existente, el paso de mensajes en C ++ tiene algunas capacidades y terminologías únicas. Por ejemplo, en C ++ un método se conoce como función miembro. C ++ también tiene el concepto de funciones virtuales que son funciones miembro que se pueden anular en clases derivadas y permiten el envío dinámico.
Las funciones virtuales son los medios por los cuales una clase C ++ puede lograr un comportamiento polimórfico. Las funciones miembro no virtuales, o métodos regulares, son aquellos que no participan en el polimorfismo.
Ejemplo de C ++:
#include lt;iostreamgt; #include lt;memorygt; class Super { public: virtual ~Super() = default; virtual void IAm() { std::cout lt;lt; "I'm the super class!\n"; } }; class Sub: public Super { public: void IAm() override { std::cout lt;lt; "I'm the subclass!\n"; } }; int main() { std::unique_ptrlt;Supergt; inst1 = std::make_uniquelt;Supergt;(); std::unique_ptrlt;Supergt; inst2 = std::make_uniquelt;Subgt;(); inst1-gt;IAm(); // Calls |Super::IAm|. inst2-gt;IAm(); // Calls |Sub::IAm|. }