Em resumo, sempre que você usa "new", você está acoplando fortemente a classe que contém esse código ao objeto que está sendo criado; para instanciar um desses objetos, a classe que está instanciando deve saber sobre a classe concreta que está sendo instanciada. Portanto, ao usar "novo", considere se a classe em que você está inserindo a instanciação é um local "bom" para residir esse conhecimento e você está disposto a fazer alterações nessa área se a forma do o objeto instanciado mudaria.
O acoplamento apertado, que é um objeto com conhecimento de outra classe concreta, nem sempre deve ser evitado; em algum nível, alguma coisa, EM ALGUM LUGAR, precisa saber como criar esse objeto, mesmo que todo o resto lide com o objeto, recebendo uma cópia dele de outro lugar. No entanto, quando a classe que está sendo criada é alterada, qualquer classe que conhece a implementação concreta dessa classe deve ser atualizada para lidar corretamente com as alterações dessa classe.
A pergunta que você deve sempre fazer é: "Fazer com que essa classe saiba como criar essa outra classe se tornará uma responsabilidade ao manter o aplicativo?" As duas principais metodologias de projeto (SOLID e GRASP) geralmente respondem "sim" por motivos sutilmente diferentes. No entanto, são apenas metodologias e ambas têm a extrema limitação de que não foram formuladas com base no conhecimento do seu programa exclusivo. Como tal, eles podem apenas errar por precaução e assumir que qualquer ponto de acoplamento apertado EVENTUALMENTE causará um problema relacionado a alterações em um ou em ambos os lados deste ponto. Você deve tomar uma decisão final sabendo três coisas; a melhor prática teórica (que consiste em acoplar tudo porque tudo pode mudar); o custo da implementação das melhores práticas teóricas (que podem incluir várias novas camadas de abstração que facilitarão um tipo de mudança enquanto dificultam outro); e a probabilidade do mundo real de que o tipo de mudança que você está antecipando seja necessário.
Algumas diretrizes gerais:
Evite acoplamentos rígidos entre bibliotecas de códigos compiladas. A interface entre DLLs (ou um EXE e suas DLLs) é o principal local em que o acoplamento rígido apresentará uma desvantagem. Se você alterar uma classe A na DLL X e a classe B no EXE principal souber sobre a classe A, será necessário recompilar e liberar os dois binários. Dentro de um único binário, o acoplamento mais apertado é geralmente mais permitido, porque todo o binário deve ser reconstruído para qualquer alteração. Às vezes, a necessidade de reconstruir vários binários é inevitável, mas você deve estruturar seu código para evitá-lo sempre que possível, especialmente em situações em que a largura de banda é muito alta (como implantar aplicativos móveis; implantar uma nova DLL em uma atualização é muito mais barato do que empurrar o programa inteiro).
Evite acoplamentos estreitos entre os principais "centros lógicos" do seu programa. Você pode pensar em um programa bem estruturado como consistindo em fatias horizontais e verticais. As fatias horizontais podem ser camadas de aplicativos tradicionais, como interface do usuário, controlador, domínio, DAO, dados; fatias verticais podem ser definidas para janelas ou visualizações individuais ou para "histórias de usuários" individuais (como a criação de um novo registro de algum tipo básico). Ao fazer uma chamada que se move para cima, para baixo, esquerda ou direita em um sistema bem estruturado, você geralmente deve abstrair a chamada. Por exemplo, quando a validação precisa recuperar dados, ela não deve ter acesso ao banco de dados diretamente, mas deve fazer uma chamada para uma interface para recuperação de dados, que é apoiada pelo objeto real que sabe como fazer isso. Quando algum controle da interface do usuário precisa executar lógica avançada envolvendo outra janela, deve abstrair o disparo dessa lógica por meio de um evento e / ou retorno de chamada; ele não precisa saber o que será feito como resultado, permitindo alterar o que será feito sem alterar o controle que o aciona.
De qualquer forma, considere o quão fácil ou difícil será uma alteração e qual a probabilidade da referida alteração. Se um objeto que você está criando é usado apenas em um local e você não prevê essa alteração, o acoplamento rígido geralmente é mais permitido e pode até ser superior nessa situação a um acoplamento solto. O acoplamento flexível requer abstração, que é uma camada extra que impede a alteração de objetos dependentes quando a implementação de uma dependência deve ser alterada. No entanto, se a própria interface precisar alterar (adicionar uma nova chamada de método ou adicionar um parâmetro a uma chamada de método existente), uma interface realmente aumentará a quantidade de trabalho necessário para fazer a alteração. Você deve avaliar a probabilidade de diferentes tipos de mudança impactarem o design,