Para tornar seu código fracamente acoplado, aqui estão algumas coisas simples a serem lembradas:
Parte 1:
Tecnicamente conhecido como "Separação de Preocupação". Cada classe tem uma função específica; deve lidar com a lógica de negócios ou lógica de aplicativo. Tente ficar longe da classe que combina as duas responsabilidades. ou seja, uma classe que gerencia dados (de longo prazo) é lógica de aplicativo enquanto uma classe que usa dados é lógica de negócios.
Pessoalmente, refiro-me a isso (no meu próprio mundinho) como create it or use it
. Uma classe deve criar um objeto ou usar um objeto que nunca deve fazer as duas coisas.
Parte 2:
Como implementar a separação de preocupações.
Como ponto de partida, existem duas técnicas simples:
Nota: Os padrões de design não são absolutos.
Eles devem ser personalizados para a situação, mas têm um tema subjacente semelhante a todos os aplicativos. Portanto, não olhe para os exemplos abaixo e diga que devo seguir isso rigidamente; estes são apenas exemplos (e levemente artificial nisso).
Injeção de Dependência :
É aqui que você passa um objeto que uma classe usa. O objeto que você passa com base em uma interface para que sua classe saiba o que fazer com ele, mas não precisa saber a implementação real.
class Tokenizer
{
public:
Tokenizer(std::istream& s)
: stream(s)
{}
std::string nextToken() { std::string token; stream >> token;return token;}
private:
std::istream& stream;
};
Aqui injetamos o fluxo em um tokenizador. O tokenizer não sabe que tipo de fluxo é, desde que implemente a interface std :: istream.
Padrão do Localizador de Serviço :
O padrão do localizador de serviço é uma pequena variação na injeção de dependência. Em vez de fornecer um objeto que ele possa usar, você passa um objeto que sabe como localizar (criar) o objeto que deseja 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;
};
Aqui passamos o objeto de aplicativo como um objeto persistente. Quando você executa uma ação de salvar / carregar, ele usa o persistor para criar um objeto que realmente sabe como executar a ação. Nota: Novamente, o persistor é uma interface e você pode fornecer implementações diferentes, dependendo da situação.
Isso é útil quando um potentially
objeto exclusivo é necessário toda vez que você instancia uma ação.
Pessoalmente, acho que isso é particularmente útil na escrita de testes de unidade.
Nota dos padrões:
Padrões de design são um assunto enorme em si. Esta não é de forma alguma uma lista exclusiva de padrões que você pode usar para ajudar no acoplamento solto; este é apenas um ponto de partida comum.
Com a experiência, você perceberá que já está usando esses padrões, apenas que não usou seus nomes formais. Ao padronizar seus nomes (e fazer com que todos os aprendam), descobrimos que é fácil e rápido comunicar idéias.
managing the data
estou me referindo às variáveis (não aos dados reais). Portanto, coisas como ponteiros precisam ser gerenciados para que não vazem. Mas os dados podem ser injetados ou a forma como os dados são recuperados pode ser abstraídos (para que sua classe possa ser reutilizada com diferentes métodos de recuperação de dados). Lamento não poder ser mais preciso.minutae of loose coupling
(ame essa palavra minutae)). O segredo da programação é aprender quando usar as técnicas. O uso excessivo pode levar a um emaranhado de código.Sou desenvolvedor do ASP.NET, por isso não conheço muito sobre o acoplamento WinForms, mas conheço um pouco dos aplicativos da web de N camadas, assumindo uma arquitetura de aplicativo de três camadas da interface do usuário, domínio, DAL (camada de acesso a dados).
O acoplamento fraco é sobre abstrações.
Como o @MKO afirma que se você pode substituir um assembly por outro (por exemplo, um novo projeto de interface do usuário que usa seu projeto de domínio, um novo DAL que salva em uma planilha em vez de um banco de dados), há um acoplamento solto. Se o seu domínio e o DAL dependem de projetos mais adiante, o acoplamento pode ser mais frouxo.
Um aspecto de algo sendo pouco acoplado é se você pode substituir um objeto por outro que implementa a mesma interface. Não depende do objeto real, mas da descrição abstrata do que ele faz (sua interface).
Acoplamentos fracos, interfaces e Injetores de Dependência (DI) e Inversão de Controle (IoC) são úteis para o aspecto de isolamento do projeto para teste.
Por exemplo, um objeto no projeto de interface do usuário chama um objeto de repositório no projeto de domínio.
Você pode criar um objeto falso que implemente a mesma interface que o repositório usado pelo código em teste e, em seguida, escrever um comportamento especial para testes ( stubs para impedir que o código de produção salve / exclua / seja chamado e zombes que agem como stubs e acompanhem do estado do objeto falso para fins de teste).
Os meios pelos quais o único código de produção que está sendo chamado agora são apenas no seu objeto de interface do usuário, seu teste será somente contra esse método e quaisquer falhas de teste isolarão o defeito desse método.
Além disso, no menu Analisar no VS (dependendo da versão que você possui), existem ferramentas para calcular métricas de código para o seu projeto, uma das quais é o acoplamento de classe, mais informações sobre isso na documentação do MSDN.
Não fique TOO atolados na minutae de baixo acoplamento, porém, se não há nenhuma chance de que as coisas estão a ficar reutilizado (por exemplo, um projeto de domínio com mais de um UI) ea vida do produto é pequena, então o acoplamento fraco torna-se menos de prioridade (ainda será levado em consideração), mas ainda será responsabilidade dos arquitetos / líderes de tecnologia que revisarão seu código.
fonte
fonte
Veja os 5 princípios do SOLID . Aderindo ao SRP, o ISP e o DIP reduzirão significativamente o acoplamento, sendo o DIP de longe o mais poderoso. É o princípio fundamental do DI já mencionado .
Além disso, vale a pena dar uma olhada no GRASP . É uma mistura estranha entre conceitos abstratos (você achará difícil de implementar a princípio) e padrões concretos (que podem realmente ajudar), mas a beleza é provavelmente a menor das suas preocupações no momento.
E por último, você pode achar esta seção sobre IoC bastante útil, como um ponto de entrada para técnicas comuns.
De fato, encontrei uma pergunta no stackoverflow , onde demonstro a aplicação do SOLID em um problema concreto. Pode ser uma leitura interessante.
fonte
De acordo com a Wikipedia:
O problema do acoplamento rígido torna difícil fazer alterações. (Muitos autores parecem sugerir que isso causa principalmente problemas durante a manutenção, mas, na minha experiência, também é relevante durante o desenvolvimento inicial.) O que tende a acontecer em sistemas fortemente acoplados é que uma alteração em um módulo no sistema requer alterações adicionais nos módulos aos quais está acoplado. Na maioria das vezes, isso requer mais alterações em outros módulos e assim por diante.
Por outro lado, em um sistema pouco acoplado, as mudanças são relativamente isoladas. Eles são, portanto, menos dispendiosos e podem ser fabricados com maior confiança.
No seu exemplo particular, o material de manipulação de eventos fornece alguma separação entre a GUI e os dados subjacentes. No entanto, parece que existem outras áreas de separação que você poderia explorar. Sem mais detalhes de sua situação específica, é difícil ser específico. No entanto, você pode começar com uma arquitetura de três camadas que se separa:
Algo a considerar é que, para aplicativos pequenos e únicos com um único desenvolvedor, os benefícios de impor acoplamentos soltos em todos os níveis podem não valer o esforço. Por outro lado, aplicativos maiores e mais complexos com vários desenvolvedores, eles são obrigatórios. Inicialmente, há um custo incorrido na introdução das abstrações e na educação dos desenvolvedores não familiarizados com o código referente à sua arquitetura. A longo prazo, no entanto, o acoplamento solto oferece vantagens que superam os custos.
Se você é sério sobre o design de sistemas com acoplamentos soltos, leia os princípios e padrões de projeto do SOLID.
O importante a perceber, no entanto, é que esses padrões e princípios são exatamente isso - padrões e princípios. Eles não são regras. Isso significa que eles precisam ser aplicados de forma pragmática e inteligente
No que diz respeito aos padrões, é importante entender que não existe uma implementação "correta" única de nenhum dos padrões. Também não são modelos de corte de biscoito para projetar sua própria implementação. Eles estão lá para informar qual a forma que uma boa solução pode ter e fornecer uma linguagem comum para comunicar decisões de design com outros desenvolvedores.
Muito bem sucedida.
fonte
Use injeção de dependência, padrões de estratégia e eventos. Em geral: leia os padrões de design, eles são sobre acoplamentos soltos e redução de dependências. Eu diria que os eventos são tão fracamente acoplados quanto você obteria, enquanto os padrões de injeção e estratégia de dependência exigem algumas interfaces.
Um bom truque é colocar classes em diferentes bibliotecas / assemblies e fazer com que elas dependam do menor número possível de bibliotecas, o que forçará você a refatorar o uso de menos dependências
fonte
Deixe-me fornecer uma visão alternativa. Eu só penso nisso em termos de cada classe ser uma boa API. A ordem em que os métodos são chamados é óbvia. O que eles fazem é óbvio. Você reduziu o número de métodos ao mínimo necessário. Por exemplo,
init, abrir, fechar
versus
setTheFoo, setBar, initX, getConnection, fechar
O primeiro é óbvio e parece uma boa API. O segundo pode causar erros se chamado na ordem errada.
Não me preocupo muito em ter que modificar e recompilar os chamadores. Eu mantenho MUITO código, alguns novos e 15 anos de idade. Eu normalmente quero erros de compilador quando faço alterações. Às vezes, eu quebro uma API de propósito por esse motivo. Isso me dá a chance de considerar as ramificações de cada chamador. Não sou muito fã de injeção de dependência, porque quero rastrear visualmente meu código sem caixas pretas e quero que o compilador capture o maior número possível de erros.
fonte