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.