Para hacer que su código se acople libremente aquí hay algunas cosas simples para recordar:
Parte 1:
Técnicamente conocido como "Separación de preocupaciones". Cada clase tiene un rol específico, debe manejar la lógica de negocios o la lógica de la aplicación. Trate de mantenerse alejado de la clase que combina ambas responsabilidades. es decir, una clase que gestiona datos (a largo plazo) es la lógica de la aplicación, mientras que una clase que utiliza datos es la lógica de negocios.
Personalmente me refiero a esto (en mi propio pequeño mundo) como create it or use it
. Una clase debe crear un objeto o usar un objeto que nunca debe hacer ambas cosas.
Parte 2:
Cómo implementar la separación de preocupaciones.
Como punto de partida hay dos técnicas simples:
Nota: Los patrones de diseño no son absolutos.
Se supone que están personalizados para la situación, pero tienen un tema subyacente que es similar a todas las aplicaciones. Así que no mire los ejemplos a continuación y diga que debo seguir esto rígidamente; estos son solo ejemplos (y un poco artificiales).
Inyección de dependencia :
Aquí es donde pasa un objeto que usa una clase. El objeto que pasa basado en una interfaz para que su clase sepa qué hacer con él, pero no necesita saber la implementación real.
class Tokenizer
{
public:
Tokenizer(std::istream& s)
: stream(s)
{}
std::string nextToken() { std::string token; stream >> token;return token;}
private:
std::istream& stream;
};
Aquí inyectamos la secuencia en un Tokenizer. El tokenizer no sabe de qué tipo es la secuencia, siempre que implemente la interfaz de std :: istream.
Patrón de localizador de servicio :
El patrón del localizador de servicios es una ligera variación en la inyección de dependencia. En lugar de dar un objeto que pueda usar, le pasa un objeto que sabe cómo ubicar (crear) el objeto que desea usar.
class Application
{
public:
Application(Persister& p)
: persistor(p)
{}
void save()
{
std::auto_ptr<SaveDialog> saveDialog = persistor.getSaveDialog();
saveDialog.DoSaveAction();
}
void load()
{
std::auto_ptr<LoadDialog> loadDialog = persistor.getLoadDialog();
loadDialog.DoLoadAction();
}
private:
Persister& persistor;
};
Aquí pasamos el objeto de aplicación un objeto persistente. Cuando realiza una acción de guardar / cargar, utiliza el persistor para crear un objeto que realmente sabe cómo realizar la acción. Nota: Una vez más, el persistor es una interfaz y puede proporcionar diferentes implementaciones según la situación.
Esto es útil cuando potentially
se requiere un objeto único cada vez que crea una instancia de una acción.
Personalmente, creo que esto es particularmente útil para escribir pruebas unitarias.
Nota de patrones:
Los patrones de diseño son un tema enorme en sí mismo. De ninguna manera es una lista exclusiva de patrones que puede usar para ayudar con el acoplamiento suelto; Este es solo un punto de partida común.
Con experiencia, se dará cuenta de que ya está usando estos patrones, es solo que no usó sus nombres formales. Al estandarizar sus nombres (y hacer que todos los aprendan) descubrimos que es más fácil y rápido comunicar ideas.
managing the data
me refiero a las variables (no a los datos reales). Por lo tanto, cosas como los punteros deben gestionarse para que no se filtren. Pero los datos se pueden inyectar o la forma en que se recuperan los datos se puede abstraer (para que su clase se pueda reutilizar con diferentes métodos de recuperación de datos). Lo siento, no puedo ser más preciso.minutae of loose coupling
(me encanta esa palabra minutae)). El secreto de la programación es aprender cuándo usar las técnicas. El uso excesivo puede conducir a una maraña de código.Soy un desarrollador de ASP.NET, así que no sé mucho sobre el acoplamiento de WinForms, pero sé un poco de las aplicaciones web N-Tier, suponiendo una arquitectura de aplicación de 3 niveles de UI, Dominio, Capa de acceso a datos (DAL).
El acoplamiento flojo se trata de abstracciones.
Como @MKO dice que si puede reemplazar un ensamblaje con otro (por ejemplo, un nuevo proyecto de interfaz de usuario que usa su proyecto de dominio, un nuevo DAL que se guarda en una hoja de cálculo en lugar de una base de datos), entonces hay un acoplamiento flojo. Si su dominio y DAL dependen de proyectos más adelante en la cadena, entonces el acoplamiento podría ser más flojo.
Un aspecto de algo que se acopla libremente es si puede reemplazar un objeto con otro que implemente la misma interfaz. No depende del objeto real, sino de la descripción abstracta de lo que hace (su interfaz).
El acoplamiento flojo, las interfaces y los Inyectores de dependencia (DI) y la Inversión de control (IoC) son útiles para el aspecto de aislamiento del diseño para la comprobabilidad.
Por ejemplo, un objeto en el proyecto de IU llama a un objeto Repository en el proyecto de dominio.
Puede crear un objeto falso que implemente la misma interfaz que el repositorio que usa el código bajo prueba, luego escriba un comportamiento especial para las pruebas ( resguardos para evitar que se llame al código de producción que guarda / elimina / obtiene y simula que actúan como resguardos y realizan un seguimiento del estado del objeto falso para fines de prueba).
Esto significa que el único código de producción que se llama ahora solo está en su objeto de interfaz de usuario, su prueba será solo contra ese método y cualquier falla de prueba aislará el defecto de ese método.
Además, en el menú Analizar en VS (dependiendo de la versión que tenga) hay herramientas para Calcular Métricas de Código para su proyecto, una de las cuales es el Acoplamiento de Clase, habrá más información sobre esto en la documentación de MSDN.
No te TOO empantanado en el minutae de acoplamiento débil sin embargo, si no hay ninguna posibilidad de que las cosas son para ser reutilizado (por ejemplo, un proyecto de dominio con más de una interfaz de usuario) y la vida útil del producto es pequeño, entonces el acoplamiento débil se vuelve menos de prioridad (aún se tendrá en cuenta), pero seguirá siendo responsabilidad de los arquitectos / líderes tecnológicos que revisarán su código.
fuente
fuente
Eche un vistazo a los 5 principios SÓLIDOS . Al adherirse al SRP, el ISP y el DIP reducirán significativamente el acoplamiento, siendo el DIP el más poderoso. Es el principio fundamental debajo de la DI ya mencionada .
Además, vale la pena echarle un vistazo a GRASP . Es una mezcla extraña entre conceptos abstractos (al principio le resultará difícil de implementar) y patrones concretos (que en realidad podrían ser de ayuda), pero la belleza es probablemente la menor de sus preocupaciones en este momento.
Y por último, puede encontrar esta sección sobre IoC bastante útil, como punto de entrada a técnicas comunes.
De hecho, encontré una pregunta sobre stackoverflow , donde demuestro la aplicación de SOLID en un problema concreto. Podría ser una lectura interesante.
fuente
De acuerdo con Wikipedia:
El problema con el acoplamiento apretado hace que sea difícil hacer cambios. (Muchos escritores parecen sugerir que esto causa problemas principalmente durante el mantenimiento, pero en mi experiencia también es relevante durante el desarrollo inicial). Lo que tiende a suceder en sistemas estrechamente acoplados es que un cambio en un módulo del sistema requiere cambios adicionales. en los módulos a los que está acoplado. La mayoría de las veces, esto requiere más cambios en otros módulos, etc.
Por el contrario, en un sistema débilmente acoplado, los cambios están relativamente aislados. Por lo tanto, son menos costosos y se pueden hacer con mayor confianza.
En su ejemplo particular, el manejo de eventos proporciona cierta separación entre la GUI y los datos subyacentes. Sin embargo, suena como si hubiera otras áreas de separación que podría explorar. Sin más detalles de su situación particular, es difícil ser específico. Sin embargo, puede comenzar con una arquitectura de 3 niveles que separe:
Algo a tener en cuenta es que para aplicaciones pequeñas y únicas con un único desarrollador, los beneficios de forzar el acoplamiento suelto en todos los niveles pueden no valer la pena. Por otro lado, las aplicaciones más grandes y complejas con múltiples desarrolladores son imprescindibles. Inicialmente, hay un costo incurrido al introducir las abstracciones y educar a los desarrolladores que no están familiarizados con el código con respecto a su arquitectura. Sin embargo, a la larga, el acoplamiento suelto ofrece ventajas que superan con creces los costos.
Si se toma en serio el diseño de sistemas acoplados libremente, lea sobre principios SÓLIDOS y patrones de diseño.
Sin embargo, lo importante es darse cuenta de que estos patrones y principios son solo eso: patrones y principios. Son no gobierna. Esto significa que deben aplicarse de forma pragmática e inteligente.
En lo que respecta a los patrones, es importante comprender que no existe una implementación "correcta" única de ninguno de los patrones. Tampoco son plantillas para cortar galletas para diseñar su propia implementación. Están allí para decirle qué forma podría tener una buena solución y para proporcionarle un lenguaje común para comunicar las decisiones de diseño con otros desarrolladores.
Todo lo mejor.
fuente
Use inyección de dependencia, patrones de estrategia y eventos. En general: lea sobre los patrones de diseño, se trata de acoplamiento suelto y reducción de dependencias. Yo diría que los eventos se acoplan tan libremente como se obtendrían, mientras que la inyección de dependencia y los patrones de estrategia requieren algunas interfaces.
Un buen truco es colocar clases en diferentes bibliotecas / ensamblados, y hacer que dependan de la menor cantidad posible de otras bibliotecas, lo que lo obligará a refactorizar para usar menos dependencias
fuente
Permítanme proporcionar una vista alternativa. Solo pienso en esto en términos de que cada clase es una buena API. El orden en que se invocan los métodos es obvio. Lo que hacen es obvio. Redujo la cantidad de métodos al mínimo necesario. Por ejemplo,
init, abrir, cerrar
versus
setTheFoo, setBar, initX, getConnection, close
El primero es obvio y parece una buena API. El segundo puede causar errores si se llama en el orden incorrecto.
No me preocupo demasiado por tener que modificar y volver a compilar las personas que llaman. Mantengo MUCHO código, algunos nuevos y otros de 15 años. Por lo general, quiero errores del compilador cuando hago cambios. A veces, a propósito, romperé una API por ese motivo. Me da la oportunidad de considerar las ramificaciones de cada persona que llama. No soy un gran admirador de la inyección de dependencias porque quiero poder rastrear visualmente mi código sin cuadros negros y quiero que el compilador detecte tantos errores como sea posible.
fuente