Considera il seguente esempio:
// Let's say this method lives in a class MyClass
public void doSomething(Foo foo, Bar bar) {
foo.prepareProcessing();
Worker work = foo.createWorker();
work.dispatchTask(new Task(bar), 12);
work.start();
foo.waitForWorker(work);
foo.finishProcessing();
}
Contrasto con il seguente:
public void doSomething(Foo foo, Bar bar) {
foo.runTaskAtPriority(bar, 12);
}
Nel primo esempio, le classi Foo e MyClass sono strettamente accoppiate: MyClass non dipende solo da Foo , ma anche dalle dipendenze di Foo , come Worker e Task classi. Inoltre, MyClass dipende da interni molto specifici di Foo : deve chiamare diversi metodi in un certo ordine. È molto più probabile che la modifica di qualcosa in Foo richieda anche la modifica di MyClass .
Il secondo esempio dipende solo da Foo stesso. Pertanto, se Foo interrompe l'utilizzo di Worker internamente, MyClass non deve preoccuparsi. MyClass e Foo sono accoppiati liberamente.
Alla fine, è tutto sulla conoscenza: più è necessario conoscere una determinata classe / metodo per usarlo, più stretto è l'accoppiamento.
"A seconda delle astrazioni anziché delle concrezioni" è un modo comune per ottenere un accoppiamento basso. Ma l'introduzione di classi / interfacce astratte non riduce automaticamente l'accoppiamento: è piuttosto necessario pensare a come ridurre le possibili interazioni (conoscenze) che una classe ha con un'altra.