Soy ingeniero eléctrico y no sé qué demonios estoy haciendo. Guarde los futuros mantenedores de mi código.
Recientemente he estado trabajando en una serie de programas más pequeños (en C #) cuya funcionalidad es lógicamente "procesal". Por ejemplo, uno de ellos es un programa que recopila información de diferentes bases de datos, usa esa información para generar una especie de página de resumen, la imprime y luego sale.
La lógica requerida para todo eso es alrededor de 2000 líneas. Ciertamente no quiero meter todo eso en un solo main()
y luego "limpiarlo" con #region
s como han estado haciendo algunos desarrolladores anteriores (estremecimiento).
Aquí hay algunas cosas que ya he probado sin mucha satisfacción:
Cree utilidades estáticas para cada bit grueso de funcionalidad, por ejemplo, DatabaseInfoGetter, SummaryPageGenerator y PrintUtility. Hacer que la función principal se vea así:
int main()
{
var thisThing = DatabaseInfoGetter.GetThis();
var thatThing = DatabaseInfoGetter.GetThat();
var summary = SummaryPageGenerator.GeneratePage(thisThing, thatThing);
PrintUtility.Print(summary);
}
Para un programa incluso fui con interfaces
int main()
{
/* pardon the psuedocode */
List<Function> toDoList = new List<Function>();
toDoList.Add(new DatabaseInfoGetter(serverUrl));
toDoList.Add(new SummaryPageGenerator());
toDoList.Add(new PrintUtility());
foreach (Function f in toDoList)
f.Do();
}
Nada de esto se siente bien. A medida que el código se alarga, ambos enfoques comienzan a ponerse feos.
¿Cuál es una buena manera de estructurar cosas como esta?
fuente
Respuestas:
Como usted no es un programador profesional, le recomendaría mantener la simplicidad. Será mucho más fácil para el programador tomar su código de procedimiento modularizado y hacer que sea OO más tarde, que será para ellos arreglar un programa OO mal escrito. Si no tienes experiencia, es posible crear programas de OO que pueden convertirse en un desastre impuro que no te ayudará a ti ni a quien te persiga.
Creo que su primer instinto, el código "esta cosa-esa cosa" en el primer ejemplo es el camino correcto. Es claro y obvio lo que quieres hacer. No se preocupe demasiado por la eficiencia del código, la claridad es mucho más importante.
Si un segmento de código es demasiado largo, divídalo en trozos pequeños, cada uno con su propia función. Si es demasiado corto, considere usar menos módulos y poner más en línea.
---- Postscript: trampas de diseño OO
Trabajar con éxito con la programación OO puede ser complicado. Incluso hay algunas personas que consideran que todo el modelo tiene fallas. Hay un muy buen libro que utilicé cuando aprendí por primera vez la programación OO llamado Pensamiento en Java (ahora en su cuarta edición). El mismo autor tiene un libro correspondiente para C ++. En realidad, hay otra pregunta sobre los programadores que se enfrentan a dificultades comunes en la programación orientada a objetos .
Algunas trampas son sofisticadas, pero hay muchas maneras de crear problemas de manera muy básica. Por ejemplo, hace un par de años hubo un pasante en mi empresa que escribió la primera versión de un software que heredé e hizo interfaces para todo lo que pudieraAlgún día tendrá múltiples implementaciones. Por supuesto, en el 98% de los casos solo hubo una única implementación, por lo que el código se cargó con interfaces no utilizadas, lo que hizo que la depuración fuera muy molesta porque no puede retroceder a través de una llamada de interfaz, por lo que termina teniendo que hacer un búsqueda de texto para la implementación (aunque ahora uso IntelliJ tenía la función "Mostrar todas las implementaciones", pero en el pasado no tenía eso). La regla aquí es la misma que para la programación de procedimientos: siempre codificar una cosa. Solo cuando tenga dos o más cosas, cree una abstracción.
Un tipo similar de error de diseño se puede encontrar en la API Java Swing. Utilizan un modelo de publicación-suscripción para el sistema de menús Swing. Esto hace que crear y depurar menús en Swing sea una pesadilla completa. La ironía es que es completamente inútil. Prácticamente nunca existe una situación en la que múltiples funciones necesiten "suscribirse" a un clic en el menú. Además, publicar-suscribir fue una falla total porque un sistema de menús normalmente siempre está en uso. No es que las funciones se suscriban y luego se den de baja al azar. El hecho de que los desarrolladores "profesionales" de Sun hayan cometido un error como este simplemente muestra lo fácil que es incluso para los profesionales cometer errores monumentales en el diseño OO.
Soy un desarrollador muy experto con décadas de experiencia en programación OO, pero incluso yo sería el primero en admitir que hay toneladas que no conozco e incluso ahora soy muy cauteloso sobre el uso de una gran cantidad de OO. Solía escuchar largas conferencias de un compañero de trabajo que era un fanático de OO sobre cómo hacer diseños particulares. Realmente sabía lo que estaba haciendo, pero honestamente, me costó entender sus programas porque tenían modelos de diseño tan sofisticados.
fuente
El primer enfoque está bien. En lenguajes de procedimiento, como C, el enfoque estándar es dividir la funcionalidad en espacios de nombres; usar clases estáticas como
DatabaseInfoGetter
es básicamente lo mismo. Obviamente, este enfoque conduce a un código más simple, más modular y más legible / mantenible que poner todo en una clase, incluso si todo se divide en métodos.Hablando de eso, trate de limitar los métodos para realizar la menor cantidad de acciones posible. Algunos programadores prefieren un poco menos de granularidad, pero los métodos enormes siempre se consideran dañinos.
Si todavía está luchando con la complejidad, lo más probable es que necesite desglosar su programa aún más. Cree más niveles en la jerarquía; tal vez
DatabaseInfoGetter
necesite hacer referencia a otras clases comoProductTableEntry
o algo así. Está escribiendo código de procedimiento, pero recuerde, ESTÁ usando C #, y OOP le brinda un montón de herramientas para reducir la complejidad, por ejemplo:Además, tenga cuidado con las clases estáticas. Las clases de bases de datos son buenas candidatas ... siempre que tenga una sola base de datos ... se le ocurre la idea.
En última instancia, si piensa en funciones matemáticas y C # no se adapta a su estilo, pruebe algo como Scala, Haskell, etc. También son geniales.
fuente
Estoy respondiendo 4 años tarde, pero cuando intentas hacer cosas de procedimiento en un lenguaje OO, entonces estás trabajando en un antipatrón. ¡Eso no es algo que debas hacer! Encontré un blog que aborda este antipatrón y muestra una solución para él, pero requiere mucha refactorización y un cambio de mentalidad. Consulte Código de procedimiento en Código orientado a objetos para obtener más detalles.
Como mencionaste "esto" y "aquello", básicamente estás sugiriendo que tienes dos clases diferentes. A Esta clase (¡mal nombre!) Y Esa clase. Y podría, por ejemplo, traducir esto:
dentro de esto:
Ahora, sería interesante ver esas más de 2,000 líneas de código de procedimiento y determinar cómo se pueden agrupar varias partes en clases separadas y mover varios procedimientos hacia abajo en esas clases.
Cada clase debe ser simple y centrada en hacer una sola cosa. Y me parece que algunas partes del código ya están tratando con superclases, que en realidad son demasiado grandes para ser útiles. DatabaseInfoGetter, por ejemplo, parece confuso. ¿Qué está recibiendo de todos modos? Suena demasiado genérico. ¡Sin embargo, SummaryPageGenerator es terriblemente específico! Y PrintUtility está siendo genérico nuevamente.
Entonces, para comenzar, debe evitar los nombres genéricos para sus clases y comenzar a usar nombres más específicos. La clase PrintUtility, por ejemplo, sería más una clase Print con una clase Header, una clase Footer, una clase TextBlock, una clase Image y posiblemente alguna forma de indicar cómo fluirían en la impresión misma. Todo esto podría estar en el Sin embargo, el espacio de nombres PrintUtility .
Para la base de datos, historia similar. Incluso si usa Entity Framework para el acceso a la base de datos, debe limitarlo a las cosas que usa y dividirlo en grupos lógicos.
Pero me pregunto si todavía estás lidiando con este problema después de 4 años. Si es así, entonces es una mentalidad que necesita ser cambiada del "concepto procesal" al "concepto orientado a objetos". Lo cual es desafiante si no tienes la experiencia ...
fuente
Soy mi opinión, debe cambiar su código para que sea simple, consistente y legible.
Escribí algunas publicaciones de blog sobre este tema, y confía en mí, son fáciles de entender: ¿Qué es un buen código?
Simple: al igual que una placa de circuito contiene diferentes piezas, cada una con una responsabilidad. El código debe dividirse en partes más pequeñas y simples.
Consistente: use patrones, un estándar para nombrar elementos y cosas. Te gustan las herramientas que funcionan de la misma manera todo el tiempo y quieres saber dónde encontrarlas. Es lo mismo con el código.
Legible: no use siglas a menos que sean ampliamente utilizadas y conocidas dentro del dominio al que se dirige el software (mattnz), prefiera nombres pronunciables que sean claros y fáciles de entender.
fuente