Refactorizando una clase abstracta existente y sus parámetros

8

Tengo un abstract class Aque declara un método abstracto doStuff. Actualmente hay muchas clases que heredan Ae implementan doStuff.

Las instancias de la clase se inicializan en tiempo de ejecución mediante AFactoryla 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 Anecesita 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, AFactorypor 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)en A.doStuff(AParams params).

    AParamspuede contener todos los parámetros necesarios y doStuffpuede 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á Xarrojando Exceptioneste 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 doStuffmé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.

Tijera
fuente
¿Puede aclarar si su problema es que necesita agregar un argumento específico a su método de fábrica para una subclase específica, o es que necesita pasar un argumento específico de subclase al doStuffmétodo?
Martin Wickman el
@MartinWickman La respuesta es: necesito pasar un argumento específico de subclase al método doStuff. Gracias por mencionar esto, me disculpo por no ser lo suficientemente claro, pero creo que ahora es mejor :). He editado mi pregunta ...
Scis
Me parece que has encontrado algo similar a Builder en las soluciones a las que te has vinculado (que es lo que hice, nunca había oído hablar de Builder, pero solo codifiqué algo que hacía lo que necesitaba. hacer).
Amy Blankenship
¿Realmente necesita pasar el parámetro a doStuff, o se conoce el valor del parámetro cuando construye el objeto? Además, ¿de dónde viene el parámetro adicional?
Aaron Kurtzhals
@AaronKurtzhals Como escribí en el archivo de esqueleto "problemático", actualmente se conoce el valor del parámetro adicional EntryPointpara llamarlo, mainque luego invoca a algunos Interpreterque 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 describe UserInput(que desafortunadamente no puedo cambiar).
Tijera

Respuestas:

9

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.

Kilian Foth
fuente
Bueno, son lo suficientemente similares :) pero no estoy seguro de entender tu solución. ¿Puede ver la pregunta editada (he intentado hacer un esqueleto que muestre el problema y los diferentes enfoques) y decirme si mi interpretación es correcta o no?
Tijera
2

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).

Amy Blankenship
fuente
2
  • 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 otherArgumentque Bdurante la construcción.

  • Cambiar la lógica podría abrir algunas opciones. Por ejemplo, mover doStuff a AParams:

    for (String arg : listOfArgs) {
        AParams p = AParams.create(arg, otherArgument));
        for (A a : listOfA) {
            p.doStuff(a); // Let AParams to the lifting depending on type of A
        }
    }
  • Alternativamente, puede tragarse su orgullo y consultarlo instanceofen el bucle y configurarlo otherArgumentmanualmente antes de llamar doStuff.

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.

Martin Wickman
fuente