[design-patterns] Cos'è l'iniezione di dipendenza?



Answers

La migliore definizione che ho trovato finora è una di James Shore :

"Dependency Injection" è un termine di 25 dollari per un concetto di 5 centesimi. [...] Iniezione di dipendenza significa dare a un oggetto le sue variabili di istanza. [...].

C'è anche un articolo di Martin Fowler che potrebbe rivelarsi utile.

L'iniezione di dipendenza fornisce fondamentalmente gli oggetti di cui un oggetto ha bisogno (le sue dipendenze) invece di costruirli da sé. È una tecnica molto utile per i test, poiché consente alle dipendenze di essere derise o eliminate.

Le dipendenze possono essere iniettate in oggetti con molti mezzi (come iniezione del costruttore o iniezione setter). Si può persino usare strutture specializzate per l'iniezione delle dipendenze (es. Spring) per farlo, ma certamente non sono richieste. Non hai bisogno di questi quadri per avere un'iniezione di dipendenza. Istanziare e passare oggetti (dipendenze) in modo esplicito è un'iniezione altrettanto buona di un'iniezione per quadro.

Question

Sono già state pubblicate diverse domande con domande specifiche sull'iniezione delle dipendenze , come quando usarlo e quali strutture ci sono per esso. Però,

Qual è l'iniezione di dipendenza e quando / perché dovrebbe o non dovrebbe essere utilizzata?




Dependency Injection is a type of implementation of the " Inversion of Control " principle on which is based Frameworks building.

Frameworks as stated in "Design Pattern" of GoF are classes that implement the main control flow logic raising the developer to do that, in this way Frameworks realize the inversion of control principle.

A way to implement as a technique, and not as class hierarchy, this IoC principle it is just Dependency Injection.

DI consists mainly into delegate the mapping of classes instances and type reference to that instances, to an external "entity": an object, static class, component, framework, etc...

Classes instances are the " dependencies ", the external binding of the calling component with the class instance through the reference it is the " injection ".

Obviously you can implement this technique in many way as you want from OOP point of view, see for example constructor injection , setter injection , interface injection .

Delegating a third party to carry out the task of match a ref to an object it is very useful when you want to completely separate a component that needs some services from the same services implementation.

In this way, when designing components, you can focus exclusively on their architecture and their specific logic, trusting on interfaces for collaborating with other objects without worry about any type of implementation changes of objects/services used, also if the same object you are using will be totally replaced (obviously respecting the interface).




Che cos'è Dipendenza Iniezione (DI)?

Come altri hanno già detto, Dipendenza Iniezione (DI) rimuove la responsabilità della creazione diretta e della gestione della durata della vita di altre istanze di oggetti su cui la nostra classe di interesse (classe del consumatore) dipende (nel senso di UML ). Queste istanze vengono invece passate alla nostra classe consumer, in genere come parametri del costruttore o tramite i setter di proprietà (la gestione dell'istanza di dipendenza che instanzia e passa alla classe consumer viene solitamente eseguita da un contenitore Inversion of Control (IoC) , ma questo è un altro argomento) .

DI, DIP e SOLID

Nello specifico, nel paradigma dei principi SOLID di Robert C Martin di Object Oriented Design , DI è una delle possibili implementazioni del principio di inversione di dipendenza (DIP) . Il DIP è il D del mantra SOLID - le altre implementazioni DIP includono il localizzatore di servizio e i modelli di plugin.

L'obiettivo del DIP è quello di disaccoppiare le dipendenze strette e concrete tra le classi e, invece, di sciogliere l'accoppiamento mediante un'astrazione, che può essere ottenuta tramite interface , abstract class o pure virtual class , a seconda della lingua e dell'approccio utilizzato .

Senza il DIP, il nostro codice (che ho chiamato questa "classe consumatrice") è direttamente accoppiato a una dipendenza concreta ed è spesso gravato dalla responsabilità di sapere come ottenere e gestire un'istanza di questa dipendenza, cioè concettualmente:

"I need to create/use a Foo and invoke method `GetBar()`"

Considerando che dopo l'applicazione del DIP, il requisito è allentato, e la preoccupazione di ottenere e gestire la durata della dipendenza Foo è stata rimossa:

"I need to invoke something which offers `GetBar()`"

Perché usare DIP (e DI)?

Il disaccoppiamento delle dipendenze tra classi in questo modo consente una facile sostituzione di queste classi di dipendenza con altre implementazioni che soddisfano anche i prerequisiti dell'astrazione (ad esempio, la dipendenza può essere cambiata con un'altra implementazione della stessa interfaccia). Inoltre, come altri hanno già accennato, probabilmente il motivo più comune per separare le classi tramite il DIP è consentire a una classe consumante di essere testata in isolamento, poiché queste stesse dipendenze possono ora essere soppresse e / o derise.

Una conseguenza di DI è che la gestione della durata delle istanze di oggetti di dipendenza non è più controllata da una classe di consumo, poiché l'oggetto di dipendenza viene ora passato nella classe di consumo (tramite la funzione di costruzione o l'iniezione di setter).

Questo può essere visualizzato in diversi modi:

  • Se è necessario mantenere il controllo della durata delle dipendenze da parte della classe di consumo, il controllo può essere ristabilito iniettando un factory (astratto) per la creazione delle istanze della classe di dipendenza, nella classe consumer. Il consumatore sarà in grado di ottenere istanze tramite una Create in fabbrica secondo le necessità e di disporre di tali istanze una volta completata.
  • Oppure, il controllo della durata di vita delle istanze di dipendenza può essere ceduto a un contenitore IoC (maggiori informazioni al riguardo qui sotto).

Quando usare DI?

  • Dove ci sarà probabilmente la necessità di sostituire una dipendenza per un'implementazione equivalente,
  • Qualsiasi momento in cui sarà necessario testare i metodi di una classe in isolamento delle sue dipendenze,
  • Dove l'incertezza della durata di vita di una dipendenza può giustificare una sperimentazione (per esempio, Hey, MyDepClass è sicuro per i thread - cosa succede se lo rendiamo un singleton e iniettiamo la stessa istanza in tutti i consumatori?)

Esempio

Ecco una semplice implementazione di C #. Data la classe di consumo sottostante:

public class MyLogger
{
   public void LogRecord(string somethingToLog)
   {
      Console.WriteLine("{0:HH:mm:ss} - {1}", DateTime.Now, somethingToLog);
   }
}

Sebbene apparentemente innocuo, ha due dipendenze static su due altre classi, System.DateTime e System.Console , che non limitano solo le opzioni di output di registrazione (la registrazione alla console sarà inutile se nessuno la sta guardando), ma peggio, è difficile per testare automaticamente data la dipendenza da un orologio di sistema non deterministico.

Tuttavia, possiamo applicare DIP a questa classe, MyLogger la preoccupazione di timestamping come dipendenza e accoppiando MyLogger solo a un'interfaccia semplice:

public interface IClock
{
    DateTime Now { get; }
}

Possiamo anche allentare la dipendenza dalla Console in un'astrazione, come ad esempio un TextWriter . L'iniezione di dipendenza viene in genere implementata come iniezione del constructor (passando un'astrazione a una dipendenza come parametro per il costruttore di una classe di consumo) o Setter Injection setXyz() passando la dipendenza tramite un setter setXyz() o una proprietà .Net con {set;} definito). È preferibile l'Injection del costruttore, in quanto garantisce che la classe si trovi in ​​uno stato corretto dopo la costruzione e consente di contrassegnare i campi di dipendenza interni come readonly (C #) o final (Java). Quindi, usando l'iniezione del costruttore nell'esempio sopra, questo ci lascia con:

public class MyLogger : ILogger // Others will depend on our logger.
{
    private readonly TextWriter _output;
    private readonly IClock _clock;

    // Dependencies are injected through the constructor
    public MyLogger(TextWriter stream, IClock clock)
    {
        _output = stream;
        _clock = clock;
    }

    public void LogRecord(string somethingToLog)
    {
        // We can now use our dependencies through the abstraction 
        // and without knowledge of the lifespans of the dependencies
        _output.Write("{0:yyyy-MM-dd HH:mm:ss} - {1}", _clock.Now, somethingToLog);
    }
}

(È necessario fornire un Clock concreto, che naturalmente potrebbe tornare a DateTime.Now , e le due dipendenze devono essere fornite da un contenitore IoC tramite l'iniezione del costruttore)

È possibile creare un Unit Test automatizzato, che dimostra definitivamente che il nostro logger funziona correttamente, poiché ora abbiamo il controllo sulle dipendenze - il tempo e possiamo spiare l'output scritto:

[Test]
public void LoggingMustRecordAllInformationAndStampTheTime()
{
    // Arrange
    var mockClock = new Mock<IClock>();
    mockClock.Setup(c => c.Now).Returns(new DateTime(2015, 4, 11, 12, 31, 45));
    var fakeConsole = new StringWriter();

    // Act
    new MyLogger(fakeConsole, mockClock.Object)
        .LogRecord("Foo");

    // Assert
    Assert.AreEqual("2015-04-11 12:31:45 - Foo", fakeConsole.ToString());
}

Prossimi passi

L'iniezione di dipendenza è invariabilmente associata a un contenitore Inversion of Control (IoC) , per iniettare (fornire) le istanze di dipendenza concreta e per gestire le istanze di durata. Durante il processo di configurazione / bootstrap, i contenitori IoC consentono di definire quanto segue:

  • mappatura tra ogni astrazione e l'implementazione concreta configurata (ad esempio "ogni volta che un consumatore richiede un IBar , restituisce un'istanza ConcreteBar " )
  • le politiche possono essere impostate per la gestione della vita di ogni dipendenza, ad esempio per creare un nuovo oggetto per ogni istanza di consumatore, per condividere un'istanza di dipendenza singleton tra tutti i consumatori, per condividere la stessa istanza di dipendenza solo attraverso lo stesso thread, ecc.
  • In .Net, i contenitori IoC sono a conoscenza di protocolli come IDisposable e assumono la responsabilità di Disposing dipendenze in linea con la gestione della durata della vita configurata.

Tipicamente, una volta che i contenitori IoC sono stati configurati / bootstrap, operano senza interruzioni in background consentendo al programmatore di concentrarsi sul codice in questione piuttosto che preoccuparsi delle dipendenze.

La chiave per il codice DI-friendly è evitare l'accoppiamento statico delle classi e non usare new () per la creazione di dipendenze

Come per l'esempio precedente, il disaccoppiamento delle dipendenze richiede uno sforzo progettuale, e per lo sviluppatore, è necessario uno spostamento di paradigma per rompere l'abitudine delle new dipendenze direttamente e affidarsi invece al contenitore per gestire le dipendenze.

Ma i benefici sono molti, specialmente nella capacità di testare accuratamente la vostra classe di interesse.

Nota : La creazione / mappatura / proiezione (tramite new ..() ) di POCO / POJO / DTO di serializzazione / Grafici di entità / Proiezioni JSON anonime e al - cioè "Solo dati" classi o record - utilizzati o restituiti da metodi non sono considerati come Dipendenze (nel senso di UML) e non soggetto a DI. Using new to project these is just fine.




Dependency injection is the heart of the concept related with Spring Framework.While creating the framework of any project spring may perform a vital role,and here dependency injection come in pitcher.

Actually,Suppose in java you created two different classes as class A and class B, and whatever the function are available in class B you want to use in class A, So at that time dependency injection can be used. where you can crate object of one class in other,in the same way you can inject an entire class in another class to make it accessible. by this way dependency can be overcome.

DEPENDENCY INJECTION IS SIMPLY GLUING TWO CLASSES AND AT THE SAME TIME KEEPING THEM SEPARATE.




from Book Apress.Spring.Persistence.with.Hibernate.Oct.2010

The purpose of dependency injection is to decouple the work of resolving external software components from your application business logic.Without dependency injection, the details of how a component accesses required services can get muddled in with the component's code. This not only increases the potential for errors, adds code bloat, and magnifies maintenance complexities; it couples components together more closely, making it difficult to modify dependencies when refactoring or testing.




The popular answers are unhelpful, because they define dependency injection in a way that isn't useful. Let's agree that by "dependency" we mean some pre-existing other object that our object X needs. But we don't say we're doing "dependency injection" when we say

$foo = Foo->new($bar);

We just call that passing parameters into the constructor. We've been doing that regularly ever since constructors were invented.

"Dependency injection" is considered a type of "inversion of control", which means that some logic is taken out of the caller. That isn't the case when the caller passes in parameters, so if that were DI, DI would not imply inversion of control.

DI means there is an intermediate level between the caller and the constructor which manages dependencies. A Makefile is a simple example of dependency injection. The "caller" is the person typing "make bar" on the command line, and the "constructor" is the compiler. The Makefile specifies that bar depends on foo, and it does a

gcc -c foo.cpp; gcc -c bar.cpp

before doing a

gcc foo.o bar.o -o bar

The person typing "make bar" doesn't need to know that bar depends on foo. The dependency was injected between "make bar" and gcc.

The main purpose of the intermediate level is not just to pass in the dependencies to the constructor, but to list all the dependencies in just one place , and to hide them from the coder (not to make the coder provide them).

Usually the intermediate level provides factories for the constructed objects, which must provide a role that each requested object type must satisfy. That's because by having an intermediate level that hides the details of construction, you've already incurred the abstraction penalty imposed by factories, so you might as well use factories.




Dipendenza L'iniezione è una pratica in cui gli oggetti sono progettati in modo da ricevere istanze degli oggetti da altri pezzi di codice, invece di costruirli internamente. Ciò significa che qualsiasi oggetto che implementa l'interfaccia richiesta dall'oggetto può essere sostituito senza modificare il codice, il che semplifica il test e migliora il disaccoppiamento.

Ad esempio, considera questi clases:

public class PersonService {
  public void addManager( Person employee, Person newManager ) { ... }
  public void removeManager( Person employee, Person oldManager ) { ... }
  public Group getGroupByManager( Person manager ) { ... }
}

public class GroupMembershipService() {
  public void addPersonToGroup( Person person, Group group ) { ... }
  public void removePersonFromGroup( Person person, Group group ) { ... }
} 

In questo esempio, l'implementazione di PersonService::addManager e PersonService::removeManager richiederebbe un'istanza di GroupMembershipService per poter funzionare. Senza Dependency Injection, il modo tradizionale per farlo sarebbe istanziare un nuovo GroupMembershipService nel costruttore di PersonService e usare PersonService istanza in entrambe le funzioni. Tuttavia, se il costruttore di GroupMembershipService ha più cose che richiede, o peggio ancora, ci sono alcuni "setter" di inizializzazione che devono essere chiamati su GroupMembershipService , il codice cresce piuttosto rapidamente e il PersonService ora dipende non solo da GroupMembershipService ma anche tutto ciò che dipende da GroupMembershipService . Inoltre, il collegamento a GroupMembershipService è codificato in PersonService che significa che non è possibile " PersonService " un GroupMembershipService a scopo di test o utilizzare un modello di strategia in diverse parti dell'applicazione.

Con Dependency Injection, invece di PersonService un'istanza di PersonService all'interno di PersonService , devi passarlo al costruttore PersonService , oppure aggiungere una proprietà (getter e setter) per impostarne un'istanza locale. Ciò significa che il tuo PersonService non deve più preoccuparsi di come creare un GroupMembershipService , accetta solo quelli forniti e lavora con loro. Ciò significa anche che qualsiasi cosa che è una sottoclasse di GroupMembershipService o che implementa l'interfaccia GroupMembershipService può essere "iniettata" in PersonService e che PersonService non deve conoscere la modifica.




Dependency Injection means a way (actually any-way ) for one part of code (eg a class) to have access to dependencies (other parts of code, eg other classes, it depends upon) in a modular way without them being hardcoded (so they can change or be overriden freely, or even be loaded at another time, as needed)

(and ps , yes it has become an overly-hyped 25$ name for a rather simple, concept) , my .25 cents




Any nontrivial application is made up of two or more classes that collaborate with each other to perform some business logic. Traditionally, each object is responsible for obtaining its own references to the objects it collaborates with (its dependencies). When applying DI, the objects are given their dependencies at creation time by some external entity that coordinates each object in the system. In other words, dependencies are injected into objects.

For further details please see enter link description here




Proviamo un semplice esempio con le classi di auto e motori , qualsiasi auto ha bisogno di un motore per andare ovunque, almeno per ora. Quindi sotto come apparirà il codice senza iniezione di dipendenza.

public class Car
{
    public Car()
    {
        GasEngine engine = new GasEngine();
        engine.Start();
    }
}

public class GasEngine
{
    public void Start()
    {
        Console.WriteLine("I use gas as my fuel!");
    }
}

E per istanziare la classe Car useremo il prossimo codice:

Car car = new Car();

Il problema con questo codice che abbiamo strettamente associato a GasEngine e se decidiamo di cambiarlo in ElectricityEngine, allora dovremo riscrivere la classe Car. E più grande è l'applicazione, più problemi e mal di testa dovremo aggiungere e usare un nuovo tipo di motore.

In altre parole, con questo approccio, la nostra classe Car di alto livello dipende dalla classe GasEngine di livello inferiore che viola il principio di inversione delle dipendenze (DIP) da SOLID. Il DIP suggerisce che dovremmo dipendere dalle astrazioni, non dalle lezioni concrete. Quindi per soddisfare questo, introduciamo l'interfaccia IEngine e riscriviamo il codice come di seguito:

    public interface IEngine
    {
        void Start();
    }

    public class GasEngine : IEngine
    {
        public void Start()
        {
            Console.WriteLine("I use gas as my fuel!");
        }
    }

    public class ElectricityEngine : IEngine
    {
        public void Start()
        {
            Console.WriteLine("I am electrocar");
        }
    }

    public class Car
    {
        private readonly IEngine _engine;
        public Car(IEngine engine)
        {
            _engine = engine;
        }

        public void Run()
        {
            _engine.Start();
        }
    }

Ora la nostra classe Car dipende solo dall'interfaccia IEngine, non da un'implementazione specifica del motore. Ora, l'unico trucco è come creare un'istanza della macchina e dargli una classe concreta di motore come GasEngine o ElectricityEngine. Ecco dove entra in gioco l' iniezione di dipendenza .

   Car gasCar = new Car(new GasEngine());
   gasCar.Run();
   Car electroCar = new Car(new ElectricityEngine());
   electroCar.Run();

Qui fondamentalmente iniettiamo (passiamo) la nostra dipendenza (istanza del motore) al costruttore di automobili. Così ora le nostre classi hanno un accoppiamento lento tra gli oggetti e le loro dipendenze, e possiamo facilmente aggiungere nuovi tipi di motori senza cambiare la classe Car.

Il principale vantaggio di Dependency Injection è che le classi sono più liberamente accoppiate, perché non hanno dipendenze hard-coded. Questo segue il principio di inversione della dipendenza, che è stato menzionato sopra. Invece di fare riferimento a specifiche implementazioni, le classi richiedono le astrazioni (di solito le interfacce ) che vengono fornite loro quando la classe è costruita.

Quindi alla fine l' iniezione di dipendenza è solo una tecnica per ottenere un accoppiamento lento tra gli oggetti e le loro dipendenze. Anziché creare direttamente delle dipendenze di cui la classe ha bisogno per eseguire le proprie azioni, le dipendenze vengono fornite alla classe (il più delle volte) tramite l'iniezione del costruttore.

Anche quando abbiamo molte dipendenze è molto buona pratica usare i contenitori di Inversion of Control (IoC) che possiamo dire quali interfacce devono essere mappate su quali implementazioni concrete per tutte le nostre dipendenze e possiamo far sì che risolva quelle dipendenze per noi quando costruisce il nostro oggetto. Ad esempio, potremmo specificare nella mappatura per il contenitore IoC che la dipendenza IEngine debba essere mappata alla classe GasEngine e quando chiediamo al contenitore IoC un'istanza della nostra classe Car , essa costruirà automaticamente la nostra classe Car con una dipendenza GasEngine passato dentro

AGGIORNAMENTO: Ho assistito recentemente a un corso su EF Core da Julie Lerman e mi è anche piaciuta la sua breve definizione su DI.

L'iniezione di dipendenza è uno schema che consente alla tua applicazione di iniettare oggetti al volo per le classi che ne hanno bisogno, senza forzare le classi a essere responsabili di tali oggetti. Consente al codice di essere più liberamente associato e Entity Framework Core si collega a questo stesso sistema di servizi.




Immaginiamo che tu voglia andare a pescare:

  • Senza l'iniezione di dipendenza, è necessario prendersi cura di tutto da soli. Devi trovare una barca, comprare una canna da pesca, cercare esche, ecc. È possibile, certo, ma ti mette molta responsabilità. In termini di software, significa che devi eseguire una ricerca per tutte queste cose.

  • Con l'iniezione di dipendenza, qualcun altro si occupa di tutta la preparazione e mette a disposizione l'attrezzatura necessaria. Riceverai ("essere iniettato") la barca, la canna da pesca e l'esca - tutto pronto per l'uso.




I think since everyone has written for DI, let me ask a few questions..

  1. When you have a configuration of DI where all the actual implementations(not interfaces) that are going to be injected into a class (for eg services to a controller) why is that not some sort of hard-coding?
  2. What if I want to change the object at runtime? For example, my config already says when I instantiate MyController, inject for FileLogger as ILogger. But I might want to inject DatabaseLogger.
  3. Everytime I want to change what objects my AClass needs, I need to now look into two places - The class itself and the configuration file. How does that make life easier?
  4. If Aproperty of AClass is not injected, is it harder to mock it out?
  5. Going back to the first question. If using new object() is bad, how come we inject the implementation and not the interface? I think a lot of you are saying we're in fact injecting the interface but the configuration makes you specify the implementation of that interface ..not at runtime .. it is hardcoded during compile time.

This is based on the answer @Adam N posted.

Why does PersonService no longer have to worry about GroupMembershipService? You just mentioned GroupMembership has multiple things(objects/properties) it depends on. If GMService was required in PService, you'd have it as a property. You can mock that out regardless of whether you injected it or not. The only time I'd like it to be injected is if GMService had more specific child classes, which you wouldn't know until runtime. Then you'd want to inject the subclass. Or if you wanted to use that as either singleton or prototype. To be honest, the configuration file has everything hardcoded as far as what subclass for a type (interface) it is going to inject during compile time.

MODIFICARE

A nice comment by Jose Maria Arranz on DI

DI increases cohesion by removing any need to determine the direction of dependency and write any glue code.

False. The direction of dependencies is in XML form or as annotations, your dependencies are written as XML code and annotations. XML and annotations ARE source code.

DI reduces coupling by making all of your components modular (ie replacable) and have well-defined interfaces to each other.

False. You do not need a DI framework to build a modular code based on interfaces.

About replaceable: with a very simple .properties archive and Class.forName you can define wich classes can change. If ANY class of your code can be changed, Java is not for you, use an scripting language. By the way: annotations cannot be changed without recompiling.

In my opinion there is one only reason for DI frameworks: boiler plate reduction. With a well done factory system you can do the same, more controlled and more predictable as your preferred DI framework, DI frameworks promise code reduction (XML and annotations are source code too). The problem is this boiler plate reduction is just real in very very simple cases (one instance-per class and similar), sometimes in the real world picking the appropriated service object is not as easy as mapping a class to a singleton object.







The best analogy I can think of is the surgeon and his assistant(s) in an operation theater, where the surgeon is the main person and his assistant who provides the various surgical components when he needs it so that the surgeon can concentrate on the one thing he does best (surgery). Without the assistant the surgeon has to get the components himself every time he needs one.

DI for short, is a technique to remove a common additional responsibility (burden) on components to fetch the dependent components, by providing them to it.

DI brings you closer to the Single Responsibility (SR) principle, like the surgeon who can concentrate on surgery .

When to use DI : I would recommend using DI in almost all production projects ( small/big), particularly in ever changing business environments :)

Why : Because you want your code to be easily testable, mockable etc so that you can quickly test your changes and push it to the market. Besides why would you not when you there are lots of awesome free tools/frameworks to support you in your journey to a codebase where you have more control.




The whole point of Dependency Injection (DI) is to keep application source code clean and stable :

  • clean of dependency initialization code
  • stable regardless of dependency used

Practically, every design pattern separates concerns to make future changes affect minimum files.

The specific domain of DI is delegation of dependency configuration and initialization.

Example: DI with shell script

If you occasionally work outside of Java, recall how source is often used in many scripting languages (Shell, Tcl, etc., or even import in Python misused for this purpose).

Consider simple dependent.sh script:

#!/bin/sh
# Dependent
touch         "one.txt" "two.txt"
archive_files "one.txt" "two.txt"

The script is dependent: it won't execute successfully on its own ( archive_files is not defined).

You define archive_files in archive_files_zip.sh implementation script (using zip in this case):

#!/bin/sh
# Dependency
function archive_files {
    zip files.zip "$@"
}

Instead of source -ing implementation script directly in the dependent one, you use an injector.sh "container" which wraps both "components":

#!/bin/sh 
# Injector
source ./archive_files_zip.sh
source ./dependent.sh

The archive_files dependency has just been injected into dependent script.

You could have injected dependency which implements archive_files using tar or xz .

Example: removing DI

If dependent.sh script used dependencies directly, the approach would be called dependency lookup (which is opposite to dependency injection ):

#!/bin/sh
# Dependent

# dependency look-up
source ./archive_files_zip.sh

touch         "one.txt" "two.txt"
archive_files "one.txt" "two.txt"

Now the problem is that dependent "component" has to perform initialization itself.

The "component"'s source code is neither clean nor stable because every changes in initialization of dependencies requires new release for "components"'s source code file as well.

Last words

DI is not as largely emphasized and popularized as in Java frameworks.

But it's a generic approach to split concerns of:

  • application development ( single source code release lifecycle)
  • application deployment ( multiple target environments with independent lifecycles)

Using configuration only with dependency lookup does not help as number of configuration parameters may change per dependency (eg new authentication type) as well as number of supported types of dependencies (eg new database type).




Related