Iniezione di dipendenza qualificata e accoppiamento

1

Molti framework java DI (Spring, CDI, tra gli altri) implementano una sorta di iniezione qualificata attraverso annotazioni di qualifica progettate dall'utente.

interface Engine { .. }

@Powerful
public PowerfulEngine implements Engine { .. }

public class Car {
    @Inject @Powerful
    private Engine engine;  // will inject a PowerfulEngine instance
}

E questo è pubblicizzato come un modo per abilitare accoppiamento libero tra gli oggetti. Ma poiché nel progetto è consentita una sola implementazione di @Powerful , non capisco quanto questo sia meno accoppiato di questo:

public class Car {
    @Inject
    private PowerfulEngine engine;  
}

Riesco a vedere come sarebbe vagamente più bello per deridere l'interfaccia invece di oggetto durante la scrittura di un test unitario, resta il fatto che si specifica il tipo esatto di implementazione necessario per l'iniezione.

Mi manca qualcosa?

    
posta fabienbk 16.02.2017 - 11:22
fonte

1 risposta

2

Nel tuo primo esempio stai utilizzando un attributo Engine nella tua classe Car . Il Engine è esposto attraverso un'interfaccia e come tale dipende dalla dichiarazione dell'interfaccia. Usando le annotazioni ti stai chiedendo un'implementazione dell'interfaccia Engine chiamata Powerful , per far sapere al compilatore quale implementazione esatta dell'interfaccia vuoi usare in questo caso specifico, ma il compilatore Java non ti permetterà ancora di utilizzare qualsiasi metodo al di fuori della dichiarazione dell'interfaccia.

Com'è il primo esempio meno abbinato?

Se consideriamo uno scenario in cui PowerfulEngine contiene solo metodi che l'interfaccia Engine fa, cioè che la classe stessa non aggiunge alcun metodo, l'accoppiamento è esattamente lo stesso in entrambi i casi, tuttavia una volta che si inizia ad aggiungere uno specifico metodi per la classe PowerfulEngine e utilizzandoli la situazione è molto diversa.

Per spiegazioni estendiamo un po 'il tuo esempio:

interface Engine
{
    void run();
}

class PowerfulEngine implements Engine
{
    @Override
    public void run() {
        System.out.println("Running a powerful engine.");
    }
}

class RegularEngine implements Engine
{
    @Override
    public void run() {
        System.out.println("Running a regular engine.");
    }
}

class Car
{
    private Engine engine;

    public void go() {
        engine.run();
    }
}

La classe Car ha ottenuto un metodo go che chiama il metodo run di Engine . Ora, a seconda della classe che è stata iniettata in un oggetto Car , il sistema emetterà Esecuzione di un motore potente o Esecuzione di un motore normale.

La classe Car sa che può utilizzare il metodo run di Engine perché lo fornisce e non si preoccupa della sua effettiva implementazione. Come ho detto, se decidi di sostituire la dipendenza Engine con la dipendenza PowerfulEngine , in questo caso non accade nulla perché stai ancora utilizzando solo il metodo dichiarato nell'interfaccia Engine .

Ma se aggiungi un altro metodo alla classe PowerfulEngine e lo usi, è un caso molto diverso.

Modifichiamo un po 'le classi PowerfulEngine e Car .

class PowerfulEngine implements Engine
{
    @Override
    public void run() {
        System.out.println("Running a powerful engine.");
    }

    public void rev() {
        System.out.println("Revving a powerful engine.");
    }
}

class Car
{
    private PowerfulEngine engine;

    public void go() {
        engine.rev();
        engine.run();
    }
}

Che cosa succederebbe ora se decidessi di utilizzare RegularEngine anziché PowerfulEngine ? La tua applicazione fallirebbe durante la compilazione perché la classe RegularEngine non ha il metodo rev , nemmeno l'interfaccia Engine . Dovresti accedere a un metodo che non esiste.

Tutto sommato, se si dipende da una classe concreta e si utilizzano solo i metodi disponibili in una determinata interfaccia implementata dalla classe, è meglio fare affidamento sull'interfaccia anziché sulla classe direttamente, consentendo così ad altri di sapere esplicitamente: < > Ehi, in realtà non dipende dall'implementazione, non me ne importa, mi interessa solo che l'interfaccia contenga questi contratti con metodi e li posso usare.

Se hai bisogno di utilizzare un metodo specifico di una classe che non è pubblicato attraverso alcuna astrazione, non hai altra scelta che affidarti a un'implementazione concreta.

    
risposta data 16.02.2017 - 12:21
fonte

Leggi altre domande sui tag