2009-03-08 19 views
6

Estoy diseñando una biblioteca de clases reutilizable que contiene 2 ensamblajes (entre otros) denominados core.xml.dll y core.string.dll.Dependencias circulares frente a DRY

El conjunto xml hace referencia al conjunto de cadenas para utilizar algunos métodos de ayuda de cadenas.

Sin embargo, ahora hay un método de cadena que se beneficiaría del uso de un método contenido en el conjunto xml.

Si hago referencia al conjunto xml del conjunto de cadenas, habrá creado una dependencia circular y no podré compilar ambos ensamblados a partir del código fuente. (es decir, el problema del pollo y el huevo).

Para seguir el principio de "No repita usted mismo" me gustaría evitar la duplicación de la funcionalidad en ambos conjuntos. Si encuentro un error en la implementación, solo quiero solucionarlo en un solo lugar.

Si bien podría fusionar los conjuntos en uno, esto no es ideal, ya que reduce la cohesión del conjunto.

Tendría que volver a construir y volver a implementar todo el conjunto solo por un pequeño cambio en una clase específica. Además, eventualmente, con tantas dependencias, probablemente terminaría con una gran biblioteca.

Entonces, en el contexto de un conjunto reutilizable de conjuntos de bibliotecas, ¿cuál es el mejor enfoque para usar aquí? Además, ¿cómo se maneja el .NET Framework con este problema?

(Reflector parece que hace referencia a System.Configuration.dll system.xml.dll y viceversa. Es esto realmente correcto, si es así cómo se gestiona la dependencia circular?)

+0

Podría dar un ejemplo concreto de cómo un "... método de cadena que se beneficiaría de usar un método contenido en el conjunto xml." –

+0

Estaba buscando un método de conversión que acepta XML y se convierte a un formato basado en cadenas. Sí, estoy pensando que esto específicamente podría manejarse de otra manera, sin embargo, todavía estoy interesado en la pregunta más general. – Ash

+0

Buscar hasta el libro de John Lakos, "Diseño de software de C++ a gran escala". Mucho de ese material puede no ser aplicable a C#, pero profundiza en la arquitectura nivelada, que es exactamente a lo que responde su pregunta. – Tom

Respuesta

6

De acuerdo con los cuchillos. Las dependencias circulares son un olor de diseño. Refactorizarlo sin piedad

Esto puede ser desafiante en el caso de que los objetos comerciales estén estrechamente acoplados. En la mayoría de los casos, esto puede resolverse mediante inyección de dependencia.

Pseudo C Ejemplo ++:

class Employee { 
    Company company; 
}; 

class Company { 
    vector<Employee> employees; 
}; 

Tricky? No neccesarily:

template<class CompanyT> 
class Employee { 
    CompanyT company; 
}; 

class Company { 
    vector<Employee<Company> > employees; 
}; 

tipos más primitivos, que deben depender de un nivel más alto, se pueden abstraer para trabajar con cualquier tipo de otro tipo, siempre y cuando cumpla con sus contratos.

+0

Pero vi que las dependencias circulares suelen aparecer con bastante naturalidad en un objeto diseño orientado, es decir, el empleado trabaja para la empresa, la empresa tiene una serie de empleados. ¿Cómo se refactoriza sin cambiar este tipo de relación? – Ash

+0

@Ash: ¿El enfoque de su sistema es el empleado o la empresa? Si el primero, entonces el empleado no es propiedad de la empresa, la compañía es simplemente un aspecto de sus vidas. Si esto último, la empresa tiene varias clases de empleados, que comparten una o más interfaces comunes. – Shog9

+0

@ Shog9, creo que creo referencias bidireccionales en mis diseños con bastante frecuencia. Pero parece que estás diciendo que esto puede ser un olor de diseño y debe minimizarse. Tendré que pensar un poco más sobre esto, gracias. – Ash

3

Suena como que necesita una tercera ensamblaje ...

+0

Pensé en un tercer ensamblado que contiene Intefaces solamente. Pero, ¿no obliga esto a la aplicación (cliente) de ambos ensamblajes a hacer todo el "taponamiento" de los componentes juntos? requiere un marco de inyección de dependencias completo, ¿no? – Ash

+1

@Ash - trate de no sobre-diseñarlo en su cabeza. Si necesita convertir XML -> String, eso no requiere la dependencia de inyectar ction, que solo requiere una única función libre (que hace referencia a las bibliotecas String y XML). – Tom

+0

estuvo de acuerdo con la respuesta de Tom, no está de acuerdo con la necesidad de una tercera asamblea, es excesivo cuando podría ser un método de extensión, es decir, piense en los métodos de extensión de linq en IEnumerable ... – eglasius

3

Haga que sea un método de extensión que se define en el conjunto xml (basado en su comentario "método de conversión que acepta XML y lo convierte en un formato basado en cadenas"). Se trata de xml. Al igual que Linq lo hace por IEnumerable.

+0

Consejo justo. Pero existe un enfoque de ingeniería de software más general aplicable a este problema. Mi ejemplo con string y xml quizás no sea el mejor, ¿qué tal un conjunto de Configuraciones usando ensamblado XML y viceversa? – Ash

+0

@Ash: un módulo XML central no necesita configuración en absoluto. Una configuración basada en XML tiene sentido. Un módulo de alto nivel que utiliza una configuración basada en XML para determinar cómo analizar otros documentos XML utilizará ambas bibliotecas. – Tom

3

Si su biblioteca String realmente necesita su biblioteca XML, probablemente sea una indicación de que su biblioteca String necesita ser refactorizada en una definición de tipo de datos de nivel inferior y "utilidades principales" (sin dependencias externas, si es posible) y otra biblioteca de nivel superior que puede incluir XML, SQL, expresiones regulares o cualquier otra cosa que encuentre útil en la capa de la aplicación.

+0

Buenos consejos, gracias. – Ash

Cuestiones relacionadas