Dependency injection: Difference between revisions

Content deleted Content added
Xilef97 (talk | contribs)
m C++: fix typo: wrong type was used for the member variable, code wouldn't compile
 
(4 intermediate revisions by 2 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 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 GetGamepadNameGetGamePadName();
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) {
{
varSteamController 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();