Programación orientada a objetos
La programación orientada a objetos (POO) es un paradigma de programación basado en el concepto de "objetos", que pueden contener datos y código: datos en forma de campos (a menudo conocidos como atributos o propiedades) y código, en forma de procedimientos. (a menudo conocidos como métodos).
Una característica común de los objetos es que se les adjuntan procedimientos (o métodos) que pueden acceder y modificar los campos de datos del objeto. En esta marca de programación orientada a objetos, suele haber un nombre especial como o utilizado para referirse al objeto actual. En OOP, los programas de computadora se diseñan a partir de objetos que interactúan entre sí. Los lenguajes OOP son diversos, pero los más populares están basados en clases, lo que significa que los objetos son instancias de clases, que también determinan sus tipos. this
self
Muchos de los lenguajes de programación más utilizados (como C ++, Java, Python, etc.) son multiparadigmáticos y admiten la programación orientada a objetos en mayor o menor grado, generalmente en combinación con programación imperativa y procedimental. Los lenguajes orientados a objetos significativos incluyen: Java, C++, C#, Python, R, PHP, Visual Basic.NET, JavaScript, Ruby, Perl, SIMSCRIPT, Object Pascal, Objective-C, Dart, Swift, Scala, Kotlin, Common Lisp, MATLAB y Smalltalk.
Historia
La terminología que invoca "objetos" y "orientado" en el sentido moderno de la programación orientada a objetos hizo su primera aparición en el MIT a fines de la década de 1950 y principios de la de 1960. En el entorno del grupo de inteligencia artificial, ya en 1960, "objeto" podía referirse a elementos identificados (átomos LISP) con propiedades (atributos); Alan Kay luego citó una comprensión detallada de las funciones internas de LISP como una fuerte influencia en su pensamiento en 1966.
Pensé que los objetos eran como células biológicas y/o computadoras individuales en una red, solo capaces de comunicarse con mensajes (por lo que la mensajería apareció desde el principio; tomó un tiempo ver cómo enviar mensajes en un lenguaje de programación lo suficientemente eficiente como para ser útil).
alan kay,
Otro ejemplo temprano del MIT fue Sketchpad creado por Ivan Sutherland en 1960-1961; en el glosario del informe técnico de 1963 basado en su disertación sobre Sketchpad, Sutherland definió las nociones de "objeto" e "instancia" (con el concepto de clase cubierto por "maestro" o "definición"), aunque especializado en interacción gráfica. Además, una versión de MIT ALGOL, AED-0, estableció un vínculo directo entre las estructuras de datos ("plexes", en ese dialecto) y los procedimientos, prefigurando lo que luego se denominó "mensajes", "métodos" y "funciones miembro".
Simula introdujo conceptos importantes que hoy en día son una parte esencial de la programación orientada a objetos, como clase y objeto, herencia y vinculación dinámica. El lenguaje de programación orientado a objetos Simula fue utilizado principalmente por investigadores involucrados en el modelado físico, como modelos para estudiar y mejorar el movimiento de barcos y su contenido a través de los puertos de carga.
En la década de 1970, Alan Kay, Dan Ingalls y Adele Goldberg desarrollaron la primera versión del lenguaje de programación Smalltalk en Xerox PARC. Smalltalk-72 incluía un entorno de programación y se tipeaba dinámicamente, y al principio se interpretaba, no se compilaba. Smalltalk se destacó por su aplicación de orientación a objetos a nivel de lenguaje y su entorno de desarrollo gráfico. Smalltalk pasó por varias versiones y creció el interés por el lenguaje. Si bien Smalltalk fue influenciado por las ideas introducidas en Simula 67, fue diseñado para ser un sistema completamente dinámico en el que las clases se pudieran crear y modificar dinámicamente.
En la década de 1970, Smalltalk influyó en la comunidad Lisp para incorporar técnicas basadas en objetos que se introdujeron a los desarrolladores a través de la máquina Lisp. La experimentación con varias extensiones de Lisp (como LOOPS y Flavours que introducen herencia múltiple y mixins) finalmente condujo al Sistema de objetos Common Lisp, que integra programación funcional y programación orientada a objetos y permite la extensión a través de un protocolo de metaobjetos. En la década de 1980, hubo algunos intentos de diseñar arquitecturas de procesador que incluyeran soporte de hardware para objetos en la memoria, pero no tuvieron éxito. Los ejemplos incluyen Intel iAPX 432 y Linn Smart Rekursiv.
En 1981, Goldberg editó la edición de agosto de Byte Magazine, presentando Smalltalk y la programación orientada a objetos a un público más amplio. En 1986, la Association for Computing Machinery organizó la primera Conferencia sobre programación, sistemas, lenguajes y aplicaciones orientadas a objetos (OOPSLA), a la que asistieron inesperadamente 1000 personas. A mediados de la década de 1980, Brad Cox, que había utilizado Smalltalk en ITT Inc., desarrolló Objective-C, y Bjarne Stroustrup, que había utilizado Simula para su tesis doctoral, finalmente creó el C++ orientado a objetos.En 1985, Bertrand Meyer también produjo el primer diseño del lenguaje Eiffel. Centrado en la calidad del software, Eiffel es un lenguaje de programación puramente orientado a objetos y una notación que soporta todo el ciclo de vida del software. Meyer describió el método de desarrollo de software de Eiffel, basado en un pequeño número de ideas clave de la ingeniería de software y la informática, en Construcción de software orientada a objetos. Esencial para el enfoque de calidad de Eiffel es el mecanismo de confiabilidad de Meyer, Design by Contract, que es una parte integral tanto del método como del lenguaje.
A principios y mediados de la década de 1990, la programación orientada a objetos se desarrolló como el paradigma de programación dominante cuando los lenguajes de programación que soportan las técnicas estuvieron ampliamente disponibles. Estos incluían Visual FoxPro 3.0, C++ y Delphi. Su dominio se vio reforzado por la creciente popularidad de las interfaces gráficas de usuario, que dependen en gran medida de las técnicas de programación orientada a objetos. Un ejemplo de una biblioteca de GUI dinámica estrechamente relacionada y un lenguaje OOP se puede encontrar en los marcos Cocoa en Mac OS X, escrito en Objective-C, una extensión de mensajería dinámica orientada a objetos para C basada en Smalltalk. Los kits de herramientas de programación orientada a objetos también aumentaron la popularidad de la programación basada en eventos (aunque este concepto no se limita a la programación orientada a objetos).
En ETH Zürich, Niklaus Wirth y sus colegas también habían estado investigando temas como la abstracción de datos y la programación modular (aunque esto había sido de uso común en la década de 1960 o antes). Modula-2 (1978) incluía ambos, y su diseño posterior, Oberon, incluía un enfoque distintivo de la orientación a objetos, las clases y demás.
Se han agregado funciones orientadas a objetos a muchos lenguajes existentes anteriormente, incluidos Ada, BASIC, Fortran, Pascal y COBOL. Agregar estas funciones a lenguajes que no fueron diseñados inicialmente para ellos a menudo generaba problemas de compatibilidad y mantenibilidad del código.
Más recientemente, han surgido una serie de lenguajes que están principalmente orientados a objetos, pero que también son compatibles con la metodología procedimental. Dos de estos lenguajes son Python y Ruby. Probablemente los lenguajes orientados a objetos recientes de mayor importancia comercial son Java, desarrollado por Sun Microsystems, así como C# y Visual Basic.NET (VB.NET), ambos diseñados para la plataforma.NET de Microsoft. Cada uno de estos dos marcos muestra, a su manera, el beneficio de usar OOP al crear una abstracción de la implementación. VB.NET y C# admiten la herencia entre idiomas, lo que permite que las clases definidas en un idioma se conviertan en subclases definidas en el otro idioma.
Características
La programación orientada a objetos utiliza objetos, pero no todas las técnicas y estructuras asociadas se admiten directamente en lenguajes que afirman ser compatibles con OOP. Realiza operaciones sobre operandos. Las características que se enumeran a continuación son comunes entre los lenguajes que se consideran fuertemente orientados a clases y objetos (o multiparadigma con soporte para programación orientada a objetos), con notables excepciones mencionadas.
Compartido con lenguajes no orientados a objetos
- Variables que pueden almacenar información formateada en una pequeña cantidad de tipos de datos integrados, como números enteros y caracteres alfanuméricos. Esto puede incluir estructuras de datos como cadenas, listas y tablas hash que están integradas o son el resultado de combinar variables usando punteros de memoria.
- Procedimientos, también conocidos como funciones, métodos, rutinas o subrutinas, que reciben entradas, generan salidas y manipulan datos. Los lenguajes modernos incluyen construcciones de programación estructurada como bucles y condicionales.
El soporte de programación modular brinda la capacidad de agrupar procedimientos en archivos y módulos con fines organizativos. Los módulos tienen espacios de nombres para que los identificadores en un módulo no entren en conflicto con un procedimiento o variable que comparte el mismo nombre en otro archivo o módulo.
Objetos y clases
Los lenguajes que admiten la programación orientada a objetos (POO) suelen utilizar la herencia para la reutilización del código y la extensibilidad en forma de clases o prototipos. Los que usan clases admiten dos conceptos principales:
- Clases: las definiciones del formato de datos y los procedimientos disponibles para un determinado tipo o clase de objeto; también pueden contener datos y procedimientos (conocidos como métodos de clase), es decir, las clases contienen los miembros de datos y funciones miembro
- Objetos: instancias de clases
Los objetos a veces corresponden a cosas que se encuentran en el mundo real. Por ejemplo, un programa de gráficos puede tener objetos como "círculo", "cuadrado", "menú". Un sistema de compras en línea puede tener objetos como "carrito de compras", "cliente" y "producto". A veces, los objetos representan entidades más abstractas, como un objeto que representa un archivo abierto o un objeto que brinda el servicio de traducir las medidas del uso habitual de EE. UU. al sistema métrico.
Se dice que cada objeto es una instancia de una clase particular (por ejemplo, un objeto con su campo de nombre establecido en "María" podría ser una instancia de la clase Empleado). Los procedimientos en la programación orientada a objetos se conocen como métodos; Las variables también se conocen como campos, miembros, atributos o propiedades. Esto conduce a los siguientes términos:
- Variables de clase: pertenecen a la clase como un todo; solo hay una copia de cada uno
- Variables de instancia o atributos: datos que pertenecen a objetos individuales; cada objeto tiene su propia copia de cada uno
- Variables miembro: se refiere tanto a la clase como a las variables de instancia definidas por una clase en particular.
- Métodos de clase: pertenecen a la clase como un todo y tienen acceso solo a variables de clase y entradas de la llamada al procedimiento.
- Métodos de instancia: pertenecen a objetos individuales y tienen acceso a variables de instancia para el objeto específico al que se llaman, entradas y variables de clase.
Se accede a los objetos como si fueran variables con una estructura interna compleja y, en muchos lenguajes, son efectivamente punteros, que sirven como referencias reales a una sola instancia de dicho objeto en la memoria dentro de un montón o pila. Proporcionan una capa de abstracción que se puede utilizar para separar el código interno del externo. El código externo puede usar un objeto al llamar a un método de instancia específico con un determinado conjunto de parámetros de entrada, leer una variable de instancia o escribir en una variable de instancia. Los objetos se crean llamando a un tipo especial de método en la clase conocido como constructor. Un programa puede crear muchas instancias de la misma clase a medida que se ejecuta, que funcionan de forma independiente. Esta es una manera fácil de usar los mismos procedimientos en diferentes conjuntos de datos.
La programación orientada a objetos que utiliza clases a veces se denomina programación basada en clases, mientras que la programación basada en prototipos no suele utilizar clases. Como resultado, se utiliza una terminología significativamente diferente pero análoga para definir los conceptos de objeto e instancia.
En algunos idiomas, las clases y los objetos se pueden componer utilizando otros conceptos como rasgos y mixins.
Basado en clases vs basado en prototipos
En los lenguajes basados en clases, las clases se definen de antemano y los objetos se instancian en función de las clases. Si se instancian dos objetos apple y orange de la clase Fruit, son inherentemente frutas y está garantizado que puede manejarlos de la misma manera; por ejemplo, un programador puede esperar la existencia de los mismos atributos como color o sugar_content o is_ripe.
En los lenguajes basados en prototipos, los objetos son las entidades primarias. Ni siquiera existen clases. El prototipo de un objeto es simplemente otro objeto al que se vincula el objeto. Cada objeto tiene un enlace prototipo (y solo uno). Se pueden crear nuevos objetos basados en objetos ya existentes elegidos como su prototipo. Puede llamar fruta a dos objetos diferentes, manzana y naranja, si el objeto fruta existe, y tanto la manzana como la naranja tienen fruta como prototipo. La idea de la fruta.class no existe explícitamente, sino como la clase de equivalencia de los objetos que comparten el mismo prototipo. Los atributos y métodos del prototipo se delegan a todos los objetos de la clase de equivalencia definida por este prototipo. Los atributos y métodos que posee individualmente el objeto no pueden ser compartidos por otros objetos de la misma clase de equivalencia; por ejemplo, el atributo sugar_content puede no estar presente inesperadamente en apple. Solo se puede implementar la herencia única a través del prototipo.
Despacho dinámico/paso de mensajes
Es responsabilidad del objeto, no de ningún código externo, seleccionar el código de procedimiento para ejecutar en respuesta a una llamada de método, generalmente buscando el método en tiempo de ejecución en una tabla asociada con el objeto. Esta función se conoce como despacho dinámico. Si la variabilidad de la llamada se basa en algo más que el único tipo de objeto en el que se llama (es decir, al menos otro objeto de parámetro está involucrado en la elección del método), se habla de envío múltiple.
Una llamada de método también se conoce como paso de mensajes. Se conceptualiza como un mensaje (el nombre del método y sus parámetros de entrada) que se pasa al objeto para su envío.
Abstracción de datos
La abstracción de datos es un patrón de diseño en el que los datos son visibles solo para funciones relacionadas semánticamente, a fin de evitar el uso indebido. El éxito de la abstracción de datos conduce a la incorporación frecuente de la ocultación de datos como un principio de diseño en la programación puramente funcional y orientada a objetos.
Si una clase no permite que el código de llamada acceda a los datos del objeto interno y permite el acceso solo a través de métodos, esta es una forma fuerte de abstracción u ocultación de información conocida como abstracción. Algunos lenguajes (Java, por ejemplo) permiten que las clases apliquen restricciones de acceso explícitamente, por ejemplo, denotando datos internos con la private
palabra clave y designando métodos destinados a ser utilizados por código fuera de la clase con la public
palabra clave. Los métodos también pueden diseñarse en niveles públicos, privados o intermedios como protected
(que permite el acceso desde la misma clase y sus subclases, pero no objetos de una clase diferente). En otros lenguajes (como Python), esto se aplica solo por convención (por ejemplo, private
los métodos pueden tener nombres que comienzan con un guión bajo).
Encapsulación
La encapsulación evita que el código externo se preocupe por el funcionamiento interno de un objeto. Esto facilita la refactorización del código, por ejemplo, permitiendo que el autor de la clase cambie la forma en que los objetos de esa clase representan sus datos internamente sin cambiar ningún código externo (siempre que las llamadas a métodos "públicos" funcionen de la misma manera). También alienta a los programadores a colocar todo el código relacionado con un determinado conjunto de datos en la misma clase, lo que lo organiza para que otros programadores lo comprendan fácilmente. La encapsulación es una técnica que fomenta el desacoplamiento.
Composición, herencia y delegación
Los objetos pueden contener otros objetos en sus variables de instancia; esto se conoce como composición de objetos. Por ejemplo, un objeto de la clase Empleado puede contener (directamente o mediante un puntero) un objeto de la clase Dirección, además de sus propias variables de instancia como "nombre" y "cargo". La composición de objetos se usa para representar relaciones "tiene un": cada empleado tiene una dirección, por lo que cada objeto Empleado tiene acceso a un lugar para almacenar un objeto Dirección (ya sea directamente incrustado dentro de sí mismo o en una ubicación separada dirigida a través de un puntero).
Los idiomas que admiten clases casi siempre admiten la herencia. Esto permite organizar las clases en una jerarquía que representa las relaciones "es-un-tipo-de". Por ejemplo, la clase Empleado podría heredar de la clase Persona. Todos los datos y métodos disponibles para la clase principal también aparecen en la clase secundaria con los mismos nombres. Por ejemplo, la clase Person podría definir las variables "first_name" y "last_name" con el método "make_full_name()". Estos también estarán disponibles en la clase Empleado, que podría agregar las variables "cargo" y "salario". Esta técnica permite una fácil reutilización de los mismos procedimientos y definiciones de datos, además de reflejar potencialmente las relaciones del mundo real de una manera intuitiva. En lugar de utilizar tablas de bases de datos y subrutinas de programación,
Las subclases pueden anular los métodos definidos por las superclases. La herencia múltiple está permitida en algunos idiomas, aunque esto puede complicar la resolución de anulaciones. Algunos lenguajes tienen soporte especial para mixins, aunque en cualquier lenguaje con herencia múltiple, un mixin es simplemente una clase que no representa una relación is-a-type-of. Los mixins generalmente se usan para agregar los mismos métodos a varias clases. Por ejemplo, la clase UnicodeConversionMixin podría proporcionar un método unicode_to_ascii() cuando se incluye en la clase FileReader y la clase WebPageScraper, que no comparten un padre común.
Las clases abstractas no se pueden instanciar en objetos; existen solo con el propósito de heredar en otras clases "concretas" que pueden ser instanciadas. En Java, la final
palabra clave se puede usar para evitar que una clase se convierta en subclase.
La doctrina de la composición sobre la herencia aboga por la implementación de relaciones tiene un uso de la composición en lugar de la herencia. Por ejemplo, en lugar de heredar de la clase Persona, la clase Empleado podría dar a cada objeto Empleado un objeto Persona interno, que luego tiene la oportunidad de ocultar del código externo incluso si la clase Persona tiene muchos atributos o métodos públicos. Algunos idiomas, como Go, no admiten la herencia en absoluto.
El "principio abierto/cerrado" defiende que las clases y funciones "deben estar abiertas a la extensión, pero cerradas a la modificación".
La delegación es otra característica del lenguaje que se puede utilizar como alternativa a la herencia.
Polimorfismo
La subtipificación, una forma de polimorfismo, es cuando el código de llamada puede ser independiente de la clase en la jerarquía admitida en la que está operando: la clase principal o uno de sus descendientes. Mientras tanto, el mismo nombre de operación entre objetos en una jerarquía de herencia puede comportarse de manera diferente.
Por ejemplo, los objetos de tipo Circle y Square se derivan de una clase común denominada Shape. La función Dibujar para cada tipo de Forma implementa lo que es necesario para dibujarse, mientras que el código de llamada puede permanecer indiferente al tipo particular de Forma que se está dibujando.
Este es otro tipo de abstracción que simplifica el código externo a la jerarquía de clases y permite una fuerte separación de preocupaciones.
Recursividad abierta
En los lenguajes que admiten la recursividad abierta, los métodos de objeto pueden llamar a otros métodos en el mismo objeto (incluidos ellos mismos), normalmente usando una variable especial o una palabra clave llamada this
o self
. Esta variable está enlazada en tiempo de ejecución; permite que un método definido en una clase invoque otro método que se define posteriormente, en alguna subclase de la misma.
Lenguajes orientados a objetos
Simula (1967) es generalmente aceptado como el primer lenguaje con las características principales de un lenguaje orientado a objetos. Fue creado para hacer programas de simulación, en los que lo que se denominó objetos eran la representación de información más importante. Smalltalk (1972 a 1980) es otro ejemplo temprano, y con el que se desarrolló gran parte de la teoría de la programación orientada a objetos. Con respecto al grado de orientación a objetos, se pueden hacer las siguientes distinciones:
- Los lenguajes se denominan lenguajes OO "puros", porque todo en ellos se trata de manera consistente como un objeto, desde elementos primitivos como caracteres y puntuación, hasta clases completas, prototipos, bloques, módulos, etc. Fueron diseñados específicamente para facilitar, incluso hacer cumplir los métodos OO. Ejemplos: Ruby, Scala, Smalltalk, Eiffel, Emerald, JADE, Self, Raku.
- Lenguajes diseñados principalmente para programación OO, pero con algunos elementos de procedimiento. Ejemplos: Java, Python, C++, C#, Delphi/Object Pascal, VB.NET.
- Lenguajes que históricamente son lenguajes de procedimiento, pero que se han ampliado con algunas características OO. Ejemplos: PHP, Perl, Visual Basic (derivado de BASIC), MATLAB, COBOL 2002, Fortran 2003, ABAP, Ada 95, Pascal.
- Lenguajes con la mayoría de las características de los objetos (clases, métodos, herencia), pero en una forma claramente original. Ejemplos: Oberon (Oberon-1 u Oberon-2).
- Lenguajes con soporte de tipos de datos abstractos que pueden usarse para parecerse a la programación OO, pero sin todas las características de la orientación a objetos. Esto incluye lenguajes basados en objetos y basados en prototipos. Ejemplos: JavaScript, Lua, Modula-2, CLU.
- Lenguajes camaleónicos que admiten múltiples paradigmas, incluido OO. Tcl se destaca entre estos por TclOO, un sistema de objetos híbrido que soporta tanto la programación basada en prototipos como la programación orientada a objetos basada en clases.
POO en lenguajes dinámicos
En los últimos años, la programación orientada a objetos se ha vuelto especialmente popular en los lenguajes de programación dinámicos. Python, PowerShell, Ruby y Groovy son lenguajes dinámicos basados en principios de OOP, mientras que Perl y PHP han estado agregando funciones orientadas a objetos desde Perl 5 y PHP 4, y ColdFusion desde la versión 6.
El modelo de objeto de documento de documentos HTML, XHTML y XML en Internet tiene vínculos con el popular lenguaje JavaScript/ECMAScript. JavaScript es quizás el lenguaje de programación basado en prototipos más conocido, que emplea la clonación de prototipos en lugar de heredar de una clase (en contraste con la programación basada en clases). Otro lenguaje de secuencias de comandos que adopta este enfoque es Lua.
OOP en un protocolo de red
Los mensajes que fluyen entre computadoras para solicitar servicios en un entorno cliente-servidor pueden diseñarse como linealizaciones de objetos definidos por objetos de clase conocidos tanto por el cliente como por el servidor. Por ejemplo, un objeto linealizado simple constaría de un campo de longitud, un punto de código que identifica la clase y un valor de datos. Un ejemplo más complejo sería un comando que consiste en la longitud y el punto de código del comando y valores que consisten en objetos linealizados que representan los parámetros del comando. Cada uno de estos comandos debe ser dirigido por el servidor a un objeto cuya clase (o superclase) reconozca el comando y pueda proporcionar el servicio solicitado. Los clientes y servidores se modelan mejor como estructuras complejas orientadas a objetos.
- Campos que definen los valores de datos que forman mensajes, como su longitud, punto de código y valores de datos.
- Objetos y colecciones de objetos similares a los que se encontrarían en un programa de Smalltalk para mensajes y parámetros.
- Administradores similares a IBM i Objects, como un directorio para archivos y archivos que consisten en metadatos y registros. Los administradores proporcionan conceptualmente memoria y recursos de procesamiento para sus objetos contenidos.
- Un cliente o servidor que consta de todos los administradores necesarios para implementar un entorno de procesamiento completo, que admite aspectos tales como servicios de directorio, seguridad y control de concurrencia.
La versión inicial de DDM definía los servicios de archivos distribuidos. Más tarde se amplió para ser la base de la arquitectura de base de datos relacional distribuida (DRDA).
Patrones de diseño
Los desafíos del diseño orientado a objetos se abordan mediante varios enfoques. El más común se conoce como los patrones de diseño codificados por Gamma et al. . En términos más generales, el término "patrones de diseño" se puede utilizar para referirse a cualquier patrón de solución general y repetible para un problema común en el diseño de software. Algunos de estos problemas comunes tienen implicaciones y soluciones particulares para el desarrollo orientado a objetos.
Herencia y subtipificación conductual
Es intuitivo suponer que la herencia crea una relación semántica "es un" y, por lo tanto, inferir que los objetos instanciados desde subclases siempre se pueden usar de manera segura en lugar de aquellos instanciados desde la superclase. Lamentablemente, esta intuición es falsa en la mayoría de los lenguajes OOP, en particular en todos aquellos que permiten objetos mutables. El polimorfismo de subtipo tal como lo aplica el verificador de tipos en lenguajes OOP (con objetos mutables) no puede garantizar subtipos de comportamiento en ningún contexto. La subtipificación de comportamiento es indecidible en general, por lo que no puede ser implementada por un programa (compilador). Las jerarquías de clases u objetos deben diseñarse cuidadosamente, considerando posibles usos incorrectos que no pueden detectarse sintácticamente. Este problema se conoce como el principio de sustitución de Liskov.
Patrones de diseño de la banda de los cuatro
Design Patterns: Elements of Reusable Object-Oriented Software es un influyente libro publicado en 1994 por Erich Gamma, Richard Helm, Ralph Johnson y John Vlissides, al que a menudo se hace referencia con humor como la "pandilla de los cuatro". Además de explorar las capacidades y las trampas de la programación orientada a objetos, describe 23 problemas de programación comunes y patrones para resolverlos. A partir de abril de 2007, el libro se encontraba en su edición número 36.
El libro describe los siguientes patrones:
- Patrones de creación (5): patrón de método de fábrica, patrón de fábrica abstracto, patrón Singleton, patrón de constructor, patrón de prototipo
- Patrones estructurales (7): patrón de adaptador, patrón de puente, patrón compuesto, patrón de decorador, patrón de fachada, patrón de peso ligero, patrón de proxy
- Patrones de comportamiento (11): patrón de cadena de responsabilidad, patrón de comando, patrón de intérprete, patrón de iterador, patrón de mediador, patrón de recuerdo, patrón de observador, patrón de estado, patrón de estrategia, patrón de método de plantilla, patrón de visitante
Orientación a objetos y bases de datos
Tanto la programación orientada a objetos como los sistemas de administración de bases de datos relacionales (RDBMS) son extremadamente comunes en el software actual. Dado que las bases de datos relacionales no almacenan objetos directamente (aunque algunos RDBMS tienen características orientadas a objetos para aproximarse a esto), existe una necesidad general de unir los dos mundos. El problema de vincular accesos de programación orientada a objetos y patrones de datos con bases de datos relacionales se conoce como desajuste de impedancia relacional de objetos. Hay una serie de enfoques para hacer frente a este problema, pero no hay una solución general sin inconvenientes. Uno de los enfoques más comunes es el mapeo relacional de objetos, como se encuentra en lenguajes IDE como Visual FoxPro y bibliotecas como Java Data Objects y Ruby on Rails' ActiveRecord.
También hay bases de datos de objetos que se pueden usar para reemplazar los RDBMS, pero no han tenido tanto éxito técnico ni comercial como los RDBMS.
Modelado y relaciones del mundo real
OOP se puede utilizar para asociar objetos y procesos del mundo real con contrapartes digitales. Sin embargo, no todos están de acuerdo en que la programación orientada a objetos facilita el mapeo directo del mundo real (consulte la sección Críticas) o que el mapeo del mundo real es incluso un objetivo digno; Bertrand Meyer argumenta en Object-Oriented Software Construction que un programa no es un modelo del mundo sino un modelo de alguna parte del mundo; "La realidad es prima dos veces quitada". Al mismo tiempo, se han señalado algunas limitaciones principales de OOP. Por ejemplo, el problema de círculo-elipse es difícil de manejar utilizando el concepto de herencia de OOP.
Sin embargo, Niklaus Wirth (quien popularizó el adagio ahora conocido como la ley de Wirth: "El software se está volviendo más lento más rápidamente que el hardware se vuelve más rápido") dijo sobre la programación orientada a objetos en su artículo, "Buenas ideas a través del espejo", "Este paradigma refleja fielmente el estructura de los sistemas 'en el mundo real' y, por lo tanto, es muy adecuado para modelar sistemas complejos con comportamientos complejos" (principio KISS en contraste).
Steve Yegge y otros notaron que los lenguajes naturales carecen del enfoque OOP de priorizar estrictamente las cosas (objetos/sustantivos) antes que las acciones (métodos/verbos). Este problema puede hacer que la programación orientada a objetos sufra soluciones más complicadas que la programación procedimental.
OOP y flujo de control
OOP fue desarrollado para aumentar la reutilización y mantenibilidad del código fuente. La representación transparente del flujo de control no tenía prioridad y estaba destinada a ser manejada por un compilador. Con la creciente relevancia del hardware paralelo y la codificación de subprocesos múltiples, el desarrollo de un flujo de control transparente se vuelve más importante, algo difícil de lograr con OOP.
Diseño basado en la responsabilidad frente al diseño basado en datos
El diseño basado en la responsabilidad define las clases en términos de un contrato, es decir, una clase debe definirse en torno a una responsabilidad y la información que comparte. Wirfs-Brock y Wilkerson contrastan esto con el diseño basado en datos, donde las clases se definen en torno a las estructuras de datos que deben mantenerse. Los autores sostienen que es preferible el diseño impulsado por la responsabilidad.
Directrices SOLID y GRASP
SOLID es un mnemotécnico inventado por Michael Feathers que explica cinco principios de diseño de ingeniería de software:
- Principio de responsabilidad única
- Principio abierto/cerrado
- Principio de sustitución de Liskov
- Principio de segregación de interfaz
- Principio de inversión de dependencia
GRASP (Patrones de software de asignación de responsabilidad general) es otro conjunto de pautas defendidas por Craig Larman.
Crítica
El paradigma OOP ha sido criticado por una serie de razones, entre ellas, no cumplir con sus objetivos declarados de reutilización y modularidad, y por enfatizar demasiado un aspecto del diseño y modelado de software (datos/objetos) a expensas de otros aspectos importantes (computación/algoritmos)..
Luca Cardelli ha afirmado que el código OOP es "intrínsecamente menos eficiente" que el código de procedimiento, que la programación orientada a objetos puede tardar más en compilarse y que los lenguajes orientados a objetos tienen "propiedades de modularidad extremadamente pobres con respecto a la extensión y modificación de clases", y tienden a ser extremadamente complejos.. El último punto es reiterado por Joe Armstrong, el principal inventor de Erlang, quien es citado diciendo:
El problema con los lenguajes orientados a objetos es que tienen todo este entorno implícito que llevan consigo. Querías una banana pero lo que obtuviste fue un gorila sosteniendo la banana y toda la jungla.
Un estudio de Potok et al. no ha mostrado ninguna diferencia significativa en la productividad entre OOP y los enfoques procedimentales.
Christopher J. Date afirmó que la comparación crítica de OOP con otras tecnologías, relacionales en particular, es difícil debido a la falta de una definición rigurosa y acordada de OOP; sin embargo, Date y Darwen han propuesto una base teórica sobre programación orientada a objetos que utiliza programación orientada a objetos como una especie de sistema de tipo personalizable para admitir RDBMS.
En un artículo, Lawrence Krubner afirmó que, en comparación con otros lenguajes (dialectos LISP, lenguajes funcionales, etc.), los lenguajes OOP no tienen fortalezas únicas e infligen una gran carga de complejidad innecesaria.
Alexander Stepanov compara la orientación a objetos desfavorablemente con la programación genérica:
Encuentro OOP técnicamente poco sólido. Intenta descomponer el mundo en términos de interfaces que varían en un solo tipo. Para lidiar con los problemas reales, necesita álgebras multiordenadas: familias de interfaces que abarcan múltiples tipos. Encuentro OOP filosóficamente erróneo. Afirma que todo es un objeto. Incluso si es cierto, no es muy interesante: decir que todo es un objeto es no decir nada en absoluto.
Paul Graham ha sugerido que la popularidad de OOP dentro de las grandes empresas se debe a "grupos grandes (y que cambian con frecuencia) de programadores mediocres". Según Graham, la disciplina impuesta por OOP evita que cualquier programador "haga demasiado daño".
Leo Brodie ha sugerido una conexión entre la naturaleza independiente de los objetos y la tendencia a duplicar el código en violación del principio de desarrollo de software de no repetirse.
Steve Yegge señaló que, a diferencia de la programación funcional:
La Programación Orientada a Objetos pone los Sustantivos en primer lugar. ¿Por qué haría todo lo posible para poner una parte del discurso en un pedestal? ¿Por qué un tipo de concepto debe prevalecer sobre otro? No es que la programación orientada a objetos de repente haya hecho que los verbos sean menos importantes en la forma en que realmente pensamos. Es una perspectiva extrañamente sesgada.
Rich Hickey, creador de Clojure, describió los sistemas de objetos como modelos demasiado simplistas del mundo real. Hizo hincapié en la incapacidad de OOP para modelar el tiempo correctamente, lo que se está volviendo cada vez más problemático a medida que los sistemas de software se vuelven más concurrentes.
Eric S. Raymond, programador de Unix y defensor del software de código abierto, ha criticado las afirmaciones que presentan la programación orientada a objetos como la "única solución verdadera" y ha escrito que los lenguajes de programación orientados a objetos tienden a fomentar programas con capas gruesas que destruir la transparencia. Raymond compara esto desfavorablemente con el enfoque adoptado con Unix y el lenguaje de programación C.
Rob Pike, un programador involucrado en la creación de UTF-8 y Go, ha llamado a la programación orientada a objetos "los números romanos de la computación" y ha dicho que los lenguajes OOP con frecuencia cambian el enfoque de las estructuras de datos y los algoritmos a los tipos. Además, cita un ejemplo de un profesor de Java cuya solución "idiomática" a un problema fue crear seis clases nuevas, en lugar de simplemente usar una tabla de búsqueda.
Semántica formal
Los objetos son las entidades de tiempo de ejecución en un sistema orientado a objetos. Pueden representar a una persona, un lugar, una cuenta bancaria, una tabla de datos o cualquier elemento que el programa tenga que manejar.
Ha habido varios intentos de formalizar los conceptos utilizados en la programación orientada a objetos. Los siguientes conceptos y construcciones se han utilizado como interpretaciones de los conceptos de programación orientada a objetos:
- tipos de datos coalgebraicos
- tipos recursivos
- estado encapsulado
- herencia
- los registros son la base para comprender los objetos si los literales de función se pueden almacenar en campos (como en los lenguajes de programación funcional), pero los cálculos reales deben ser considerablemente más complejos para incorporar características esenciales de OOP. Se han estudiado varias extensiones de System F <: que tratan con objetos mutables; estos permiten polimorfismo de subtipo y polimorfismo paramétrico (genéricos)
Los intentos de encontrar una definición de consenso o una teoría detrás de los objetos no han tenido mucho éxito (sin embargo, consulte Abadi & Cardelli, A Theory of Objects para definiciones formales de muchos conceptos y construcciones de programación orientada a objetos) y, a menudo, divergen ampliamente. Por ejemplo, algunas definiciones se centran en las actividades mentales y otras en la estructuración de programas. Una de las definiciones más simples es que OOP es el acto de usar estructuras de datos de "mapa" o matrices que pueden contener funciones y punteros a otros mapas, todo con algo de azúcar sintáctico y de alcance en la parte superior. La herencia se puede realizar clonando los mapas (a veces llamado "creación de prototipos").
Contenido relacionado
Código muerto
Colección de compiladores GNU
Entorno de escritorio