Tengo un abstract class A
que declara un método abstracto doStuff
. Actualmente hay muchas clases que heredan A
e implementan doStuff
.
Las instancias de la clase se inicializan en tiempo de ejecución mediante AFactory
la entrada del usuario. Originalmente, todas las clases tenían el mismo parámetro único (la entrada del usuario). Pero ahora tengo un parámetro adicional que solo A
necesita una nueva clase que herede .
Desglosándolo, pensé en la siguiente lógica:
La clase de intérprete que genera instancias basadas en la entrada del usuario (usando,
AFactory
por supuesto) no tenía conocimiento de este parámetro adicional.- Intentar introducirlo en la clase de intérprete de clase sería realmente incómodo porque entonces tendría que saber cuándo pasarlo a la fábrica, lo que anula el propósito de tener una fábrica en primer lugar.
- Enviarlo ciegamente a la Fábrica con la esperanza de que pueda hacer algo con él también parece bastante feo.
Mi solución actual: Mientras tanto he decidido refactor
A.doStuff(Param param)
enA.doStuff(AParams params)
.AParams
puede contener todos los parámetros necesarios ydoStuff
puede ignorarlos si no están interesados en ellos. Esto también me parece un poco incómodo y me evita enviar estructuras en WIN32API que pueden contener muchos parámetros feos e inútiles y no me gusta.
¿Hay alguna forma más elegante de abordar este problema? ¿O algún patrón de diseño que he pasado por alto y resuelve esto?
Notas :
- Estamos usando Java 1.7
- Los nombres de las clases son tontos para enfatizar el tema del diseño teórico, tienen nombres normales indicativos y significativos en la realidad :)
- Realicé muchas búsquedas, pero después de haber descubierto que es bastante difícil buscar en la web problemas teóricos abstractos específicos (en lugar de por qué está
X
arrojandoException
este código), he decidido preguntar de todos modos, así que lo siento si esto es un duplicar.
Editar 1 :
- Aclaración: necesito pasar un argumento específico de subclase al
doStuff
método.
EDITAR 2 :
No entendí completamente la intención de Kilian Foth, así que escribí un pseudocódigo de Java para ayudarme a explicar mejor el problema / comprender su solución. Entonces:
Este es un esqueleto de mi problema.
Este es un esqueleto de mi solución.
Esto es lo que creo que podría ser la solución de Kilian Foth, pero no estoy seguro.
doStuff
método?EntryPoint
para llamarlo,main
que luego invoca a algunosInterpreter
que actualmente no reciben el parámetro adicional como parámetro y también lo hace la fábrica. El parámetro adicional proviene de otro tipo de entrada del usuario que no se describeUserInput
(que desafortunadamente no puedo cambiar).Respuestas:
El objetivo de una fábrica es ocultar el hecho de que puede obtener objetos de clases ligeramente diferentes. Por lo tanto, si algunos de estos objetos necesitan ciertos datos y otros no ...
O no son lo suficientemente similares como para ser producidos por la misma fábrica. Entonces necesita tener más de una fábrica y tomar la decisión mucho antes de lo que está haciendo actualmente.
O son lo suficientemente similares como para que el cliente no se preocupe por cuál obtiene. Entonces no puede esperar que el cliente se preocupe por enviar o no los datos adicionales. Se deduce que siempre deben pasarse, pero la fábrica a veces los ignorará sin molestar al cliente con ese detalle de implementación. Cualquier otra cosa cargaría al cliente con la distinción que la fábrica debía ocultar.
fuente
Manejo esto usando algo como un generador que contiene varias fábricas. El Generador recibe hashes que contienen las fábricas para hacer varios tipos de objetos, y buscará en el hash y recuperará la fábrica correcta según los criterios.
Por ejemplo, tengo varios tipos de preguntas, y todo esto se puede hacer mediante la implementación de un IQuestionFactory. La mayoría de las implementaciones de IQuestionFactory son subclases de BaseQuestionFactory, y reciben XML que representa una pregunta y la analizan de acuerdo con las reglas de la pregunta, devolviendo algunas subclases de BaseQuestion.
Algunas preguntas necesitan más información, por ejemplo, si se trata de una pregunta que llena formularios, puede haber una sección del XML que contiene las preguntas que definen los campos que se utilizan en el formulario. Las Fábricas que hacen estas preguntas implementan IEnhancedQuestionFactory, y el constructor verificará la Fábrica que recupera de QuestionFactoryRegistry y verificará si implementa esta Interfaz. Si lo hace, pasa el XML completo del archivo que contenía las preguntas además del XML individual que va al método createQuestion.
Para mostrarle cuán flexible puede ser esto, algunas de las implementaciones de IQuestionFactory son simplemente shells que contienen múltiples fábricas de preguntas, y pueden mirar el XML entrante y luego llamar a una fábrica interna y devolver lo que sea que eso haga. Usamos esto cuando un archivo XML contiene múltiples tipos de preguntas.
Sin embargo, parece que necesita algo más como la forma en que construimos Ejercicios (que son esencialmente Controladores envueltos alrededor de una colección de Preguntas), donde el hash (ExerciseFactoryMap) está configurado para devolver una fábrica predeterminada la mayor parte del tiempo, pero en casos donde tiene una fábrica específica registrada, devolverá eso. Y tenemos una Fábrica específica que creará una subclase de Ejercicio que tiene una propiedad adicional, y tiene la capacidad adicional de mirar el XML y encontrar la información que necesita para llenar esa propiedad.
Entonces, estoy de acuerdo con Killian Foth en que el objetivo de una fábrica es ocultar los detalles de implementación de cómo se está construyendo el objeto, pero no hay nada que diga que no se pueden combinar múltiples fábricas de manera creativa (por ejemplo, una fábrica que contiene otras fábricas que hacen el trabajo real).
fuente
El enfoque habitual en casos como este (Command patternish) es pasar parámetros adicionales en el constructor. En su caso, que los medios de adición
otherArgument
queB
durante la construcción.Cambiar la lógica podría abrir algunas opciones. Por ejemplo, mover doStuff a AParams:
Alternativamente, puede tragarse su orgullo y consultarlo
instanceof
en el bucle y configurarlootherArgument
manualmente antes de llamardoStuff
.Si lo anterior no es posible, entonces no tiene más remedio que hacer lo que está haciendo (ampliar el argumento del parámetro). No sobreanalices estas cosas.
fuente