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.