Miré algunas respuestas y busqué en Google, pero no pude encontrar nada útil (es decir, eso no tendría efectos secundarios incómodos).
Mi problema, en resumen, es que tengo un objeto y necesito realizar una larga secuencia de operaciones en él; Lo considero como una especie de línea de montaje, como construir un automóvil.
Creo que estos objetos se llamarían objetos de método .
Entonces, en este ejemplo, en algún momento tendría un CarWithoutUpholstery en el que luego necesitaría ejecutar installBackSeat, installFrontSeat, installWoodenInserts (las operaciones no interfieren entre sí, e incluso podrían hacerse en paralelo). Estas operaciones son realizadas por CarWithoutUpholstery.worker () y producen un nuevo objeto que sería un CarWithUpholstery, en el que luego podría ejecutar cleanInsides (), verificar NoUpholsteryDefects (), y así sucesivamente.
Las operaciones en una sola fase ya son independientes, es decir, ya estoy lidiando con un subconjunto de ellas que puede ejecutarse en cualquier orden (los asientos delanteros y traseros pueden instalarse en cualquier orden).
Mi lógica actualmente usa Reflection para simplificar la implementación.
Es decir, una vez que tengo un CarWithoutUpholstery, el objeto se inspecciona a sí mismo en busca de métodos llamados performSomething (). En ese punto, ejecuta todos estos métodos:
myObject.perform001SomeOperation();
myObject.perform002SomeOtherOperation();
...
mientras revisa errores y esas cosas. Si bien el orden de operación no es importante, he asignado un orden lexicográfico en caso de que descubra que algún orden es importante después de todo. Esto contradice a YAGNI , pero costó muy poco, un tipo simple (), y podría ahorrar un cambio de nombre de método masivo (o introducir algún otro método para realizar pruebas, por ejemplo, una serie de métodos) en el futuro.
Un ejemplo diferente
Digamos que en lugar de construir un automóvil, tengo que compilar un informe de la Policía Secreta sobre alguien y presentarlo a mi Señor Supremo Maligno . Mi objeto final será un ReadyReport. Para construirlo empiezo reuniendo información básica (nombre, apellido, cónyuge ...). Esta es mi Fase A. Dependiendo de si hay un cónyuge o no, es posible que tenga que pasar a las fases B1 o B2 y recopilar datos de sexualidad de una o dos personas. Esto está hecho de varias consultas diferentes a diferentes Evil Minions que controlan la vida nocturna, las cámaras de la calle, los recibos de ventas de la tienda de sexo y lo que no. Y así sucesivamente y así sucesivamente.
Si la víctima no tiene familia, ni siquiera entraré en la fase GetInformationAboutFamily, pero si lo tengo, entonces es irrelevante si primero apunto al padre o la madre o los hermanos (si los hay). Pero no puedo hacer eso si no he realizado un FamilyStatusCheck, que por lo tanto pertenece a una fase anterior.
Todo funciona de maravilla ...
- si necesito alguna operación adicional solo necesito agregar un método privado,
- Si la operación es común a varias fases, puedo heredarla de una superclase,
- Las operaciones son simples y autónomas. Ningún valor de una operación es siempre requerido por cualquiera de los otros (operaciones que no se realizan en una fase diferente),
- los objetos en el futuro no necesitan realizar muchas pruebas ya que ni siquiera podrían existir si sus objetos creadores no hubieran verificado esas condiciones en primer lugar. Es decir, cuando coloque inserciones en el tablero, limpie el tablero y verifique el tablero, no necesito verificar que haya un tablero realmente allí .
- Permite una prueba fácil. Puedo burlarme fácilmente de un objeto parcial y ejecutar cualquier método sobre él, y todas las operaciones son cuadros negros deterministas.
...pero...
El problema surgió cuando agregué una última operación en uno de mis objetos de método, lo que provocó que el módulo general excediera un índice de complejidad obligatorio ("menos de N métodos privados").
Ya llevé el asunto arriba y sugerí que, en este caso, la riqueza de los métodos privados no es una señal de desastre. La complejidad está ahí, pero está ahí porque la operación es compleja y, en realidad, no es tan compleja, solo es larga .
Usando el ejemplo de Evil Overlord, mi problema es que Evil Overlord (también conocido como El que no se le negará ) ha solicitado toda la información dietética, mis Minions dietéticos me dicen que necesito consultar restaurantes, cocinas, vendedores ambulantes, vendedores ambulantes sin licencia, invernadero propietarios, etc., y el malvado (sub) Overlord, conocido familiarmente como Él que tampoco se le negará, se queja de que estoy realizando demasiadas consultas en la fase GetDietaryInformation.
Nota : Soy consciente de que desde varios puntos de vista esto no es un problema en absoluto (ignorando posibles problemas de rendimiento, etc.). Todo lo que sucede es que una métrica específica es infeliz, y hay justificación para eso.
Lo que creo que podría hacer
Aparte de la primera, todas estas opciones son factibles y, creo, defendibles.
- He verificado que puedo ser astuto y declarar la mitad de mis métodos
protected
. Pero estaría explotando una debilidad en el procedimiento de prueba, y aparte de justificarme cuando me atrapan, no me gusta esto. Además, es una medida provisional. ¿Qué pasa si se duplica el número de operaciones requeridas? Improbable, pero ¿entonces qué? - Puedo dividir arbitrariamente esta fase en AnnealedObjectAlpha, AnnealedObjectBravo y AnnealedObjectCharlie, y hacer que un tercio de las operaciones se realicen en cada etapa. Tengo la impresión de que esto realmente agrega complejidad (N-1 más clases), sin ningún beneficio, excepto pasar una prueba. Por supuesto, puedo sostener que un CarWithFrontSeatsInstalled y un CarWithAllSeatsInstalled son etapas lógicamente sucesivas . El riesgo de que Alpha requiera más tarde un método Bravo es pequeño, e incluso menor si lo juego bien. Pero aún.
- Puedo agrupar diferentes operaciones, remotamente similares, en una sola.
performAllSeatsInstallation()
. Esta es solo una medida provisional y aumenta la complejidad de la operación individual. Si alguna vez necesito hacer las operaciones A y B en un orden diferente, y las he empaquetado dentro de E = (A + C) y F (B + D), tendré que separar E y F y barajar el código . - Puedo usar una variedad de funciones lambda y esquivar el cheque por completo, pero eso me parece torpe. Sin embargo, esta es la mejor alternativa hasta ahora. Se libraría de la reflexión. Los dos problemas que tengo es que probablemente me pedirán que reescriba todos los objetos del método, no solo el hipotético
CarWithEngineInstalled
, y aunque eso sería una muy buena seguridad laboral, realmente no es tan atractivo; y que el verificador de cobertura de código tiene problemas con lambdas (que son solucionables, pero aún así ).
Entonces...
- ¿Cuál crees que es mi mejor opción?
- ¿Hay alguna forma mejor que no haya considerado? ( tal vez sea mejor que me limpie y pregunte directamente ¿qué es? )
- ¿Es este diseño irremediablemente defectuoso, y mejor admito la derrota y la zanja, esta arquitectura por completo? No es bueno para mi carrera, pero ¿sería mejor escribir código mal diseñado a largo plazo?
- ¿Mi elección actual es en realidad la One True Way, y necesito luchar para obtener métricas (y / o instrumentación) de mejor calidad? Para esta última opción, necesitaría referencias ... No puedo simplemente saludar a @PHB mientras murmuro Estas no son las métricas que estás buscando . No importa cuánto me gustaría poder