วิธีที่ DI สามารถนำไปใช้นั้นขึ้นอยู่กับภาษาที่ใช้
นี่เป็นตัวอย่างที่ไม่ง่าย:
class Foo {
private Bar bar;
private Qux qux;
public Foo() {
bar = new Bar();
qux = new Qux();
}
}
bar
นี้ครับเช่นเมื่อสำหรับการทดสอบผมต้องการที่จะใช้วัตถุจำลองสำหรับ ดังนั้นเราสามารถทำให้มีความยืดหยุ่นมากขึ้นและอนุญาตให้ส่งผ่านอินสแตนซ์ของ Constructor ได้:
class Foo {
private Bar bar;
private Qux qux;
public Foo(Bar bar, Qux qux) {
this.bar = bar;
this.qux = qux;
}
}
// in production:
new Foo(new Bar(), new Qux());
// in test:
new Foo(new BarMock(), new Qux());
นี่เป็นรูปแบบการฉีดที่ง่ายที่สุดแล้ว แต่สิ่งนี้ยังคงแย่อยู่เพราะทุกสิ่งต้องทำด้วยตนเอง (เช่นกันเพราะผู้โทรสามารถเก็บการอ้างอิงไปยังวัตถุภายในของเราและทำให้สถานะของเราเป็นโมฆะ)
เราสามารถแนะนำสิ่งที่เป็นนามธรรมมากขึ้นโดยใช้โรงงาน:
ทางเลือกหนึ่งสำหรับการFoo
สร้างโดยโรงงานนามธรรม
interface FooFactory {
public Foo makeFoo();
}
class ProductionFooFactory implements FooFactory {
public Foo makeFoo() { return new Foo(new Bar(), new Baz()) }
}
class TestFooFactory implements FooFactory {
public Foo makeFoo() { return new Foo(new BarMock(), new Baz()) }
}
FooFactory fac = ...; // depends on test or production
Foo foo = fac.makeFoo();
ตัวเลือกอื่นคือการส่งโรงงานไปยังตัวสร้าง:
interface DependencyManager {
public Bar makeBar();
public Qux makeQux();
}
class ProductionDM implements DependencyManager {
public Bar makeBar() { return new Bar() }
public Qux makeQux() { return new Qux() }
}
class TestDM implements DependencyManager {
public Bar makeBar() { return new BarMock() }
public Qux makeQux() { return new Qux() }
}
class Foo {
private Bar bar;
private Qux qux;
public Foo(DependencyManager dm) {
bar = dm.makeBar();
qux = dm.makeQux();
}
}
ปัญหาที่เหลืออยู่คือเราต้องเขียนDependencyManager
คลาสย่อยใหม่สำหรับการกำหนดค่าแต่ละครั้งและจำนวนการพึ่งพาที่สามารถจัดการได้นั้นค่อนข้าง จำกัด (การพึ่งพาใหม่แต่ละครั้งต้องใช้วิธีการใหม่ในอินเตอร์เฟส)
ด้วยคุณสมบัติเช่นภาพสะท้อนและการโหลดคลาสแบบไดนามิกเราสามารถหลีกเลี่ยงสิ่งนี้ได้ แต่สิ่งนี้ขึ้นอยู่กับภาษาที่ใช้ ใน Perl คลาสสามารถอ้างอิงโดยชื่อของพวกเขาและฉันก็ทำได้
package Foo {
use signatures;
sub new($class, $dm) {
return bless {
bar => $dm->{bar}->new,
qux => $dm->{qux}->new,
} => $class;
}
}
my $prod = { bar => 'My::Bar', qux => 'My::Qux' };
my $test = { bar => 'BarMock', qux => 'QuxMock' };
$test->{bar} = 'OtherBarMock'; # change conf at runtime
my $foo = Foo->new(rand > 0.5 ? $prod : $test);
ในภาษาอย่าง Java ฉันสามารถให้ตัวจัดการการพึ่งพาทำงานคล้ายกับMap<Class, Object>
:
Bar bar = dm.make(Bar.class);
คลาสที่แท้จริงได้รับการBar.class
แก้ไขสามารถกำหนดค่าที่รันไทม์เช่นโดยการรักษาMap<Class, Class>
อินเทอร์เฟซที่แมปไปยังการใช้งาน
Map<Class, Class> dependencies = ...;
public <T> T make(Class<T> c) throws ... {
// plus a lot more error checking...
return dependencies.get(c).newInstance();
}
ยังคงมีองค์ประกอบแบบแมนนวลที่เกี่ยวข้องในการเขียนนวกรรมิก แต่เราสามารถสร้างนวกรรมิกได้โดยไม่จำเป็นเช่นการขับ DI ผ่านคำอธิบายประกอบ:
class Foo {
@Inject(Bar.class)
private Bar bar;
@Inject(Qux.class)
private Qux qux;
...
}
dm.make(Foo.class); // takes care of initializing "bar" and "qux"
นี่คือตัวอย่างการใช้งานกรอบเล็ก ๆ DI (และถูก จำกัด มาก): http://ideone.com/b2ubuFแม้ว่าการใช้งานนี้จะไม่สามารถใช้ได้อย่างสมบูรณ์สำหรับวัตถุที่ไม่เปลี่ยนรูป (การใช้งานแบบไร้เดียงสานี้ไม่สามารถรับพารามิเตอร์ใด ๆ