sourcemaking - oop design patterns
Objeto de Dios: disminuir el acoplamiento a un objeto ''maestro'' (6)
¿Consulta a cada cliente para sus parámetros requeridos e inyéctelos?
Ejemplo: cada "objeto" que requiere "parámetros" es un "Cliente". Cada "Cliente" expone una interfaz a través de la cual un "Agente de configuración" consulta al Cliente sus parámetros requeridos. El Agente de configuración luego "inyecta" los parámetros (y solo aquellos requeridos por un Cliente).
Tengo un objeto llamado Parámetros que se lanza de método en método hacia abajo y hacia arriba en el árbol de llamadas, a través de los límites del paquete. Tiene alrededor de cincuenta variables de estado. Cada método puede usar una o dos variables para controlar su salida.
Creo que esta es una mala idea, porque no puedo ver fácilmente qué método necesita para funcionar, o incluso qué podría suceder si con una cierta combinación de parámetros para el módulo Y que no tiene relación con mi módulo actual.
¿Cuáles son algunas buenas técnicas para disminuir el acoplamiento a este objeto divino o, idealmente, eliminarlo?
public void ExporterExcelParFonds(ParametresExecution parametres)
{
ApplicationExcel appExcel = null;
LogTool.Instance.ExceptionSoulevee = false;
bool inclureReferences = parametres.inclureReferences;
bool inclureBornes = parametres.inclureBornes;
DateTime dateDebut = parametres.date;
DateTime dateFin = parametres.dateFin;
try
{
LogTool.Instance.AfficherMessage(Variables.msg_GenerationRapportPortefeuilleReference);
bool fichiersPreparesAvecSucces = PreparerFichiers(parametres, Sections.exportExcelParFonds);
if (!fichiersPreparesAvecSucces)
{
parametres.afficherRapportApresGeneration = false;
LogTool.Instance.ExceptionSoulevee = true;
}
else
{
La persona que llama haría:
PortefeuillesReference pr = new PortefeuillesReference();
pr.ExporterExcelParFonds(parametres);
(Estoy asumiendo que esto está dentro de un entorno Java o .NET) Convierta la clase en un singleton. Agregue un método estático llamado "getInstance ()" o algo similar para llamar para obtener el paquete de nombre y valor (y deje de "recorrerlo"; consulte el capítulo 10 del libro "Code Complete").
Ahora la parte difícil. Presumiblemente, esto se encuentra dentro de una aplicación web o en algún otro entorno no por lotes / de un único subproceso. Entonces, para obtener acceso a la instancia correcta cuando el objeto no es realmente un singleton verdadero, debe ocultar la lógica de selección dentro del acceso estático.
En java, puede configurar una referencia "hilo local" e inicializarla cuando se inicie cada solicitud o subtarea. Luego, codifique el acceso en términos de ese thread-local. No sé si existe algo análogo en .NET, pero siempre puede simularlo con un diccionario (Hash, Map) que utiliza la instancia de la secuencia actual como clave.
Es un comienzo ... (siempre hay una descomposición del blob, pero construí un framework que tiene un valor-store semi-global muy similar dentro de él)
Parece que no está aplicando principios orientados a objetos (OO) en su diseño. Como mencionas la palabra "objeto", supongo que estás trabajando en algún tipo de paradigma OO. Te recomiendo que conviertas tu "árbol de llamadas" en objetos que modelen el problema que estás resolviendo. Un "objeto de dios" definitivamente es algo que debe evitarse. Creo que puede que te estés perdiendo algo fundamental ... Si publicas algunos ejemplos de código, puedo responder con más detalles.
Si todos sus métodos están usando la misma clase de Parameters
entonces tal vez debería ser una variable miembro de una clase con los métodos relevantes en ella, entonces puede pasar Parameters
al constructor de esta clase, asignarla a una variable miembro y todos sus métodos puede usarlo para tener que pasarlo como un parámetro.
Una buena manera de comenzar a refactorizar esta clase de Dios es dividiéndola en pedazos más pequeños. Encuentre grupos de propiedades relacionadas y divídalas en su propia clase.
Luego puede volver a visitar los métodos que dependen de Parameters
y ver si puede reemplazarlo con una de las clases más pequeñas que creó.
Es difícil dar una buena solución sin algunas muestras de código y situaciones del mundo real.
En primer lugar, a riesgo de afirmar lo obvio: pasar los parámetros que utilizan los métodos, en lugar del objeto divino.
Esto, sin embargo, puede llevar a que algunos métodos necesiten grandes cantidades de parámetros porque llaman a otros métodos, que llaman a otros métodos uno por uno, etcétera. Esa fue probablemente la inspiración para poner todo en un objeto divino. Daré un ejemplo simplificado de tal método con demasiados parámetros; tendrás que imaginar que "demasiados" == 3 aquí :-)
public void PrintFilteredReport(
Data data, FilterCriteria criteria, ReportFormat format)
{
var filteredData = Filter(data, criteria);
PrintReport(filteredData, format);
}
Entonces la pregunta es, ¿cómo podemos reducir la cantidad de parámetros sin recurrir a un objeto divino? La respuesta es deshacerse de la programación de procedimientos y hacer un buen uso del diseño orientado a objetos. Los objetos pueden usarse entre sí sin necesidad de conocer los parámetros que se utilizaron para inicializar a sus colaboradores:
// dataFilter service object only needs to know the criteria
var dataFilter = new DataFilter(criteria);
// report printer service object only needs to know the format
var reportPrinter = new ReportPrinter(format);
// filteredReportPrinter service object is initialized with a
// dataFilter and a reportPrinter service, but it doesn''t need
// to know which parameters those are using to do their job
var filteredReportPrinter = new FilteredReportPrinter(dataFilter, reportPrinter);
Ahora el método FilteredReportPrinter.Print se puede implementar con un solo parámetro:
public void Print(data)
{
var filteredData = this.dataFilter.Filter(data);
this.reportPrinter.Print(filteredData);
}
A propósito, este tipo de separación de preocupaciones e inyección de dependencia es buena para algo más que la eliminación de parámetros. Si accede a objetos colaboradores a través de interfaces, eso hace que su clase
- muy flexible: puede configurar FilteredReportPrinter con cualquier implementación de filtro / impresora que pueda imaginar
- muy comprobable: puede pasar colaboradores simulados con respuestas enlatadas y verificar que se usaron correctamente en una prueba unitaria
Para los parámetros que dictan el comportamiento, uno puede instanciar un objeto que exhibe el comportamiento configurado. Entonces las clases de clientes simplemente usan el objeto instanciado, ni el cliente ni el servicio tienen que saber cuál es el valor del parámetro. Por ejemplo, para un parámetro que indique dónde leer los datos, haga que FlatFileReader, XMLFileReader y DatabaseReader hereden la misma clase base (o implementen la misma interfaz). Cree una instancia de uno de ellos base en el valor del parámetro, luego los clientes de la clase lectora simplemente solicitan datos al objeto lector instanciado sin saber si los datos provienen de un archivo o de la base de datos.
Para empezar, puede dividir su gran clase ParametresExecution en varias clases, una por paquete, que solo contiene los parámetros del paquete.
Otra dirección podría ser pasar el objeto ParametresExecution en el momento de la construcción. No tendrá que pasarlo por alto en cada llamada a la función.