Dependency injection: Difference between revisions

Content deleted Content added
Kupiakos (talk | contribs)
m Types of dependency injection: avoid referencing a specific (currently inconsistent) number of mechanisms
Xilef97 (talk | contribs)
m C++: fix typo: wrong type was used for the member variable, code wouldn't compile
 
(6 intermediate revisions by 4 users not shown)
Line 4:
In [[software engineering]], '''dependency injection''' is a programming technique in which an [[Object (computer science)|object]] or [[Subroutine|function]] receives other objects or functions that it requires, as opposed to creating them internally. Dependency injection aims to [[separation of concerns|separate the concerns]] of constructing objects and using them, leading to [[Loose coupling|loosely coupled]] programs.<ref>{{Cite web |last=Seemann |first=Mark |title=Dependency Injection is Loose Coupling |url=http://blog.ploeh.dk/2010/04/07/DependencyInjectionisLooseCoupling/ |access-date=2015-07-28 |website=blog.ploeh.dk}}</ref><ref name="MarkSeeman2011P4" /><ref>Niko Schwarz, Mircea Lungu, Oscar Nierstrasz, "Seuss: Decoupling responsibilities from static methods for fine-grained configurability", Journal of Object Technology, volume&nbsp;11, no.&nbsp;1 (April 2012), pp.&nbsp;3:1–23.</ref> The pattern ensures that an object or function that wants to use a given [[Service (systems architecture)|service]] should not have to know how to construct those services. Instead, the receiving "[[Client (computing)|client]]" (object or function) is provided with its dependencies by external code (an "injector"), which it is not aware of.<ref name="HollywoodPrinciple.c2">{{Cite web |title=HollywoodPrinciple |url=http://c2.com/cgi/wiki?HollywoodPrinciple |access-date=2015-07-19 |website=c2.com}}</ref> Dependency injection makes implicit dependencies explicit and helps solve the following problems:<ref>{{cite web |title=The Dependency Injection design pattern&nbsp;– Problem, Solution, and Applicability |url=http://w3sdesign.com/?gr=u01&ugr=proble |access-date=2017-08-12 |website=w3sDesign.com}}</ref>
* How can a [[Class (computer programming)|class]] be independent from the creation of the objects it depends on?
* How can an application, and the objects it uses support different configurations?
Dependency injection is often used to keep code in-line with the [[dependency inversion principle]].<ref>{{Cite web |last=Erez |first=Guy |date=2022-03-09 |title=Dependency Inversion vs. Dependency Injection |url=https://betterprogramming.pub/straightforward-simple-dependency-inversion-vs-dependency-injection-7d8c0d0ed28e |access-date=2022-12-06 |website=Medium |language=en}}</ref><ref>{{Cite web |last=Mathews |first=Sasha |date=2021-03-25 |title=You are Simply Injecting a Dependency, Thinking that You are Following the Dependency Inversion... |url=https://levelup.gitconnected.com/you-are-simply-injecting-a-dependency-thinking-that-you-are-following-the-dependency-inversion-32632954c208 |access-date=2022-12-06 |website=Medium |language=en }}</ref>
 
Line 43:
 
=== Analogy ===
As an analogy, [[car]]s can be thought of as services which perform the useful work of transporting people from one place to another. Car engines can require [[Gasoline|gas]], [[Diesel fuel|diesel]] or [[Electric car|electricity]], but this detail is unimportant to the client—a driver—whopassenger—who only cares if it can get them to their destination.
 
Cars present a uniform interface through their pedals, steering wheels and other controls. As such, which engine they were 'injected' with on the factory line ceases to matter and drivers can switch between any kind of car as needed.
Line 295:
 
The <code>ng-controller</code> directive triggers the injector to create an instance of the controller and its dependencies.
 
=== C++ ===
This sample provides an example of constructor injection in [[C++]].
<syntaxhighlight lang="c++">
import std;
 
class DatabaseConnection {
public:
void connect() {
std::println("Connecting to database...");
}
};
 
class DatabaseService {
private:
DatabaseConnection& dbConn;
public:
explicit DatabaseService(DatabaseConnection& db):
dbConn{db} {}
 
void execute() {
dbConn.connect();
std::println("Executing database service...");
}
};
 
int main(int argc, char* argv[]) {
DatabaseConnection db;
DatabaseService sv(db);
sv.execute();
}
</syntaxhighlight>
 
This sample provides an example of interface injection in C++.
<syntaxhighlight lang="c++">
import std;
 
enum class DatabaseConnectionError {
NoConnection,
// more errors here
};
 
class IConnection {
public:
virtual void connect() = 0;
virtual ~IConnection() = default;
};
 
class DatabaseConnection: public IConnection {
public:
DatabaseConnection() = default;
 
void connect() override {
std::println("Connecting to database...");
}
};
 
class DatabaseService {
private:
std::shared_ptr<IConnection> conn;
public:
DatabaseService() = default;
 
void setConnection(std::shared_ptr<IConnection> nextConn) noexcept {
conn = nextConn;
}
 
std::expected<void, DatabaseConnectionError> execute() {
if (conn) {
conn->connect();
std::println("Executing database service...");
} else {
return std::unexpected(DatabaseConnectionError::NoConnection);
}
}
};
 
int main(int argc, char* argv[]) {
std::shared_ptr<DatabaseConnection> db = std::make_shared<DatabaseConnection>();
DatabaseService sv;
sv.setConnection(db);
sv.execute();
}
</syntaxhighlight>
 
=== C# ===
Line 304 ⟶ 389:
 
// Our client will only know about this interface, not which specific gamepad it is using.
interface IGamepadFunctionalityIGamePadFunctionality {
{
string GetGamepadName();
string GetGamePadName();
void SetVibrationPower(float power);
}
Line 311 ⟶ 397:
// The following services provide concrete implementations of the above interface.
 
class XboxGamepadXboxGamePad : IGamepadFunctionalityIGamePadFunctionality {
{
float vibrationPower = 1.0f;
public string GetGamepadNameGetGamePadName() => "Xbox controller";
public void SetVibrationPower(float power) => this.vibrationPower = Math.Clamp(power, 0.0f, 1.0f);
}
 
class PlayStationJoystick : IGamePadFunctionality
class PlaystationJoystick : IGamepadFunctionality {
{
float vibratingPower = 100.0f;
public string GetGamepadNameGetGamePadName() => "PlayStation controller";
public void SetVibrationPower(float power) => this.vibratingPower = Math.Clamp(power * 100.0f, 0.0f, 100.0f);
}
 
class SteamController : IGamepadFunctionalityIGamePadFunctionality {
{
double vibrating = 1.0;
public string GetGamepadNameGetGamePadName() => "Steam controller";
public void SetVibrationPower(float power) => this.vibrating = Convert.ToDouble(Math.Clamp(power, 0.0f, 1.0f));
Line 336 ⟶ 425:
 
// This class is the client which receives a service.
class Gamepad {GamePad
{
IGamepadFunctionality gamepadFunctionality;
IGamePadFunctionality gamePadFunctionality;
 
// The service is injected through the constructor and stored in the above field.
public GamepadGamePad(IGamepadFunctionalityIGamePadFunctionality gamepadFunctionalitygamePadFunctionality) => this.gamepadFunctionalitygamePadFunctionality = gamepadFunctionalitygamePadFunctionality;
 
public void Showcase() {
{
// The injected service is used.
varstring gamepadNamegamePadName = this.gamepadFunctionalitygamePadFunctionality.GetGamepadNameGetGamePadName();
varstring message = $"We're using the {gamepadNamegamePadName} right now, do you want to change the vibrating power?";
Console.WriteLine(message);
}
Line 351 ⟶ 442:
 
class Program {
static void Main(string[] args) {
{
var steamController = new SteamController();
SteamController steamController = new SteamController();
// We could have also passed in an XboxController, PlaystationJoystickPlayStationJoystick, etc.
// The gamepad doesn't know what it's using and doesn't need to.
varGamePad gamepad = new GamepadGamePad(steamController);
gamepad.Showcase();
Line 511 ⟶ 603:
* [[Factory pattern]]
* [[Inversion of control]]
* [[Mock trainwreck]]
* [[Plug-in (computing)]]
* [[Strategy pattern]]