Content deleted Content added
→Assembly: Minor improvements Tags: Mobile edit Mobile web edit |
m →C++: fix typo: wrong type was used for the member variable, code wouldn't compile |
||
(10 intermediate revisions by 6 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 11, no. 1 (April 2012), pp. 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 – 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
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
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 78:
== Types of dependency injection ==
There are
* Constructor injection, where dependencies are provided through a client's class [[Constructor (object-oriented programming)|constructor]].
* Method Injection, where dependencies are provided to a method only when required for specific functionality.
* Setter injection, where the client exposes a setter method which accepts the dependency.
* Interface injection, where the dependency's interface provides an injector method that will inject the dependency into any client passed to it.
Line 113 ⟶ 114:
}
}
</syntaxhighlight>
=== Method Injection ===
Dependencies are passed as arguments to a specific method, allowing them to be used only during that method's execution without maintaining a long-term reference. This approach is particularly useful for temporary dependencies or when different implementations are needed for various method calls.
<syntaxhighlight lang="java">
public class Client {
public void performAction(Service service) {
if (service == null) {
throw new IllegalArgumentException("service must not be null");
}
service.execute();
}
}
</syntaxhighlight>
Line 280 ⟶ 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 289 ⟶ 389:
// Our client will only know about this interface, not which specific gamepad it is using.
interface
{
string GetGamePadName();
void SetVibrationPower(float power);
}
Line 296 ⟶ 397:
// The following services provide concrete implementations of the above interface.
class
{
float vibrationPower = 1.0f;
public string
public void SetVibrationPower(float power) => this.vibrationPower = Math.Clamp(power, 0.0f, 1.0f);
}
class PlayStationJoystick : IGamePadFunctionality
{
float vibratingPower = 100.0f;
public string
public void SetVibrationPower(float power) => this.vibratingPower = Math.Clamp(power * 100.0f, 0.0f, 100.0f);
}
class SteamController :
{
double vibrating = 1.0;
public string
public void SetVibrationPower(float power) => this.vibrating = Convert.ToDouble(Math.Clamp(power, 0.0f, 1.0f));
Line 321 ⟶ 425:
// This class is the client which receives a service.
class
{
IGamePadFunctionality gamePadFunctionality;
// The service is injected through the constructor and stored in the above field.
public
public void Showcase()
{
// The injected service is used.
Console.WriteLine(message);
}
Line 336 ⟶ 442:
class Program {
static void Main(string[] args)
{
SteamController steamController = new SteamController();
// We could have also passed in an XboxController,
// The gamepad doesn't know what it's using and doesn't need to.
gamepad.Showcase();
Line 496 ⟶ 603:
* [[Factory pattern]]
* [[Inversion of control]]
* [[Mock trainwreck]]
* [[Plug-in (computing)]]
* [[Strategy pattern]]
|