Pregunta

¿Crees que la programación orientada a objetos es una solución a la complejidad? ¿Por qué? ¡Este tema puede ser un poco controvertido, pero mis intenciones de saber la respuesta de por qué de los expertos aquí!

¿Fue útil?

Solución

No hay solución a la complejidad.

En "The Mythical Man-Month", Fred Brooks analiza la diferencia entre la complejidad accidental y esencial en la programación. La complejidad accidental es causada por nuestras herramientas y métodos, como tener que escribir y probar código adicional en un idioma porque no podemos expresar nuestras ideas directamente, y cosas así. Los nuevos métodos y técnicas pueden reducir la complejidad accidental. Puedo escribir programas más rápido y mejor de lo que pude hace veinticinco años, porque tengo mejores idiomas y herramientas.

La complejidad esencial proviene del hecho de que lo que intentamos hacer con la programación es inherentemente complicada y que existe una complejidad irreductible. "Esencial", en este contexto, significa "relacionado con la esencia de la cosa" en lugar de "muy necesaria".

Por lo tanto, afirmó que no habría una bala de plata, que el software de escritura continuaría siendo difícil.

Recomiendo encarecidamente que lea su libro: específicamente, recomiendo la Edición de Aniversario Silver, con un ensayo adicional "No Bullet Silver". En eso, revisa las soluciones propuestas a la complejidad y considera su impacto. (Lo que encuentra más efectivo es el software de envoltura contraria: escriba algo complejo una vez y venda miles o millones de copias).

Ahora, la programación orientada a objetos ayuda, cuando se hace bien, creando abstracciones y escondiendo la complejidad. Un objeto de una clase tiene un cierto comportamiento definido del que podemos razonar, sin preocuparnos por la complejidad de la implementación. Las clases escritas correctamente tienen un bajo acoplamiento entre sí, y Divide-and-Conquer es una excelente manera de lidiar con la complejidad si puedes salirse con la suya. También tienen una alta cohesión, ya que son un conjunto de funciones y datos que se relacionan muy estrechamente entre sí.

Otros consejos

Espero que obtenga algunas respuestas mejores en breve, pero aquí hay una simple:

OOP ayuda* con la complejidad modelando el software de una manera más cercana a la forma en que modelamos todo lo demás en el mundo. En general, es más fácil imaginar un objeto de pelota que interactúa con un objeto de pared que imaginar una serie de rutinas y estructuras de datos para hacer lo mismo, ya que está más cerca de cómo interactuamos con el mundo real.

*Porque nada puede 'resolver' la complejidad

Creo que la definición actual de OOP no es una buena solución para manejar la complejidad.

Si vuelves a sus raíces, creo que Alan Kay fue influenciado por "Lisp" mucho.

Debido a que LISP no ha sido corrompido por la adopción convencional, probablemente logró conservar sus valores centrales. Así que creo que mirar cómo Lisp aborda este problema de complejidad podría darnos una idea, y podemos usarlo como base para juzgar cuán útil es OOP para tratar con la complejidad.

Si observa el final de la "Conferencia 3A: Ejemplo de Henderson Escher" de SICP, Hal Abelson propone que la complejidad se maneja no dividiendo la tarea en subtareas más pequeñas, sino creando capas de abstracción. En el nivel más alto, expresa la solución al complicado problema en términos de la solución al nivel inferior de abstracción.

Creo que OOP se pretendía originalmente como un mecanismo para crear estas capas de abstracciones.

Desafortunadamente, hoy en día, OOP se usa (AB) para escribir código/estructuras de espagueti.

Haré un ejemplo: un juego multijugador de FPS.

Al más alto nivel, el juego funciona al tener un montón de jugadores corriendo alrededor de un mapa y dispararse entre sí usando armas.

En el siguiente nivel inferior, tenemos que hablar sobre mapas, armas y jugadores. Quizás podamos hablar de ellos como objetos físicos que interactúan dentro del mundo del juego.

En el siguiente nivel inferior, podemos hablar sobre cómo los objetos interactúan físicamente (movimiento, colisiones, etc.).

Y así sucesivamente y así sucesivamente.

Lo que esto significa, (y estoy citando de SICP ...), es que en cada capa, no solo resolvemos un problema específico particular, sino una clase de problemas que caen en el vecindario del problema nosotros '' Estoy tratando de resolver. Entonces, si hay un pequeño cambio en la descripción del problema, es probable que solo requeriría un pequeño cambio en la solución.

Por lo tanto, la forma sabia de usar OOP es crear capas de abstracciones, en cada nivel de abstracción, resuelve el problema en cuestión utilizando los "objetos" desde el nivel que está directamente a continuación.

Aquí está el bit que estaba citando de la conferencia: http://www.youtube.com/watch?v=Cytrfncmqhq

Como de costumbre, no estoy de acuerdo con todos. Lejos de brindarle herramientas para manejar la complejidad, OOP crea una gran cantidad de complejidad porque es un paradigma inadecuado y matemáticamente falso. No confunde a los programadores sin fin, para tratar de modelar cosas con OOP que no se pueden modelar con OOP.

En mi opinión, el trabajo seminal aquí está la construcción de software orientado a objetos de Meyer. Detalla un conjunto de requisitos, incluido uno que considero crucial: el principio cerrado. Esto dice que una cosa debe estar abierta para la extensión pero cerrada para su uso, al mismo tiempo.

Meyer procede a derivar la orientación de objetos de estos requisitos, como se encarna en Eiffel. La encapsulación proporciona cierre, herencia abierta y la "cosa" mencionada es la clase.

Considero este trabajo como una buena ciencia porque Meyer estaba demostrablemente equivocado, y es posible debido a la calidad de su trabajo, apuntar el error y arreglarlo.

El error está haciendo que la clase, o tipo, la unidad de modularidad. Eso está mal, y probablemente así. Incluso Meyer reconoció el problema (llamado problema de covarianza), que OOP no puede manejar las relaciones de aridad superiores a una (es decir, OOP funciona bien para las propiedades pero falla en las relaciones binarias). ¡En Eiffel, este problema resultó en un sistema de tipo no sólido en el tipo!

La solución es bastante clara. La unidad de modularidad debe ser más grande que un solo tipo. Debe consistir en varios tipos y los métodos que los relacionan.

No es sorprendente que este modelo de abstracción esté respaldado por la teoría matemática de la abstracción, a saber, la teoría de la categoría: los tipos son objetos de una categoría y los métodos (funciones) son flechas.

Con este modelo, las representaciones de varios Los tipos son conocidos por un conjunto de funciones. La representación está oculta al público, por lo que esta es la encapulación, pero usamos módulos, no clases.

El meta-idioma estándar (SML) y OCAML se basan directamente en este modelo. OCAML también tiene clases y OOP: no es inútil porque OOP le da envío de propiedades, también conocida como vinculación dinámica. Sin embargo, la mayoría de los problemas del mundo real involucran relaciones y no es sorprendente que las clases no se usen mucho en OCAML.

No es sorprendente la herencia apenas se usa en la biblioteca de plantillas estándar C ++.

El simple hecho es que OOP no le brinda las herramientas adecuadas para manejar la complejidad, ni siquiera le da las herramientas para manejar problemas realmente simples, sino que ha engañado y confundido dos generaciones de programadores. Lejos de ayudar, OOP es lo más malvado y malo que ha sucedido a la programación desde que C, Fortran y Cobol comenzaron a cansarse.

La programación orientada a objetos tiene raíces que se pueden rastrear hasta la década de 1960. A medida que el hardware y el software se volvieron cada vez más complejos, la capacidad de administración a menudo se convirtió en una preocupación. Los investigadores estudiaron formas de mantener la calidad del software y desarrollaron una programación orientada a objetos en parte para abordar los problemas comunes enfatizando fuertemente Unidades discretas y reutilizables de lógica de programación.

Por lo tanto, un programa orientado a objetos puede verse como una colección de objetos interactivos, en oposición al modelo convencional, en el que un programa se considera una lista de tareas (subrutinas) para realizar. En OOP, cada objeto es capaz de recibir mensajes, procesar datos y enviar mensajes a otros objetos. Cada objeto puede verse como una 'máquina' independiente con un papel o responsabilidad distinto. Las acciones (o "métodos") en estos objetos son estrechamente asociado con el objeto en sí.

http://en.wikipedia.org/wiki/object-oriented_programming

Esta separación de las preocupaciones, junto con otras características de la orientación de objetos, como el polimorfismo, la herencia, el paso de mensajes, el desacoplamiento y la encapsulación, proporcionan un marco lógico y conceptual por el cual la complejidad de los grandes programas se puede gestionar de una manera muy efectiva.

Hay muchos tipos de complejidad para el desarrollo de software. En el nivel de programación, OOP busca abordar la complejidad utilizando objetos y clases para modelar el dominio del problema. Un gurú bien conocido dijo que la resolución de problemas solo representa el problema para que la solución sea la representación en sí. Por lo tanto, por abstracción utilizando clases, encapsulación utilizando modificadores y métodos de acceso, la herencia para especificar la relación y la reutilización, la composición en el establecimiento de la relación y la colaboración entre las clases, el polimorfismo como un medio para simplificar la determinación de diferentes comportamientos en objetos similares, se puede manejar la complejidad.

También hay otras formas de administrar la complejidad del software, por ejemplo, la programación Logic (Prolog) y funcional (Haskell).

En un nivel más alto que la programación, necesitamos patrones de diseño y principios para guiar OOP. Por lo tanto, OOP está gestionando la complejidad a un nivel bajo (codificación), mientras que estas metodologías, como los patrones de diseño y los principios, guían el diseño de la solución a un nivel superior (sistema y aplicación) y hacen que el desarrollo de software y el manejo de complejidades sean más manejables.

Para responder a su pregunta, sí, OOP es solo una solución para manejar la complejidad entre muchas otras soluciones. Es una solución a un nivel bajo. Necesitamos patrones de diseño y principios para guiar a OOP en un nivel superior.

Programación orientada a objetos manejar Complejidad esencial y opcional, pero tampoco reduce.

Prefiero la definición proporcionada por Eric Steven Raymond en El arte de la programación de Unix, porque delinea entre complejidad esencial, opcional y accidental.http://www.faqs.org/docs/artu/ch13s01.html#id2964646

OOP no hace nada para la complejidad esencial u opcional, son una función de los requisitos del programa. Puede tener un efecto en la complejidad accidental, ya que puede crear un diseño más elegante a veces con OOP. A veces, sin embargo, el diseño es peor cuando se usa OOP.

Los problemas complejos no se pueden simplificar a través de la tecnología, solo pueden ser administrado a través de la tecnología.

OOP es una tecnología, un concepto y una forma de abordar un problema.

OOP le brinda las herramientas para hacer cumplir un diseño Eso puede hacer que sea más fácil manejar la complejidad, pero puede tener un mal diseño que aumente su complejidad. En otras palabras, si no se usa correctamente, puede tener una complejidad inducida por la tecnología en sus problemas.

Tenga en cuenta que hay muchos otros aspectos que dictarán cuán exitoso será su proyecto (es decir, estilo de gestión de proyectos, definición de problemas, gestión de cambios, etc.). La tecnología que usa solo es relevante en cuánto te ayudará. administrar el problema.

Al final, La programación orientada a objetos no puede ser un solución a la complejidad; Es solo una herramienta para administrarla. (si se usa correctamente)

La orientación de objetos (como se usa convencionalmente) es una herramienta útil en muchas circunstancias, pero no es una solución suficiente para la complejidad.

En particular, a menudo agrega mucho "complejidad incidental". Los ejemplos son la complejidad que rodea la herencia de implementación, la necesidad de proporcionar mucha" funcionalidad estándar "Suach como igual () y hashcode (), etc. Una buena presentación de Stuart Halloway sobre este tema:"La simplicidad no es fácil"

Los objetos en la mayoría de los idiomas también tienden a encapsular mucho estado mutable - que en un mundo concurrente está comenzando a parecer una mala decisión de diseño. De nuevo un interesante Video de Rich Hickey Mira la distinción entre identidad de objetos y estado, y cómo podría ser un error combinar los dos.

La programación orientada a objetos es una forma de representar un problema, nada más, nada menos. Es, en sí mismo, no menos complejo que cualquier otro paradigma de programación. Un sistema OOP bien diseñado maneja y reduce la complejidad, pero también es muy fácil diseñar un sistema que sea mucho más complejo de lo necesario y se interponga en el camino de todo.

Como a menudo dijo de C ++, OOP te da suficiente cuerda para colgarte.

pienso , solo porque le permite cortar la complejidad en pequeños "bloques de construcción" autónomos que ocultan los detalles, y luego usarlos para crear la funcionalidad que necesita, paso a paso, capa por capa.

Divide y conquistaras.

OOP es un intento de una solución.

La mejor manera de manejar la complejidad es crear abstracciones. Si puedo convertir mis datos en colecciones útiles, con funciones reconocibles que operan en esas colecciones, puedo comenzar a pensar en las colecciones como "cosas" discretas. Esa es la base de clases y métodos. En ese sentido, OOP adecuadamente diseñado puede ayudar a gestionar la complejidad.

En algún momento, alguien decidió que podríamos usar OOP para ayudar a resolver el problema de la reutilización de código. Quiero decir, ¿por qué reinventar la rueda? Si alguien más ha realizado gran parte del trabajo para resolver este problema, aproveche lo que ha hecho, agregue los ajustes que su proyecto en particular requiere y ¡listo! Ha creado una aplicación poderosa y sofisticada con relativamente poco trabajo de su parte. Los programadores de OO pueden ser programadores muy productivos.

El resultado final es que los programadores modernos de OO terminan siendo "aprendices de hechicero", donde unen un montón de bibliotecas grandes y difíciles de manejar con algunas líneas de "pegamento" y obtienen algo que funciona. Amontonado. Un tanto. La mayoría de las veces. ¿Existen posibles efectos secundarios al usar esta biblioteca con esa? Quizás. Pero, ¿quién tiene tiempo para profundizar en el código contenido en esas bibliotecas? Especialmente cuando las bibliotecas están evolucionando. El resultado es que terminamos con aplicaciones hinchadas, donde un programador necesitaba un puñado de clases y métodos de esa biblioteca, pero la aplicación tiene que llevar el peso de todas las otras cosas que no necesitaban.

El resultado final es que terminas con mucha más complejidad de la que necesitas.

Otro mecanismo para lidiar con la complejidad que desea separar la funcionalidad. Desea todas sus funciones de acceso a datos en un solo lugar. Desea toda su funcionalidad de interfaz de usuario en un solo lugar. Desea todos sus controladores en un solo lugar. Entonces crea diferentes clases que administran diferentes partes de la funcionalidad. Hasta aquí todo bien. Y esto escala, hasta cierto punto; Sus desarrolladores que están hábiles con acceso a datos pueden escribir esas clases, las personas de interfaz de usuario pueden escribir las clases de interfaz de usuario, etc. Todo está bien.

Hasta que tenga que mantener algo escrito por otra persona.

Sí, es bueno saber que todas las funciones de acceso a datos se encuentran aquí. Pero, ¿qué los llama?

Este método está llamando a ese método en esa clase. Pero cuando miro la definición de clase, no hay ningún método con ese nombre. Oh, eso se hereda de algo más una o dos capas en la cadena de herencia. Espera un minuto; ¿Esa clase implementó una interfaz? ¿Cuántas clases diferentes implementan esa interfaz? ¿Y estamos utilizando un sistema complicado de tiempo de ejecución (te estoy mirando, resorte) para "conectar" instancias de clases en tiempo de ejecución? ¿Dónde se puede usar cualquier clase que implementa esa interfaz?

Terminas con muchos métodos pequeños y discretos que hacen cosas precisas. Pero este lo llama a eso, en otra clase. Lo que llama a eso, en otra clase más. Lo que llama a eso, en otra clase más. Lo que llama a eso, en una clase adicional. Que devuelve el resultado de un tipo particular. En el que debes llamar a un método para hacer algo. Que devuelve el resultado de otro tipo. Etc.

Hay un término para esto: código de espagueti.

Termina con un sistema muy complejo necesario solo para componer el código. Por lo tanto, ides como Visual Studio, Eclipse y NetBeans. Todos los cuales tienen una curva de aprendizaje significativa. De hecho, muchos de ellos pueden encapsular/agregar múltiples herramientas, desarrolladas por diferentes grupos, cada una de las cuales tiene sus propias curvas de aprendizaje.

¿Esto es gestionar la complejidad?

El código de depuración es el doble de duro que escribirlo. Buena suerte depurando algunas de estas cosas. Especialmente si está usando múltiples bibliotecas, "conectado juntos" en tiempo de ejecución utilizando algún tipo de sistema de inyección de dependencia.

En resumen: OOP proporciona lo que parece una herramienta prometedora para ayudar a administrar la complejidad. La realidad es que el código resultante tiende a estar terriblemente hinchado (porque no puede extraer solo las piezas que necesita de todas esas bibliotecas vinculadas) y necesita herramientas sofisticadas solo para navegar el código. Rápidamente se convierte en una pesadilla de mantenimiento.

En mi humilde opinión, es una pérdida neta; Agrega más complejidad de la que elimina. Le permite hacer cosas que serían extremadamente difíciles, posiblemente incluso imposibles, sin ella. Pero cualquier proyecto grande evoluciona rápidamente hacia un desastre imperdible.

Si ya sabe cómo funciona y puede recordarlo, puede tener la oportunidad de mantenerlo.

Recuerde aplicar la ley de Eagleson: cualquier código propio, que no ha visto en seis meses, bien podría ser escrito por otra persona.

Hasta cierto grado...

¿Por qué? Porque facilita la modularidad muy lógica. Al menos en comparación con la programación de procedimientos, donde es demasiado tentador escribir enormes montones de código de espagueti.

La razón por la cual la programación orientada a objetos parece ayudarnos a manejar la complejidad es porque nos obliga a escribir código de manera específica en lugar de una gran variedad de formas. La programación orientada a tareas es mucho más intuitiva, por lo que la programación comenzó de esa manera. La orientación de objetos requiere capacitación y práctica para comprender y usar de manera efectiva, pero al restringir la programación en una cierta ruta, permite a los entrenados mantener efectivamente el código que se escribió.

No es más lógico o en el mundo real que cualquier otro método, es solo una forma de enfocar nuestra resolución de problemas a través de lentes similares. Muchas especialidades técnicas utilizan el paradigma de una metodología rígida no intuitiva para manejar la complejidad de sus tareas.

Un tercer método para manejar la complejidad sería la programación funcional, y probablemente también habrá otros métodos nuevos en el futuro.

Creo que es más una solución a la mantenibilidad, porque, como programador, se supone que debe poner métodos en los que tiene los datos, creando así un modelo de objeto de su aplicación.

Sí, también es una solución a la complejidad al proporcionar un modelo para que "vea" su código de manera natural, como objetos que tienen propiedades y posibles acciones

Licenciado bajo: CC-BY-SA con atribución
scroll top