He estado programando en lenguajes de procedimiento desde hace bastante tiempo, y mi primera reacción a un problema es comenzar a dividirlo en tareas para realizar en lugar de considerar las diferentes entidades (objetos) que existen y sus relaciones.
He tenido un curso universitario en OOP y entiendo los fundamentos de la encapsulación, la abstracción de datos, el polimorfismo, la modularidad y la herencia.
Leí /programming/2688910/learning-to-think-in-the-object-oriented-way y /programming/1157847/learning-object-oriented-thinking , y miraré algunos de los libros señalados en esas respuestas.
Creo que varios de mis proyectos de tamaño medio a grande se beneficiarán del uso efectivo de OOP, pero como novato me gustaría evitar errores comunes que requieren mucho tiempo.
Según sus experiencias, ¿cuáles son estos obstáculos y cuáles son las formas razonables de evitarlos? Si pudiera explicar por qué son trampas y cómo su sugerencia es efectiva para abordar el problema, sería apreciado.
Estoy pensando en algo como "¿Es común tener una buena cantidad de métodos de observación y modificación y usar variables privadas o existen técnicas para consolidarlas / reducirlas?"
No me preocupa usar C ++ como un lenguaje OO puro, si hay buenas razones para mezclar métodos. (Recuerda las razones para usar GOTO, aunque con moderación).
¡Gracias!
fuente
Respuestas:
Una gran cosa que he aprendido es diseñar clases desde afuera hacia adentro. Diseñe la interfaz incluso antes de comenzar a pensar en la implementación. Esto hará que la clase sea mucho, mucho más intuitiva para sus usuarios (aquellos que usan la clase) que escribir los algoritmos subyacentes y desarrollar la clase, y escribir nuevas funciones de miembros públicos según sea necesario.
fuente
Bueno, el primero es el peligro de exponer demasiada información. El valor predeterminado debería ser
private
, nopublic
.Después de eso viene demasiados captadores / setters. Digamos que tengo un miembro de datos. ¿ Realmente necesito estos datos en otra clase? Ok, haz un captador. ¿ Realmente, realmente necesito cambiar estos datos durante la vida útil del objeto? Luego haz un setter.
La mayoría de los programadores novatos tienen el valor predeterminado para hacer un captador / configurador para cada miembro de datos. Esto satura las interfaces y a menudo son malas elecciones de diseño.
fuente
Cuando crucé ese abismo me decidí por el siguiente enfoque:
0) Comencé lento, con aplicaciones de procedimiento pequeñas / medianas y sin cosas de misión crítica en el trabajo.
1) un mapeo simple de primer paso de cómo escribiría el programa desde cero en el estilo OO, lo más importante para mí en ese momento, y esto ES subjetivo, fue descubrir todas las clases base. Mi objetivo era encapsular lo más posible en las clases base. Métodos virtuales puros para todo lo posible en las clases base.
2) Luego, el siguiente paso fue crear las derivaciones.
3) el paso final fue: en el código de procedimiento original, separe las estructuras de datos del código del observador / modificador. Luego, use la ocultación de datos y asigne todos los datos comunes a las clases base, y en las subclases fueron los datos que no eran comunes en todo el programa. Y el mismo tratamiento para el código de observador / modificador de procedimiento: toda la lógica 'utilizada en todas partes' entró en las clases base. Y la lógica de visualización / modificación que solo actuaba en un subconjunto de datos entró en las clases derivadas.
Esto es subjetivo pero es RÁPIDO, si conoce bien el código de procedimiento y las estructuras de datos. Un FALLO en una revisión de código es cuando un bit de datos o lógica no aparece en las clases base pero se usa en todas partes.
fuente
Eche un vistazo a otros proyectos exitosos que usan OOP para tener una sensación de buen estilo. Recomiendo mirar Qt , un proyecto que siempre admiro cuando tomo mis propias decisiones de diseño.
fuente
Dependiendo de con quién hable, todos los datos en OOP deben ser privados o protegidos, y solo estar disponibles a través de accesores y mutadores. En general, creo que esta es una buena práctica, pero hay ocasiones en las que me desvío de esta norma. Por ejemplo, si tiene una clase (en Java, digamos) cuyo único propósito es agrupar algunos datos en una unidad lógica, tiene sentido dejar los campos públicos. Si se trata de una cápsula inmutable, simplemente márquelas como finales e inicialícelas en el constructor. Esto reduce la clase (en este caso) a poco más que una estructura (de hecho, usando C ++, debería llamar a esto una estructura. Funciona igual que una clase, pero la visibilidad predeterminada es pública y su intención es más clara), pero Creo que encontrarás que usarlo es mucho más cómodo en estos casos.
Una cosa que definitivamente no desea hacer es tener un campo que debe verificarse para determinar la coherencia en el mutador, y dejarlo público.
fuente
struct
s: no hay diferencia semántica, pero aclara la intención y hace que su uso parezca más natural, especialmente para los programadores de C.El libro de Kent Beck, Implementation Patterns, es una excelente base sobre cómo usar, no usar mal, los mecanismos orientados a objetos.
fuente
No me preocupa usar C ++ como un lenguaje OO puro, si hay buenas razones para mezclar métodos. (Recuerda las razones para usar GOTO, aunque con moderación).
Realmente no pensé que tenía mucho que ofrecer la conversación hasta que vi esto. Tengo que estar en desacuerdo con el sentimiento. OOP es solo uno de los paradigmas que pueden y deben usarse en C ++. Francamente, en mi opinión, no es una de sus características más fuertes.
Desde el punto de vista de OO, creo que C ++ en realidad se queda un poco corto. La idea de tener funciones no virtuales, por ejemplo, es una marca en su contra a este respecto. He tenido discusiones con aquellos que no están de acuerdo conmigo, pero los miembros no virtuales simplemente no se ajustan al paradigma en lo que a mí respecta. El polimorfismo es un componente clave para OO y las clases con funciones no virtuales no son polimórficas en el sentido OO. Entonces, como lenguaje OO, creo que C ++ es bastante débil en comparación con lenguajes como Java u Objective-C.
Programación genérica por otro lado, C ++ tiene esta bastante buena. He oído decir que también hay mejores lenguajes para esto, pero la combinación de objetos y funciones genéricas es algo muy potente y expresivo. Además, puede ser muy rápido tanto en tiempo de programación como en tiempo de procesamiento. Es realmente en esta área que creo que C ++ brilla, aunque es cierto que podría ser mejor (soporte de lenguaje para conceptos, por ejemplo). Alguien que piensa que debería apegarse al paradigma OO y tratar a los demás en el orden de la declaración goto en niveles de inmoralidad realmente se está perdiendo al no mirar este paradigma.
La capacidad de metaprogramación de las plantillas también es bastante impresionante. Echa un vistazo a la biblioteca Boost.Units, por ejemplo. Esta biblioteca proporciona soporte de tipo para cantidades dimensionales. He utilizado ampliamente esta biblioteca en la empresa de ingeniería para la que trabajo actualmente. Simplemente proporciona una respuesta mucho más inmediata para un aspecto del posible programador, o incluso un error de especificación. Es imposible compilar un programa que use una fórmula en la que ambos lados del operador '=' no sean dimensionalmente equivalentes sin una conversión explícita. Personalmente no tengo experiencia con ningún otro lenguaje en el que esto sea posible, y ciertamente no con uno que también tenga el poder y la velocidad de C ++.
La metaprogramación es un paradigma puramente funcional.
Entonces, realmente, creo que ya estás entrando en C ++ con algunos conceptos erróneos desafortunados. Los otros paradigmas además de OO no deben ser evitados, deben ser APROVECHADOS. Use el paradigma que es natural para el aspecto del problema en el que está trabajando. No fuerce objetos sobre lo que esencialmente no es un problema propenso a objetos. En lo que a mí respecta, OO no es ni la mitad de la historia de C ++.
fuente
Quería aceptar una respuesta a esta pregunta, pero no podía decidir una respuesta para otorgarle la marca de verificación. Como tal, he votado a los autores originales y he creado esto como una respuesta resumida. Gracias a todos los que se tomaron unos minutos, descubrí que la información que me brindaron me dio una buena dirección y un poco de tranquilidad de que no me había salido de los rieles.
@nightcracker
Sentí que había observado este problema en acción en el pasado. Sus comentarios también me hicieron recordar que al ocultar las variables subyacentes y su implementación, soy libre de cambiar su implementación sin destruir nada que dependa de ellas.
Dominic Gurto
Pensé que el comentario de Dominic era un gran ideal al que aspirar, pero creo que el comentario de Gene realmente golpea la realidad de la situación. Hasta ahora he visto esto en acción ... y me siento un poco mejor de que no sea raro. Creo que a medida que madure como programador, me inclinaré hacia diseños más completos, pero en este momento todavía sufro por saltar y obtener algunos códigos escritos.
wantTheBest
Esto tiene mucho sentido ... Me gustó la idea de mantener las cosas funcionando en el trabajo, pero refactorizar algunas de las cosas no críticas con las clases.
jpm
Hace tiempo que sé que este es uno de los puntos fuertes de encapsular los datos ... poder imponer la coherencia y, en realidad, condiciones / rangos / etc.
Eddie loco
Originalmente me perdí mucho en la respuesta de Crazy Eddie, creo que porque no había leído sobre algunos de los temas mencionados ... como la metaprogramación. Creo que el gran mensaje en la publicación de CE fue que C ++ es una mezcla de capacidades y estilos que cada uno debe usar para su mejor potencial ... incluido el imperativo si eso es lo que tiene sentido.
Así que de nuevo, ¡gracias a todos los que respondieron!
fuente
El mayor escollo es la creencia de que OOP es una bala de plata, o el "paradigma perfecto".
fuente