มีคำถามมากมายที่โพสต์ไว้แล้วพร้อมคำถามเฉพาะเกี่ยวกับการฉีดพึ่งพาเช่นเมื่อไรที่จะใช้มันและกรอบใดบ้างที่มีให้ อย่างไรก็ตาม
การฉีดพึ่งพาคืออะไรและเมื่อใด / ทำไมควรหรือไม่ควรใช้?
มีคำถามมากมายที่โพสต์ไว้แล้วพร้อมคำถามเฉพาะเกี่ยวกับการฉีดพึ่งพาเช่นเมื่อไรที่จะใช้มันและกรอบใดบ้างที่มีให้ อย่างไรก็ตาม
การฉีดพึ่งพาคืออะไรและเมื่อใด / ทำไมควรหรือไม่ควรใช้?
คำตอบ:
การพึ่งพาการฉีดจะผ่านการอ้างอิงไปยังวัตถุหรือกรอบงานอื่น ๆ(หัวฉีดพึ่งพา)
การฉีดพึ่งพาทำให้การทดสอบง่ายขึ้น ฉีดสามารถทำได้ผ่านคอนสตรัค
SomeClass()
มีนวกรรมิกดังนี้
public SomeClass() {
myObject = Factory.getObject();
}
ปัญหา : ถ้าmyObject
เกี่ยวข้องกับงานที่ซับซ้อนเช่นการเข้าถึงดิสก์หรือการเข้าถึงเครือข่ายมันเป็นเรื่องยากSomeClass()
ที่จะทำแบบทดสอบหน่วย โปรแกรมเมอร์ต้องล้อเลียนmyObject
และอาจขัดขวางการโทรจากโรงงาน
ทางเลือกอื่น ๆ :
myObject
เป็นอาร์กิวเมนต์ไปยังตัวสร้างpublic SomeClass (MyClass myObject) {
this.myObject = myObject;
}
myObject
สามารถส่งผ่านโดยตรงซึ่งทำให้การทดสอบง่ายขึ้น
มันยากที่จะแยกส่วนประกอบในการทดสอบหน่วยโดยไม่ต้องฉีดพึ่งพา
ในปี 2013 เมื่อผมเขียนคำตอบนี้นี้เป็นเรื่องสำคัญในบล็อกของ Google ทดสอบ มันยังคงเป็นข้อได้เปรียบที่ใหญ่ที่สุดสำหรับฉันเนื่องจากโปรแกรมเมอร์ไม่จำเป็นต้องมีความยืดหยุ่นเป็นพิเศษในการออกแบบรันไทม์ (ตัวอย่างเช่นสำหรับตัวระบุบริการหรือรูปแบบที่คล้ายกัน) โปรแกรมเมอร์มักจะต้องแยกชั้นเรียนในระหว่างการทดสอบ
คำจำกัดความที่ดีที่สุดที่ฉันเคยพบมาคือJames ฝั่ง :
"การพึ่งพาการฉีด" เป็นคำที่ 25 ดอลลาร์สำหรับแนวคิดร้อยละ 5 [... ] การพึ่งพาการฉีดหมายถึงการให้วัตถุตัวแปรอินสแตนซ์ของมัน [ ... ]
มีบทความโดย Martin Fowlerที่อาจพิสูจน์ว่ามีประโยชน์เช่นกัน
โดยทั่วไปการพึ่งพาการฉีดให้วัตถุที่วัตถุต้องการ (การอ้างอิง) แทนที่จะสร้างมันเอง มันเป็นเทคนิคที่มีประโยชน์มากสำหรับการทดสอบเพราะมันช่วยให้การพึ่งพาถูกเยาะเย้ยหรือถูกขัดจังหวะ
การพึ่งพาสามารถฉีดเข้าไปในวัตถุได้หลายวิธี (เช่นการฉีดคอนสตรัคเตอร์หรือการฉีดเซทเทอร์) เราสามารถใช้กรอบการฉีดแบบพึ่งพา (เช่นสปริง) เพื่อทำสิ่งนั้น แต่พวกเขาไม่จำเป็นต้องใช้แน่นอน คุณไม่จำเป็นต้องมีกรอบการทำงานเหล่านี้เพื่อทำการฉีด การสร้างอินสแตนซ์และการผ่านวัตถุ (การพึ่งพา) ชัดเจนว่าเป็นการฉีดที่ดีเหมือนการฉีดตามกรอบ
ฉันพบตัวอย่างที่ตลกนี้ในแง่ของการมีเพศสัมพันธ์แบบหลวม :
แอปพลิเคชันใด ๆ ที่ประกอบด้วยวัตถุจำนวนมากที่ทำงานร่วมกันเพื่อทำสิ่งที่มีประโยชน์ ตามเนื้อผ้าแต่ละวัตถุมีหน้าที่รับผิดชอบในการได้รับการอ้างอิงของตัวเองไปยังวัตถุที่อ้างถึง (อ้างอิง) มันร่วมมือกับ สิ่งนี้นำไปสู่คลาสที่มีความสัมพันธ์สูงและรหัสที่ยากต่อการทดสอบ
ตัวอย่างเช่นพิจารณาCar
วัตถุ
Car
ขึ้นอยู่กับล้อเครื่องยนต์, น้ำมัน, แบตเตอรี่ ฯลฯ เพื่อให้ทำงานได้ ตามเนื้อผ้าเรากำหนดแบรนด์ของวัตถุดังกล่าวพร้อมกับคำจำกัดความของCar
วัตถุ
โดยไม่ต้องพึ่งพาการฉีด (DI):
class Car{
private Wheel wh = new NepaliRubberWheel();
private Battery bt = new ExcideBattery();
//The rest
}
ที่นี่Car
วัตถุมีหน้าที่สร้างวัตถุที่ต้องพึ่งพา
จะเป็นอย่างไรถ้าเราต้องการเปลี่ยนประเภทของวัตถุที่อ้างถึง - พูดWheel
- หลังจากการNepaliRubberWheel()
เจาะเริ่มต้น? เราจำเป็นต้องสร้างวัตถุรถยนต์ใหม่ด้วยการพึ่งพาใหม่บอกChineseRubberWheel()
แต่เฉพาะCar
ผู้ผลิตเท่านั้นที่ทำได้
ถ้าDependency Injection
เช่นนั้นเราจะทำอะไร ... ?
เมื่อใช้ฉีดพึ่งพาวัตถุจะได้รับการอ้างอิงของพวกเขาในเวลาทำงานมากกว่ารวบรวมเวลา (รถผลิตเวลา) เพื่อให้เราสามารถเปลี่ยนแปลงได้Wheel
ทุกเวลาที่เราต้องการ ที่นี่สามารถdependency
( wheel
) ฉีดเข้าไปCar
ในเวลาทำงาน
หลังจากใช้การฉีดพึ่งพา:
ที่นี่เรามีการฉีดอ้างอิง (ล้อและ Battery) ที่รันไทม์ ดังนั้นคำว่า: การพึ่งพาการฉีด
class Car{
private Wheel wh; // Inject an Instance of Wheel (dependency of car) at runtime
private Battery bt; // Inject an Instance of Battery (dependency of car) at runtime
Car(Wheel wh,Battery bt) {
this.wh = wh;
this.bt = bt;
}
//Or we can have setters
void setWheel(Wheel wh) {
this.wh = wh;
}
}
ที่มา: ทำความเข้าใจกับการฉีดพึ่งพา
new
ยาง? ฉันไม่. สิ่งที่ฉันต้องทำคือซื้อ (ฉีดผ่าน param) จากพวกเขาติดตั้งและวา - ลาห์! ดังนั้นเมื่อกลับมาที่การเขียนโปรแกรมสมมติว่าโครงการ C # จำเป็นต้องใช้ไลบรารี / คลาสที่มีอยู่มีสองวิธีในการรัน / ดีบักการอ้างอิงเพิ่ม 1 รายการกับโครงการทั้งหมดนี้
new
มันตัวเลือกที่ 2 คือผ่านมันเป็นพารามิเตอร์ อาจไม่ถูกต้อง แต่โง่ง่ายเข้าใจง่าย
Dependency Injection เป็นวิธีการที่วัตถุได้รับการออกแบบในลักษณะที่พวกเขาได้รับอินสแตนซ์ของวัตถุจากชิ้นส่วนของรหัสอื่น ๆ แทนที่จะสร้างพวกเขาภายใน ซึ่งหมายความว่าวัตถุใด ๆ ที่ใช้อินเทอร์เฟซที่วัตถุต้องการนั้นสามารถทดแทนได้โดยไม่ต้องเปลี่ยนรหัสซึ่งช่วยให้การทดสอบง่ายขึ้นและปรับปรุงการแยกชิ้นส่วน
ตัวอย่างเช่นพิจารณาคำสั่งเหล่านี้:
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 ) { ... }
}
ในตัวอย่างนี้การดำเนินการPersonService::addManager
และPersonService::removeManager
จะต้องมีอินสแตนซ์ของGroupMembershipService
เพื่อที่จะทำงาน โดยไม่ต้องพึ่งพาการฉีดวิธีดั้งเดิมของการทำเช่นนี้คือการสร้างอินสแตนซ์ใหม่GroupMembershipService
ในตัวสร้างPersonService
และใช้แอตทริบิวต์อินสแตนซ์นั้นในทั้งสองฟังก์ชัน อย่างไรก็ตามหากตัวสร้างของGroupMembershipService
มีหลายสิ่งที่มันต้องการหรือแย่กว่านั้นก็ยังมีการเริ่มต้น "setters" บางอย่างที่จำเป็นต้องเรียกใช้บนGroupMembershipService
รหัสนั้นเติบโตขึ้นอย่างรวดเร็วและPersonService
ตอนนี้ขึ้นอยู่กับไม่เพียงGroupMembershipService
แต่ทุกอย่างอื่นGroupMembershipService
ขึ้นอยู่กับ. นอกจากนี้การเชื่อมโยงไปยังGroupMembershipService
ฮาร์ดโค้ดPersonService
ซึ่งหมายความว่าคุณไม่สามารถ "หลอกตา" ได้GroupMembershipService
สำหรับการทดสอบหรือใช้รูปแบบกลยุทธ์ในส่วนต่าง ๆ ของแอปพลิเคชันของคุณ
ด้วยการฉีดพึ่งพาแทนการ instantiating GroupMembershipService
ภายในของPersonService
คุณคุณจะผ่านมันไปที่PersonService
นวกรรมิกหรือมิฉะนั้นเพิ่มคุณสมบัติ (ทะเยอทะยานและ setter) เพื่อตั้งค่าอินสแตนซ์ในท้องถิ่นของมัน ซึ่งหมายความว่าคุณPersonService
ไม่ต้องกังวลเกี่ยวกับวิธีการสร้างอีกต่อGroupMembershipService
ไปเพียงแค่ยอมรับสิ่งที่ได้รับและทำงานร่วมกับพวกเขา นี่ก็หมายความว่าอะไรก็ตามที่เป็น subclass ของGroupMembershipService
หรือใช้GroupMembershipService
อินเทอร์เฟซสามารถ "ฉีด" เข้าไปในPersonService
และPersonService
ไม่จำเป็นต้องรู้เกี่ยวกับการเปลี่ยนแปลง
คำตอบที่ได้รับการยอมรับเป็นคำตอบที่ดี - แต่ฉันต้องการเพิ่มในส่วนนี้ว่า DI นั้นเหมือนกับการหลีกเลี่ยงค่าคงที่แบบฮาร์ดโค้ดในรหัส
เมื่อคุณใช้ค่าคงที่เช่นชื่อฐานข้อมูลคุณจะย้ายอย่างรวดเร็วจากด้านในของรหัสไปยังไฟล์กำหนดค่าบางส่วนและส่งผ่านตัวแปรที่มีค่านั้นไปยังตำแหน่งที่ต้องการ เหตุผลในการทำเช่นนั้นก็คือค่าคงที่เหล่านี้มักจะเปลี่ยนบ่อยกว่าส่วนที่เหลือของรหัส ตัวอย่างเช่นหากคุณต้องการทดสอบโค้ดในฐานข้อมูลทดสอบ
DI นั้นคล้ายคลึงกับสิ่งนี้ในโลกของการเขียนโปรแกรม Object Oriented ค่าที่นั่นแทนที่จะเป็นตัวอักษรคงที่คือวัตถุทั้งหมด - แต่เหตุผลในการย้ายรหัสที่สร้างพวกเขาออกมาจากรหัสชั้นเรียนมีความคล้ายคลึงกัน - วัตถุเปลี่ยนบ่อยขึ้นแล้วรหัสที่ใช้พวกเขา กรณีที่สำคัญอย่างหนึ่งที่จำเป็นต้องมีการเปลี่ยนแปลงคือการทดสอบ
ลองตัวอย่างง่ายๆกับคลาสCarและEngineรถทุกคันต้องการเครื่องยนต์เพื่อไปทุกที่อย่างน้อยตอนนี้ ดังนั้นด้านล่างว่าโค้ดจะมีลักษณะอย่างไรโดยไม่ต้องพึ่งพาการฉีด
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!");
}
}
และเพื่อยกระดับคลาสรถยนต์เราจะใช้รหัสถัดไป:
Car car = new Car();
ปัญหาของรหัสนี้ที่เราจับคู่กับ GasEngine อย่างแน่นหนาและถ้าเราตัดสินใจที่จะเปลี่ยนเป็น ElectricityEngine เราจะต้องเขียนคลาสรถยนต์ใหม่ และยิ่งแอปพลิเคชั่นใหญ่ขึ้นปัญหาและอาการปวดหัวมากขึ้นเราจะต้องเพิ่มและใช้เครื่องยนต์ชนิดใหม่
กล่าวอีกนัยหนึ่งด้วยวิธีการนี้คือระดับรถยนต์ของเราในระดับสูงขึ้นอยู่กับระดับ GasEngine ระดับล่างซึ่งละเมิดหลักการพึ่งพาการผกผันของการพึ่งพา (DIP) จาก SOLID กรมทรัพย์สินทางปัญญาชี้ให้เห็นว่าเราควรพึ่งพา abstractions ไม่ใช่คลาสที่เป็นรูปธรรม ดังนั้นเพื่อตอบสนองสิ่งนี้เราขอแนะนำอินเทอร์เฟซ IEngine และเขียนรหัสใหม่ด้านล่าง:
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();
}
}
ตอนนี้คลาสรถยนต์ของเราขึ้นอยู่กับอินเตอร์เฟส IEngine ไม่ใช่การติดตั้งเฉพาะเครื่องยนต์ ตอนนี้เคล็ดลับเดียวคือเราจะสร้างตัวอย่างของรถและให้ชั้นเรียนคอนกรีตที่แท้จริงเช่น GasEngine หรือ ElectricityEngine ได้อย่างไร นั่นคือสิ่งที่พึ่งพาการฉีดเข้ามา
Car gasCar = new Car(new GasEngine());
gasCar.Run();
Car electroCar = new Car(new ElectricityEngine());
electroCar.Run();
ที่นี่เราโดยทั่วไปฉีด (ผ่าน) การพึ่งพาของเรา (ตัวอย่างเครื่องยนต์) เพื่อสร้างรถยนต์ ดังนั้นตอนนี้ชั้นเรียนของเรามีข้อต่อหลวมระหว่างวัตถุและการพึ่งพาและเราสามารถเพิ่มเครื่องยนต์ชนิดใหม่ ๆ ได้อย่างง่ายดายโดยไม่ต้องเปลี่ยนคลาสรถยนต์
ประโยชน์หลักของการขึ้นต่อกันของการพึ่งพาการเรียนที่มีการเชื่อมโยงอย่างอิสระมากขึ้นเพราะพวกเขาไม่ได้มีการพึ่งพารหัสยาก สิ่งนี้เป็นไปตามหลักการผกผันของการพึ่งพาซึ่งกล่าวถึงข้างต้น แทนที่จะอ้างถึงการใช้งานที่เฉพาะเจาะจงคลาสร้องขอ abstractions (โดยปกติจะเป็นอินเตอร์เฟส ) ซึ่งมีให้กับพวกเขาเมื่อสร้างคลาส
ดังนั้นในที่สุดการพึ่งพาการฉีดจึงเป็นเพียงเทคนิคหนึ่งในการทำให้การมีเพศสัมพันธ์ระหว่างวัตถุและการพึ่งพานั้นหลวม แทนที่จะพึ่งพาการสร้างอินสแตนซ์โดยตรงที่คลาสต้องการเพื่อดำเนินการของมันการพึ่งพาถูกจัดเตรียมให้กับคลาส (ส่วนใหญ่) ผ่านการสร้างคอนสตรัค
นอกจากนี้เมื่อเรามีการอ้างอิงจำนวนมากมันเป็นวิธีปฏิบัติที่ดีมากในการใช้คอนเทนเนอร์ผกผันของการควบคุม (IoC) ซึ่งเราสามารถบอกได้ว่าอินเทอร์เฟซใดที่ควรแมปกับการใช้งานที่เป็นรูปธรรมสำหรับการอ้างอิงทั้งหมดของเรา เป้าหมายของเรา ตัวอย่างเช่นเราสามารถระบุในการแมปสำหรับคอนเทนเนอร์ IoC ที่การพึ่งพาIEngineควรถูกแมปกับคลาสGasEngineและเมื่อเราถามคอนเทนเนอร์ IoC สำหรับอินสแตนซ์ของคลาสรถยนต์ของเรามันจะสร้างคลาสรถยนต์ของเราด้วยการพึ่งพาGasEngineผ่านไปแล้ว
อัพเดท:ดูหลักสูตรเกี่ยวกับ EF Core จาก Julie Lerman เมื่อเร็ว ๆ นี้และชอบคำจำกัดความสั้น ๆ ของเธอเกี่ยวกับ DI
การขึ้นต่อกันของการพึ่งพาเป็นรูปแบบที่ช่วยให้แอปพลิเคชันของคุณสามารถฉีดออบเจกต์ในคลาสที่ต้องการโดยไม่ต้องบังคับให้คลาสเหล่านั้นรับผิดชอบต่อวัตถุเหล่านั้น จะช่วยให้รหัสของคุณมีการเชื่อมโยงอย่างอิสระมากขึ้นและ Entity Framework Core เชื่อมต่อกับระบบบริการเดียวกันนี้
ลองนึกภาพว่าคุณต้องการตกปลา:
คุณจะต้องดูแลทุกอย่างด้วยตัวเอง คุณต้องหาเรือซื้อเบ็ดตกปลาเพื่อหาเหยื่อ ฯลฯ เป็นไปได้แน่นอน แต่มันมีความรับผิดชอบมากสำหรับคุณ ในแง่ซอฟต์แวร์หมายความว่าคุณต้องค้นหาสิ่งเหล่านี้ทั้งหมด
ด้วยการฉีดพึ่งพาคนอื่นดูแลการเตรียมและทำให้อุปกรณ์ที่จำเป็นสำหรับคุณ คุณจะได้รับ ("ถูกฉีด") เรือเบ็ดตกปลาและเหยื่อ - ทั้งหมดพร้อมใช้งาน
นี่คือคำอธิบายที่ง่ายที่สุดเกี่ยวกับการฉีดพึ่งพาและคอนเทนเนอร์ฉีดพึ่งพาฉันเคยเห็น:
การฉีดพึ่งพาและภาชนะฉีดขึ้นอยู่กับสิ่งที่แตกต่าง:
คุณไม่จำเป็นต้องใช้ตู้คอนเทนเนอร์เพื่อทำการฉีด อย่างไรก็ตามคอนเทนเนอร์สามารถช่วยคุณได้
"การฉีดพึ่งพา" ไม่ได้หมายถึงเพียงแค่ใช้การกำหนดพารามิเตอร์แบบสร้างและเซ็ตสาธารณะ?
บทความเจมส์ชอร์แสดงให้เห็นตัวอย่างต่อไปนี้สำหรับการเปรียบเทียบ
ตัวสร้างโดยไม่ต้องพึ่งพาการฉีด:
public class Example { private DatabaseThingie myDatabase; public Example() { myDatabase = new DatabaseThingie(); } public void doStuff() { ... myDatabase.getData(); ... } }
ตัวสร้างด้วยการฉีดพึ่งพา:
public class Example { private DatabaseThingie myDatabase; public Example(DatabaseThingie useThisDatabaseInstead) { myDatabase = useThisDatabaseInstead; } public void doStuff() { ... myDatabase.getData(); ... } }
new DatabaseThingie()
ไม่ได้สร้างอินสแตนซ์ myDatabase ที่ถูกต้อง
เพื่อให้แนวคิดการพึ่งพาการฉีดง่ายต่อการเข้าใจ ลองมาตัวอย่างของปุ่มสลับเพื่อสลับ (เปิด / ปิด) หลอดไฟ
สวิตช์จำเป็นต้องทราบล่วงหน้าว่าหลอดไฟใดที่ฉันเชื่อมต่ออยู่ (การอ้างอิงแบบฮาร์ดโค้ด) ดังนั้น,
สวิตช์ -> PermanentBulb // สวิตช์เชื่อมต่อโดยตรงกับหลอดไฟแบบถาวรทำให้ไม่สามารถทำการทดสอบได้อย่างง่ายดาย
Switch(){
PermanentBulb = new Bulb();
PermanentBulb.Toggle();
}
สวิตช์เท่านั้นที่รู้ว่าฉันต้องเปิด / ปิดหลอดใดก็ตามที่ผ่านมาให้ฉัน ดังนั้น,
สวิตช์ -> Bulb1 หรือ Bulb2 หรือ NightBulb (การพึ่งพาการฉีด)
Switch(AnyBulb){ //pass it whichever bulb you like
AnyBulb.Toggle();
}
การแก้ไขตัวอย่างJamesสำหรับสวิตช์และหลอดไฟ:
public class SwitchTest {
TestToggleBulb() {
MockBulb mockbulb = new MockBulb();
// MockBulb is a subclass of Bulb, so we can
// "inject" it here:
Switch switch = new Switch(mockBulb);
switch.ToggleBulb();
mockBulb.AssertToggleWasCalled();
}
}
public class Switch {
private Bulb myBulb;
public Switch() {
myBulb = new Bulb();
}
public Switch(Bulb useThisBulbInstead) {
myBulb = useThisBulbInstead;
}
public void ToggleBulb() {
...
myBulb.Toggle();
...
}
}`
การพึ่งพาการฉีด (DI) คืออะไร?
ดังที่คนอื่น ๆ ได้กล่าวไว้ว่าการพึ่งพาการฉีด (DI) เป็นการขจัดความรับผิดชอบของการสร้างโดยตรงและการจัดการอายุขัยของวัตถุอื่น ๆ ที่ซึ่งระดับความสนใจของเรา (ระดับผู้บริโภค) ขึ้นอยู่กับ (ในแง่ของ UML ) อินสแตนซ์เหล่านี้จะถูกส่งผ่านไปยังคลาสผู้บริโภคของเราโดยทั่วไปเป็นพารามิเตอร์คอนสตรัคเตอร์หรือผ่านตัวตั้งค่าคุณสมบัติ (การจัดการของวัตถุอ้างอิงและการส่งผ่านไปยังระดับผู้บริโภคมักจะดำเนินการโดยInversion of Control (IoC)คอนเทนเนอร์แต่นั่นเป็นหัวข้ออื่น) .
DI กรมทรัพย์สินทางปัญญาและของแข็ง
โดยเฉพาะในกระบวนทัศน์ของโรเบิร์ตซีมาร์ตินเป็นหลักการที่มั่นคงของการออกแบบเชิงวัตถุ , DI
เป็นหนึ่งในการใช้งานเป็นไปได้ของการพึ่งพาผกผันหลักการ (DIP) กรมทรัพย์สินทางปัญญาเป็นD
ของSOLID
มนต์ - การใช้งานกรมทรัพย์สินทางปัญญาอื่น ๆ รวมถึงตัวระบุบริการและรูปแบบปลั๊กอิน
วัตถุประสงค์ของกรมทรัพย์สินทางปัญญาคือการแยกแน่นอ้างอิงคอนกรีตระหว่างเรียนและแทนเพื่อคลายการมีเพศสัมพันธ์โดยวิธีการของนามธรรมซึ่งสามารถทำได้ผ่านทางinterface
, abstract class
หรือpure virtual class
ขึ้นอยู่กับภาษาและวิธีการใช้
หากไม่มีกรมทรัพย์สินทางปัญญารหัสของเรา (ฉันเรียกว่า 'คลาสการบริโภค') นั้นเชื่อมโยงโดยตรงกับการพึ่งพาที่เป็นรูปธรรมและมักจะเป็นภาระกับความรับผิดชอบในการรู้วิธีที่จะได้รับและจัดการตัวอย่างของการพึ่งพาเช่นนี้:
"I need to create/use a Foo and invoke method `GetBar()`"
ในขณะที่หลังจากการประยุกต์ใช้กรมทรัพย์สินทางปัญญาจะคลายความต้องการและความกังวลของการได้รับและการจัดการอายุการใช้งานของการFoo
พึ่งพาถูกลบออก:
"I need to invoke something which offers `GetBar()`"
ทำไมต้องใช้ DIP (และ DI)
การแยก Decoupling dependencies ระหว่างคลาสด้วยวิธีนี้ช่วยให้ง่ายต่อการทดแทนคลาสพึ่งพาเหล่านี้ด้วยการใช้งานอื่น ๆ ซึ่งยังเติมเต็มสิ่งที่จำเป็นต้องมีของ abstraction (เช่นการพึ่งพาสามารถสลับกับการใช้งานของอินเตอร์เฟสเดียวกัน) นอกจากนี้ยังเป็นคนอื่น ๆ ได้กล่าวอาจสาเหตุที่พบได้บ่อยที่สุดในการแยกคลาสผ่านทางกรมทรัพย์สินทางปัญญาคืออนุญาตให้มีการทดสอบคลาสที่แยกออกจากกันเนื่องจากการพึ่งพาที่เหมือนกันเหล่านี้สามารถถูกขัดจังหวะและ / หรือเยาะเย้ย
ผลลัพธ์หนึ่งของ DI คือการจัดการอายุการใช้งานของอินสแตนซ์วัตถุพึ่งพาไม่ได้ถูกควบคุมโดยคลาสการบริโภคอีกต่อไปเนื่องจากวัตถุการอ้างอิงถูกส่งผ่านไปยังคลาสบริโภค (ผ่านคอนสตรัคเตอร์หรือ setter injection)
สามารถดูได้ในรูปแบบต่างๆ:
Create
บนโรงงานได้ตามต้องการและกำจัดอินสแตนซ์เหล่านี้เมื่อเสร็จสมบูรณ์ควรใช้ DI เมื่อใด
MyDepClass
เป็นหัวข้อที่ปลอดภัย - จะทำอย่างไรถ้าเราทำให้เป็นซิงเกิลตันและฉีดอินสแตนซ์เดียวกันไปยังผู้บริโภคทั้งหมด?)ตัวอย่าง
นี่คือการใช้ C # อย่างง่าย รับระดับบริโภคด้านล่าง:
public class MyLogger
{
public void LogRecord(string somethingToLog)
{
Console.WriteLine("{0:HH:mm:ss} - {1}", DateTime.Now, somethingToLog);
}
}
แม้ว่าจะดูไม่เป็นอันตราย แต่ก็มีสองการstatic
พึ่งพาในสองคลาสอื่น ๆSystem.DateTime
และSystem.Console
ไม่เพียง แต่ จำกัด ตัวเลือกเอาต์พุตการบันทึกเท่านั้น (การบันทึกไปยังคอนโซลจะไม่มีค่าหากไม่มีใครดู) แต่ที่แย่กว่านั้นคือยากที่จะทดสอบโดยอัตโนมัติ นาฬิการะบบที่ไม่สามารถกำหนดค่าได้
อย่างไรก็ตามเราสามารถนำDIP
ไปใช้กับคลาสนี้โดยแยกแยะความกังวลของการประทับเวลาเป็นการพึ่งพาและเชื่อมต่อMyLogger
กับอินเตอร์เฟสที่เรียบง่ายเท่านั้น:
public interface IClock
{
DateTime Now { get; }
}
เราสามารถคลายการพึ่งพาConsole
ให้กับสิ่งที่เป็นนามธรรมเช่น a TextWriter
. โดยทั่วไปแล้วการพึ่งพาการฉีดจะถูกนำไปใช้เป็นการconstructor
ฉีดอย่างใดอย่างหนึ่ง(ผ่านนามธรรมไปยังการพึ่งพาเป็นพารามิเตอร์ไปยังตัวสร้างของคลาสที่บริโภค) หรือSetter Injection
(ผ่านการพึ่งพาผ่านsetXyz()
setter หรือ. Net คุณสมบัติที่{set;}
กำหนดไว้) Constructor Injection เป็นที่ต้องการเนื่องจากเป็นการรับประกันว่าคลาสจะอยู่ในสถานะที่ถูกต้องหลังการก่อสร้างและอนุญาตให้ทำเครื่องหมายเขตข้อมูลการพึ่งพาภายในเป็นreadonly
(C #) หรือfinal
(Java) ดังนั้นการใช้ constructor injection ในตัวอย่างด้านบนทำให้เรามี:
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);
}
}
(ต้องมีรูปธรรมClock
ที่แน่นอนซึ่งแน่นอนว่าสามารถเปลี่ยนกลับไปใช้ได้DateTime.Now
และต้องมีการอ้างอิงสองรายการโดยคอนเทนเนอร์ IoC ผ่านตัวสร้างคอนสตรัคเตอร์)
สามารถสร้างการทดสอบหน่วยอัตโนมัติซึ่งพิสูจน์ได้อย่างชัดเจนว่าคนตัดไม้ของเราทำงานอย่างถูกต้องเนื่องจากเราสามารถควบคุมการพึ่งพา - เวลาและเราสามารถสอดแนมผลลัพธ์ที่เขียนได้:
[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());
}
ขั้นตอนถัดไป
การพึ่งพาการฉีดมีความเกี่ยวข้องกับInversion of Control container (IoC)อย่างสม่ำเสมอเพื่อฉีด (ให้) อินสแตนซ์การพึ่งพาที่เป็นรูปธรรมและเพื่อจัดการอินสแตนซ์อายุการใช้งาน ในระหว่างกระบวนการกำหนดค่า / การบูตสแตรปIoC
คอนเทนเนอร์อนุญาตให้มีการกำหนดดังต่อไปนี้:
IBar
และส่งคืนConcreteBar
อินสแตนซ์" )IDisposable
และจะรับผิดชอบเกี่ยวDisposing
กับการพึ่งพาซึ่งสอดคล้องกับการจัดการอายุการใช้งานที่กำหนดไว้โดยทั่วไปเมื่อคอนเทนเนอร์ IoC ได้รับการกำหนดค่า / bootstrapped พวกเขาทำงานได้อย่างราบรื่นในพื้นหลังทำให้ coder มุ่งเน้นไปที่รหัสในมือมากกว่ากังวลเกี่ยวกับการพึ่งพา
กุญแจสำคัญในรหัส DI ง่ายคือการหลีกเลี่ยงการมีเพศสัมพันธ์แบบคงที่ของชั้นเรียนและไม่ใช้ใหม่ () สำหรับการสร้างการพึ่งพา
ตามตัวอย่างข้างบนการแยกการพึ่งพาต้องอาศัยความพยายามในการออกแบบและสำหรับนักพัฒนามีการเปลี่ยนกระบวนทัศน์ที่จำเป็นในการทำลายนิสัยของการnew
พึ่งพาโดยตรงและแทนที่จะไว้วางใจคอนเทนเนอร์เพื่อจัดการการพึ่งพา
แต่คุณประโยชน์มากมายโดยเฉพาะอย่างยิ่งในความสามารถในการทดสอบระดับความสนใจ
หมายเหตุ : การสร้าง / การทำแผนที่ / ฉาย (ผ่านnew ..()
) ของประมาณการ POCO / POJO / อันดับ DTOs / Entity กราฟ / ไม่ประสงค์ออกนาม JSON, et al - คือ "เท่านั้นข้อมูล" เรียนหรือระเบียน - การใช้หรือกลับมาจากวิธีการที่จะไม่ได้รับการยกย่องเป็นพึ่งพา (ใน ความหมาย UML) และไม่อยู่ภายใต้ DI การใช้new
โปรเจ็กต์เหล่านี้ก็ใช้ได้
จุดรวมของการพึ่งพาการฉีด (DI) คือการทำให้ซอร์สโค้ดแอปพลิเคชันสะอาดและเสถียร :
ในทางปฏิบัติทุกรูปแบบการออกแบบจะแยกข้อกังวลออกเพื่อให้การเปลี่ยนแปลงในอนาคตมีผลต่อไฟล์ขั้นต่ำ
โดเมนเฉพาะของ DI คือการมอบหมายการกำหนดค่าการพึ่งพาและการเริ่มต้น
หากคุณทำงานนอก Java เป็นครั้งคราวให้จำวิธีที่source
ใช้บ่อยในภาษาสคริปต์หลาย ๆ แบบ (Shell, Tcl ฯลฯ หรือแม้กระทั่งimport
ใน Python ใช้ผิดวัตถุประสงค์เพื่อวัตถุประสงค์นี้)
พิจารณาdependent.sh
สคริปต์ง่าย ๆ:
#!/bin/sh
# Dependent
touch "one.txt" "two.txt"
archive_files "one.txt" "two.txt"
สคริปต์ขึ้นอยู่กับ: สคริปต์จะไม่ทำงานด้วยตนเอง ( archive_files
ไม่ได้กำหนดไว้)
คุณกำหนดarchive_files
ในarchive_files_zip.sh
สคริปต์การใช้งาน (ใช้zip
ในกรณีนี้):
#!/bin/sh
# Dependency
function archive_files {
zip files.zip "$@"
}
แทนที่จะsource
ใช้สคริปต์การนำไปใช้โดยตรงในสคริปต์ที่อ้างอิงคุณใช้injector.sh
"คอนเทนเนอร์" ซึ่งล้อมทั้ง "ส่วนประกอบ":
#!/bin/sh
# Injector
source ./archive_files_zip.sh
source ./dependent.sh
การarchive_files
พึ่งพาได้รับการฉีดเข้าไปในสคริปต์พึ่งพา
คุณอาจจะได้ฉีดพึ่งพาซึ่งดำเนินการarchive_files
โดยใช้หรือtar
xz
หากdependent.sh
สคริปต์ใช้การพึ่งพาโดยตรงวิธีการนั้นจะเรียกว่าการค้นหาการพึ่งพา (ซึ่งตรงกันข้ามกับการฉีดพึ่งพา ):
#!/bin/sh
# Dependent
# dependency look-up
source ./archive_files_zip.sh
touch "one.txt" "two.txt"
archive_files "one.txt" "two.txt"
ตอนนี้ปัญหาคือ "องค์ประกอบ" ขึ้นอยู่กับมีการดำเนินการเริ่มต้นเอง
ซอร์สโค้ดของ "component" นั้นไม่สะอาดและไม่เสถียรเนื่องจากการเปลี่ยนแปลงทุกอย่างในการเริ่มต้นการพึ่งพาต้องมีรีลีสใหม่สำหรับซอร์สโค้ดของ "component" เช่นกัน
DI ไม่ได้เน้นย้ำและเป็นที่นิยมเท่าที่เป็นอยู่ในกรอบงาน Java
แต่มันเป็นวิธีการทั่วไปในการแยกข้อกังวลของ:
การใช้การกำหนดค่าเฉพาะกับการค้นหาการพึ่งพาไม่ได้ช่วยเนื่องจากจำนวนพารามิเตอร์การกำหนดค่าอาจเปลี่ยนแปลงตามการอ้างอิง (เช่นชนิดการพิสูจน์ตัวตนใหม่) รวมทั้งจำนวนประเภทการพึ่งพาที่รองรับ (เช่นชนิดฐานข้อมูลใหม่)
คำตอบทั้งหมดข้างต้นเป็นสิ่งที่ดีเป้าหมายของฉันคือการอธิบายแนวคิดด้วยวิธีง่าย ๆ เพื่อให้ทุกคนที่ไม่มีความรู้ด้านการเขียนโปรแกรมสามารถเข้าใจแนวคิดได้
การฉีดพึ่งพาเป็นหนึ่งในรูปแบบการออกแบบที่ช่วยให้เราสร้างระบบที่ซับซ้อนในลักษณะที่ง่ายขึ้น
เราสามารถเห็นความหลากหลายของรูปแบบนี้ในชีวิตประจำวันของเรา ตัวอย่างบางส่วน ได้แก่ เครื่องบันทึกเทป, VCD, ซีดีไดรฟ์เป็นต้น
ภาพด้านบนเป็นภาพของเครื่องบันทึกเทปแบบพกพาที่ม้วนเก็บม้วนในช่วงกลางศตวรรษที่ 20 แหล่ง
ความตั้งใจหลักของเครื่องบันทึกเทปคือการบันทึกหรือเล่นเสียง
ในขณะที่การออกแบบระบบมันต้องใช้รอกเพื่อบันทึกหรือเล่นเสียงหรือเพลง มีสองความเป็นไปได้สำหรับการออกแบบระบบนี้
ถ้าเราใช้อันแรกเราต้องเปิดเครื่องเพื่อเปลี่ยนล้อ หากเราเลือกที่สองนั่นคือการวางเบ็ดสำหรับรีลเราจะได้รับประโยชน์เพิ่มเติมจากการเล่นเพลงใด ๆ โดยการเปลี่ยนรีล และยังลดฟังก์ชั่นเฉพาะการเล่นอะไรก็ตามที่อยู่ในรอก
เช่นการฉีดพึ่งพาที่ชาญฉลาดเป็นกระบวนการของการส่งออกการพึ่งพาที่จะมุ่งเน้นเฉพาะฟังก์ชั่นที่เฉพาะเจาะจงขององค์ประกอบเพื่อให้ส่วนประกอบที่เป็นอิสระสามารถเชื่อมต่อกันเพื่อสร้างระบบที่ซับซ้อน
ประโยชน์หลักที่เราประสบความสำเร็จโดยใช้การฉีดพึ่งพา
วันนี้แนวคิดเหล่านี้เป็นพื้นฐานของกรอบที่รู้จักกันดีในโลกการเขียนโปรแกรม Spring Angular เป็นกรอบซอฟต์แวร์ที่รู้จักกันดีซึ่งสร้างขึ้นจากแนวคิดนี้
การพึ่งพาการฉีดเป็นรูปแบบที่ใช้ในการสร้างอินสแตนซ์ของวัตถุที่วัตถุอื่นพึ่งพาโดยไม่ทราบว่าในเวลารวบรวมชั้นที่จะใช้เพื่อให้การทำงานนั้นหรือเพียงวิธีการฉีดคุณสมบัติไปยังวัตถุที่เรียกว่าการพึ่งพาการฉีด
ตัวอย่างสำหรับการฉีดพึ่งพา
ก่อนหน้านี้เรากำลังเขียนโค้ดแบบนี้
Public MyClass{
DependentClass dependentObject
/*
At somewhere in our code we need to instantiate
the object with new operator inorder to use it or perform some method.
*/
dependentObject= new DependentClass();
dependentObject.someMethod();
}
ด้วยการฉีดพึ่งพาการพึ่งพาหัวฉีดจะถอดอินสแตนซ์สำหรับเรา
Public MyClass{
/* Dependency injector will instantiate object*/
DependentClass dependentObject
/*
At somewhere in our code we perform some method.
The process of instantiation will be handled by the dependency injector
*/
dependentObject.someMethod();
}
คุณยังสามารถอ่าน
ความแตกต่างระหว่าง Inversion of Control และ Dependency Injection
Dependency Injection (DI) หมายถึงการแยกวัตถุที่ขึ้นอยู่กับแต่ละอื่น ๆ สมมติว่า object A ขึ้นอยู่กับ Object B ดังนั้นแนวคิดคือการแยกวัตถุเหล่านี้ออกจากกัน เราไม่จำเป็นต้องใช้รหัสยากกับวัตถุที่ใช้คำหลักใหม่แทนที่จะแบ่งปันการพึ่งพาวัตถุที่รันไทม์ทั้งๆที่รวบรวมเวลา ถ้าเราพูดถึง
เราไม่จำเป็นต้องรหัสยากวัตถุโดยใช้คำหลักใหม่ค่อนข้างกำหนดถั่วพึ่งพาในไฟล์การกำหนดค่า ภาชนะสปริงจะรับผิดชอบการเชื่อมต่อทั้งหมด
IOC เป็นแนวคิดทั่วไปและสามารถแสดงออกได้หลายวิธีและการพึ่งพาการฉีดเป็นตัวอย่างที่ชัดเจนของ IOC
Constructor-based DI สำเร็จเมื่อคอนเทนเนอร์เรียกใช้ Constructor คลาสที่มีอาร์กิวเมนต์จำนวนหนึ่งซึ่งแต่ละตัวจะแทนการพึ่งพาคลาสอื่น ๆ
public class Triangle {
private String type;
public String getType(){
return type;
}
public Triangle(String type){ //constructor injection
this.type=type;
}
}
<bean id=triangle" class ="com.test.dependencyInjection.Triangle">
<constructor-arg value="20"/>
</bean>
Setter-based DI สามารถทำได้โดยคอนเทนเนอร์ที่เรียกเมธอด setter บน bean ของคุณหลังจากเรียกใช้ตัวสร้างแบบไม่มีอาร์กิวเมนต์หรือเมธอดแบบสแตติกของโรงงานแบบไม่มีอาร์กิวเมนต์เพื่อสร้างอินสแตนซ์ถั่วของคุณ
public class Triangle{
private String type;
public String getType(){
return type;
}
public void setType(String type){ //setter injection
this.type = type;
}
}
<!-- setter injection -->
<bean id="triangle" class="com.test.dependencyInjection.Triangle">
<property name="type" value="equivialteral"/>
หมายเหตุ: มันเป็นกฎที่ดีในการใช้ข้อโต้แย้งคอนสตรัคสำหรับการอ้างอิงที่บังคับและ setters สำหรับการอ้างอิงที่ไม่จำเป็น โปรดทราบว่าหากเราใช้คำอธิบายประกอบโดยใช้คำอธิบายประกอบแบบ @Required บน setter สามารถใช้เพื่อสร้าง setters เป็นการอ้างอิงที่จำเป็น
การเปรียบเทียบที่ดีที่สุดที่ฉันนึกได้คือศัลยแพทย์และผู้ช่วยของเขาในโรงละครผ่าตัดซึ่งศัลยแพทย์เป็นบุคคลหลักและผู้ช่วยของเขาซึ่งเป็นผู้ให้การผ่าตัดในเวลาที่เขาต้องการเพื่อให้ศัลยแพทย์สามารถตั้งสมาธิได้ สิ่งที่เขาทำได้ดีที่สุด (การผ่าตัด) หากไม่มีผู้ช่วยศัลยแพทย์จะต้องนำส่วนประกอบมาเองทุกครั้งที่ต้องการ
DI สำหรับระยะสั้นเป็นเทคนิคที่จะลบความรับผิดชอบเพิ่มเติมทั่วไป (ภาระ) ในส่วนประกอบเพื่อดึงส่วนประกอบที่ขึ้นอยู่กับโดยให้พวกเขาไป
DI นำคุณใกล้ชิดกับหลักการ Single รับผิดชอบ (อาร์) surgeon who can concentrate on surgery
เช่น
จะใช้ DI เมื่อใด: ฉันแนะนำให้ใช้ DI ในโครงการผลิตเกือบทั้งหมด (เล็ก / ใหญ่) โดยเฉพาะในสภาพแวดล้อมทางธุรกิจที่เปลี่ยนแปลง :)
ทำไม: เพราะคุณต้องการให้รหัสของคุณสามารถทดสอบได้ง่าย ๆ เลียนแบบและอื่น ๆ เพื่อให้คุณสามารถทดสอบการเปลี่ยนแปลงได้อย่างรวดเร็วและผลักดันมันออกสู่ตลาด นอกจากทำไมคุณจะไม่เมื่อคุณมีเครื่องมือ / กรอบฟรีที่ยอดเยี่ยมมากมายที่จะสนับสนุนคุณในการเดินทางไปยัง codebase ซึ่งคุณสามารถควบคุมได้มากขึ้น
ตัวอย่างเช่นเรามี 2 ชั้นและClient
จะใช้Service
Client
Service
public class Service {
public void doSomeThingInService() {
// ...
}
}
วิธีที่ 1)
public class Client {
public void doSomeThingInClient() {
Service service = new Service();
service.doSomeThingInService();
}
}
วิธีที่ 2)
public class Client {
Service service = new Service();
public void doSomeThingInClient() {
service.doSomeThingInService();
}
}
วิธีที่ 3)
public class Client {
Service service;
public Client() {
service = new Service();
}
public void doSomeThingInClient() {
service.doSomeThingInService();
}
}
1) 2) 3) การใช้
Client client = new Client();
client.doSomeThingInService();
ข้อดี
ข้อเสีย
Client
ชั้นเรียนทดสอบService
สตรัคเตอร์เราจำเป็นต้องเปลี่ยนรหัสในทุก ๆ ที่สร้างService
อ็อบเจกต์วิธีที่ 1)การฉีดคอนสตรัคเตอร์
public class Client {
Service service;
Client(Service service) {
this.service = service;
}
// Example Client has 2 dependency
// Client(Service service, IDatabas database) {
// this.service = service;
// this.database = database;
// }
public void doSomeThingInClient() {
service.doSomeThingInService();
}
}
การใช้
Client client = new Client(new Service());
// Client client = new Client(new Service(), new SqliteDatabase());
client.doSomeThingInClient();
วิธีที่ 2) Setter injection
public class Client {
Service service;
public void setService(Service service) {
this.service = service;
}
public void doSomeThingInClient() {
service.doSomeThingInService();
}
}
การใช้
Client client = new Client();
client.setService(new Service());
client.doSomeThingInClient();
วิธีที่ 3) การต่อประสานการฉีด
ตรวจสอบhttps://en.wikipedia.org/wiki/Dependency_inject
===
ตอนนี้โค้ดนี้ได้ติดตามไปแล้วDependency Injection
และง่ายสำหรับClient
คลาสทดสอบ
อย่างไรก็ตามเรายังคงใช้new Service()
เวลาหลายครั้งและมันก็ไม่ดีเมื่อตัวService
สร้างการเปลี่ยนแปลง เพื่อป้องกันมันเราสามารถใช้หัวฉีด DI เช่น
1) คู่มือแบบง่ายInjector
public class Injector {
public static Service provideService(){
return new Service();
}
public static IDatabase provideDatatBase(){
return new SqliteDatabase();
}
public static ObjectA provideObjectA(){
return new ObjectA(provideService(...));
}
}
การใช้
Service service = Injector.provideService();
2) ใช้ไลบรารี่ : สำหรับ Android dagger2
ข้อดี
Service
คุณจะต้องเปลี่ยนมันในชั้นหัวฉีดConstructor Injection
เมื่อคุณดูคอนสตรัคเตอร์ของClient
คุณจะเห็นจำนวนการพึ่งพาของClient
คลาสข้อเสีย
Constructor Injection
ที่Service
วัตถุถูกสร้างขึ้นเมื่อClient
สร้างขึ้นบางครั้งเราใช้ฟังก์ชั่นในClient
ชั้นเรียนโดยไม่ต้องใช้Service
สร้างขึ้นเพื่อให้Service
มีการสูญเสียhttps://en.wikipedia.org/wiki/Dependency_injection
การพึ่งพาเป็นวัตถุที่สามารถใช้ (
Service
)
การฉีดเป็นการส่งผ่านการพึ่งพา (Service
) ไปยังวัตถุClient
ที่อ้างถึง( ) ที่จะใช้มัน
มันหมายความว่าวัตถุควรมีการอ้างอิงมากเท่าที่จำเป็นในการทำงานของพวกเขาและการพึ่งพาควรจะน้อย นอกจากนี้การพึ่งพาของวัตถุควรอยู่ในส่วนต่อประสานและไม่ใช่วัตถุที่เป็นรูปธรรมเมื่อเป็นไปได้ (วัตถุที่เป็นรูปธรรมคือวัตถุใด ๆ ที่สร้างขึ้นด้วยคำหลักใหม่) การมีเพศสัมพันธ์แบบหลวมช่วยให้สามารถนำกลับมาใช้ใหม่ได้ง่ายขึ้นบำรุงรักษาง่ายขึ้น
“ การพึ่งพาการฉีด” (DI) หรือที่เรียกว่า“ การกลับกันของการควบคุม” (IoC) สามารถใช้เป็นเทคนิคในการกระตุ้นการมีเพศสัมพันธ์แบบหลวม ๆ
มีสองวิธีหลักในการนำ DI ไปใช้:
มันเป็นเทคนิคของการส่งผ่านการพึ่งพาวัตถุไปยังตัวสร้าง
โปรดสังเกตว่านวกรรมิกยอมรับส่วนต่อประสานไม่ใช่วัตถุที่เป็นรูปธรรม นอกจากนี้โปรดทราบว่ามีการโยนข้อยกเว้นหากพารามิเตอร์ orderDao เป็นโมฆะ สิ่งนี้เน้นความสำคัญของการได้รับการอ้างอิงที่ถูกต้อง ในความคิดของฉันการสร้างคอนสตรัคชันนั้นเป็นกลไกที่ต้องการสำหรับการพึ่งพาวัตถุ เป็นที่ชัดเจนสำหรับนักพัฒนาในขณะที่เรียกใช้วัตถุที่ต้องมีการอ้างอิงไปยังวัตถุ“ บุคคล” สำหรับการดำเนินการที่เหมาะสม
แต่ลองพิจารณาตัวอย่างต่อไปนี้ ... สมมติว่าคุณมีคลาสที่มีสิบวิธีที่ไม่มีการพึ่งพา แต่คุณกำลังเพิ่มวิธีการใหม่ที่มีการพึ่งพา IDAO คุณสามารถเปลี่ยน Constructor ให้ใช้ Constructor Injection ได้ แต่สิ่งนี้อาจบังคับให้คุณเปลี่ยนการเรียก Constructor ทั้งหมดได้ อีกวิธีหนึ่งคุณสามารถเพิ่มตัวสร้างใหม่ที่ใช้การพึ่งพา แต่แล้วนักพัฒนาจะรู้ได้อย่างไรว่าจะใช้ตัวสร้างตัวหนึ่งทับอีกตัวหนึ่งได้อย่างไร ในที่สุดหากการพึ่งพามีราคาแพงมากในการสร้างทำไมมันควรจะถูกสร้างขึ้นและส่งผ่านไปยังตัวสร้างเมื่อมันอาจถูกนำมาใช้เพียงเล็กน้อยเท่านั้น? “ Setter Injection” เป็นอีกหนึ่งเทคนิค DI ที่สามารถใช้ในสถานการณ์เช่นนี้
Setter Injection ไม่ได้บังคับให้ต้องพึ่งพาการส่งผ่านไปยังตัวสร้าง แต่การอ้างอิงจะถูกตั้งค่าไปยังคุณสมบัติสาธารณะที่ถูกเปิดเผยโดยวัตถุที่ต้องการ ตามที่ระบุไว้ก่อนหน้านี้แรงจูงใจหลักในการทำสิ่งนี้ ได้แก่ :
นี่คือตัวอย่างลักษณะของโค้ดด้านบน:
public class Person {
public Person() {}
public IDAO Address {
set { addressdao = value; }
get {
if (addressdao == null)
throw new MemberAccessException("addressdao" +
" has not been initialized");
return addressdao;
}
}
public Address GetAddress() {
// ... code that uses the addressdao object
// to fetch address details from the datasource ...
}
// Should not be called directly;
// use the public property instead
private IDAO addressdao;
ฉันคิดว่าเนื่องจากทุกคนเขียน DI ให้ฉันถามคำถามสองสามข้อ ..
นี้จะขึ้นอยู่กับคำตอบ @Adam N โพสต์
เหตุใด PersonService ไม่ต้องกังวลเกี่ยวกับ GroupMembershipService อีกต่อไป คุณเพียงกล่าวถึง GroupMembership มีหลายสิ่ง (วัตถุ / คุณสมบัติ) ขึ้นอยู่กับ ถ้าจำเป็นต้องใช้ GMService ใน PService คุณจะต้องใช้มันเป็นคุณสมบัติ คุณสามารถเยาะเย้ยเรื่องนั้นได้ไม่ว่าคุณจะฉีดหรือไม่ก็ตาม ครั้งเดียวที่ฉันต้องการให้มันฉีดคือถ้า GMService มีคลาสลูกที่เฉพาะเจาะจงมากขึ้นซึ่งคุณจะไม่รู้จนกระทั่งรันไทม์ จากนั้นคุณต้องการฉีดคลาสย่อย หรือถ้าคุณต้องการที่จะใช้มันเป็นทั้งแบบซิงเกิลหรือต้นแบบ ตามความเป็นจริงไฟล์กำหนดค่ามีทุกสิ่งที่ฮาร์ดโค้ดเท่า subclass สำหรับประเภท (อินเตอร์เฟส) ที่มันจะถูกฉีดระหว่างการรวบรวมเวลา
แก้ไข
ความคิดเห็นที่ดีโดย Jose Maria Arranz บน DI
DI เพิ่มการติดต่อกันโดยไม่จำเป็นต้องกำหนดทิศทางของการพึ่งพาและเขียนรหัสกาวใด ๆ
เท็จ ทิศทางของการพึ่งพาอยู่ในรูปแบบ XML หรือเป็นคำอธิบายประกอบการพึ่งพาของคุณจะถูกเขียนเป็นรหัส XML และคำอธิบายประกอบ XML และคำอธิบายประกอบเป็นรหัสที่มา
DI ลดการเชื่อมต่อด้วยการทำให้ส่วนประกอบทั้งหมดของคุณเป็นโมดูล (เช่นสามารถถอดเปลี่ยนได้) และมีอินเตอร์เฟซที่กำหนดชัดเจนซึ่งกันและกัน
เท็จ คุณไม่จำเป็นต้องมีเฟรมเวิร์ก DI เพื่อสร้างรหัสแบบแยกส่วนตามส่วนต่อประสาน
เกี่ยวกับการถอดเปลี่ยนได้: ด้วยไฟล์เก็บถาวร. properties ที่ง่ายมากและ Class.forName คุณสามารถกำหนดคลาสที่สามารถเปลี่ยนแปลงได้ ถ้าคลาสใดของรหัสของคุณสามารถเปลี่ยนแปลงได้ Java ไม่เหมาะสำหรับคุณใช้ภาษาสคริปต์ โดยวิธีการ: คำอธิบายประกอบไม่สามารถเปลี่ยนแปลงได้หากไม่ทำการรวบรวมซ้ำ
ในความคิดของฉันมีเพียงเหตุผลเดียวสำหรับกรอบ DI: การลดจานหม้อไอน้ำ ด้วยระบบโรงงานที่ทำงานได้เป็นอย่างดีคุณสามารถทำสิ่งเดียวกันได้ควบคุมได้มากกว่าและสามารถคาดเดาได้มากขึ้นตามกรอบ DI ที่คุณต้องการ DI Frameworks สัญญาว่าจะลดโค้ด (XML และหมายเหตุประกอบเป็นซอร์สโค้ดด้วย) ปัญหาคือการลดจานหม้อไอน้ำนี้เป็นเรื่องจริงในกรณีที่ง่ายมาก (หนึ่งอินสแตนซ์ต่อคลาสและคล้ายกัน) บางครั้งในโลกแห่งความเป็นจริงการเลือกวัตถุบริการที่เหมาะสมนั้นไม่ใช่เรื่องง่ายเหมือนกับการแมปคลาสกับวัตถุเดี่ยว
คำตอบที่ได้รับความนิยมนั้นไม่ช่วยเหลือเพราะพวกเขากำหนดการฉีดยาเสพติดในแบบที่ไม่มีประโยชน์ มาตกลงกันว่าด้วย "การพึ่งพา" เราหมายถึงวัตถุอื่นที่มีอยู่แล้วที่วัตถุ X ของเราต้องการ แต่เราไม่ได้บอกว่าเรากำลังทำ "การฉีดพึ่งพา" เมื่อเราพูด
$foo = Foo->new($bar);
เราเพิ่งเรียกพารามิเตอร์ที่ผ่านเข้าไปในตัวสร้าง เราได้ทำสิ่งนี้เป็นประจำนับตั้งแต่มีการสร้างคอนสตรัคเตอร์
"การพึ่งพาการฉีด" ถือเป็นประเภทของ "การผกผันของการควบคุม" ซึ่งหมายความว่าตรรกะบางอย่างถูกนำออกมาจากผู้โทร ไม่เป็นเช่นนั้นเมื่อผู้เรียกส่งผ่านพารามิเตอร์ดังนั้นถ้าเป็น DI, DI จะไม่บอกเป็นนัยถึงการควบคุม
DI หมายถึงมีระดับกลางระหว่างผู้โทรและผู้สร้างที่จัดการการอ้างอิง Makefile เป็นตัวอย่างง่ายๆของการฉีดการพึ่งพา "ผู้เรียก" คือบุคคลที่พิมพ์ "make bar" บนบรรทัดคำสั่งและ "ตัวสร้าง" เป็นคอมไพเลอร์ Makefile ระบุว่าแถบนั้นขึ้นอยู่กับ foo และทำเช่นนั้น
gcc -c foo.cpp; gcc -c bar.cpp
ก่อนทำ
gcc foo.o bar.o -o bar
บุคคลที่พิมพ์ "make bar" ไม่จำเป็นต้องรู้ว่าแถบนั้นขึ้นอยู่กับ foo การอ้างอิงถูกฉีดระหว่าง "make bar" และ gcc
วัตถุประสงค์หลักของระดับกลางไม่เพียง แต่จะส่งผ่านการพึ่งพาไปยังผู้สร้าง แต่เพื่อแสดงรายการการพึ่งพาทั้งหมดในที่เดียวและเพื่อซ่อนพวกเขาจาก coder (เพื่อไม่ให้ coder ให้พวกเขา)
โดยปกติระดับกลางจะให้โรงงานสำหรับวัตถุที่สร้างขึ้นซึ่งจะต้องจัดเตรียมบทบาทที่แต่ละประเภทของวัตถุที่ร้องขอต้องเป็นไปตาม นั่นเป็นเพราะการมีระดับกลางที่ซ่อนรายละเอียดของการก่อสร้างคุณได้รับโทษที่เป็นนามธรรมที่กำหนดโดยโรงงานดังนั้นคุณอาจใช้โรงงาน
การพึ่งพาการฉีดหมายถึงวิธี (จริงๆแล้วทางใดทางหนึ่ง ) สำหรับส่วนหนึ่งของรหัส (เช่นคลาส) เพื่อเข้าถึงการอ้างอิง (ส่วนอื่น ๆ ของรหัสเช่นคลาสอื่น ๆ มันขึ้นอยู่กับ) ในรูปแบบโมดูลาร์โดยไม่ต้อง hardcoded ดังนั้น พวกเขาสามารถเปลี่ยนหรือ overriden ได้อย่างอิสระหรือแม้กระทั่งโหลดในเวลาอื่นตามความจำเป็น)
(และ ps ใช่มันกลายเป็นชื่อที่เกินจริงไป 25 $ สำหรับแนวคิดที่ค่อนข้างง่าย).25
เซนต์ของฉัน
ฉันรู้ว่ามีคำตอบมากมายอยู่แล้ว แต่ฉันพบว่าสิ่งนี้มีประโยชน์มาก: http://tutorials.jenkov.com/dependency-injection/index.html
public class MyDao {
protected DataSource dataSource = new DataSourceImpl(
"driver", "url", "user", "password");
//data access methods...
public Person readPerson(int primaryKey) {...}
}
public class MyDao {
protected DataSource dataSource = null;
public MyDao(String driver, String url, String user, String password) {
this.dataSource = new DataSourceImpl(driver, url, user, password);
}
//data access methods...
public Person readPerson(int primaryKey) {...}
}
สังเกตว่าการDataSourceImpl
สร้างอินสแตนซ์นั้นถูกย้ายไปยังตัวสร้าง DataSourceImpl
นวกรรมิกใช้พารามิเตอร์สี่ซึ่งเป็นค่าที่สี่ที่จำเป็นโดย แม้ว่าMyDao
ชั้นเรียนจะยังคงขึ้นอยู่กับคุณค่าทั้งสี่นี้ แต่ก็ไม่เป็นที่น่าพอใจอีกต่อไป พวกเขาจะได้รับจากคลาสใดก็ตามที่สร้างMyDao
อินสแตนซ์
การพึ่งพาการฉีดเป็นวิธีหนึ่งที่เป็นไปได้สำหรับสิ่งที่โดยทั่วไปอาจเรียกว่าข้อกำหนด "การทำให้งงงวยการพึ่งพา" Obfuscation พึ่งพาเป็นวิธีการเอาธรรมชาติ 'ชัดเจน' ออกจากกระบวนการของการให้การอ้างอิงไปยังคลาสที่ต้องใช้และดังนั้นจึง obfuscating ในบางวิธีบทบัญญัติของการอ้างอิงดังกล่าวกับคลาสดังกล่าว นี่ไม่จำเป็นว่าจะเป็นสิ่งเลวร้าย ในความเป็นจริงโดยทำให้งงงวยลักษณะที่มีการพึ่งพาให้กับชั้นเรียนแล้วบางสิ่งนอกชั้นเรียนมีหน้าที่รับผิดชอบในการสร้างการพึ่งพาซึ่งหมายความว่าในสถานการณ์ต่าง ๆ การใช้งานที่แตกต่างกันของการพึ่งพาสามารถให้กับชั้นเรียนโดยไม่ต้องทำการเปลี่ยนแปลงใด ๆ เข้าชั้นเรียน สิ่งนี้เหมาะอย่างยิ่งสำหรับการสลับระหว่างโหมดการผลิตและการทดสอบ (เช่นใช้การพึ่งพาบริการ 'จำลอง')
น่าเสียดายที่ส่วนที่ไม่ดีคือบางคนคิดว่าคุณจำเป็นต้องมีกรอบการทำงานเฉพาะด้านเพื่อสร้างความสับสนให้แก่ผู้พึ่งพาและคุณก็เป็นโปรแกรมเมอร์ที่ 'ด้อยกว่า' ถ้าคุณเลือกที่จะไม่ใช้เฟรมเวิร์กเฉพาะเพื่อทำมัน อีกเรื่องที่น่ารำคาญอย่างมากที่เชื่อกันโดยหลาย ๆ คนก็คือการฉีดการพึ่งพาเป็นวิธีเดียวที่จะทำให้เกิดความสับสนในการพึ่งพา นี่เป็นตัวอย่างที่ชัดเจนและมีประวัติในอดีตและเห็นได้ชัดว่าผิด 100% แต่คุณจะมีปัญหาในการโน้มน้าวใจคนบางคนว่ามีทางเลือกในการฉีดยาพึ่งพาสำหรับข้อกำหนดการทำให้งงงวยของผู้พึ่งพา
โปรแกรมเมอร์เข้าใจความต้องการในการทำให้ยุ่งเหยิงของผู้พึ่งพาอาศัยกันมานานหลายปีและมีทางเลือกมากมายที่พัฒนาขึ้นทั้งก่อนและหลังการฉีด มีรูปแบบของโรงงาน แต่ยังมีตัวเลือกมากมายโดยใช้ ThreadLocal ที่ไม่จำเป็นต้องฉีดอินสแตนซ์เฉพาะ - การพึ่งพานั้นจะถูกฉีดเข้าไปในเธรดอย่างมีประสิทธิภาพซึ่งมีประโยชน์ในการทำให้วัตถุพร้อมใช้งานใด ๆคลาสที่ต้องการโดยไม่ต้องเพิ่มคำอธิบายประกอบในคลาสที่ต้องการและตั้งค่า 'กาว' XML ที่ซับซ้อนเพื่อให้มันเกิดขึ้น เมื่อการพึ่งพาของคุณเป็นสิ่งที่จำเป็นสำหรับการคงอยู่ (JPA / JDO หรืออะไรก็ตาม) มันจะช่วยให้คุณบรรลุ 'การมีอยู่อย่างต่อเนื่อง' ได้ง่ายขึ้นมากและด้วยโมเดลโดเมนและคลาสโมเดลธุรกิจที่ประกอบขึ้นจาก POJOs ทั้งหมด
จากหนังสือเล่มนี้ ' ผู้พัฒนา Java ที่มีเหตุผลดี: เทคนิคสำคัญของ Java 7 และการเขียนโปรแกรมหลายภาษา
DI เป็นรูปแบบเฉพาะของ IoC โดยกระบวนการค้นหาการพึ่งพาของคุณอยู่นอกการควบคุมโดยตรงของรหัสที่คุณกำลังใช้งานในปัจจุบัน
ก่อนที่จะไปอธิบายรายละเอียดทางเทคนิคก่อนเห็นภาพด้วยตัวอย่างในชีวิตจริงเพราะคุณจะได้พบกับข้อมูลทางเทคนิคมากมายที่จะเรียนรู้การฉีดพึ่งพา แต่เวลาสูงสุดที่คนอย่างฉันไม่สามารถได้รับแนวคิดหลักของมัน
ในภาพแรกสมมติว่าคุณมีโรงงานผลิตรถยนต์ที่รวมตัวกันเป็นจำนวนมาก รถที่ถูกสร้างขึ้นจริงในหน่วยการชุมนุมแต่ต้องเครื่องยนต์ , ที่นั่งเช่นเดียวกับล้อ ดังนั้นแอสเซมบลียูนิตจะขึ้นอยู่กับหน่วยทั้งหมดเหล่านี้และพวกเขาจะขึ้นอยู่กับโรงงาน
คุณสามารถรู้สึกว่าตอนนี้มันซับซ้อนเกินไปที่จะรักษางานทั้งหมดในโรงงานนี้เพราะพร้อมกับงานหลัก (การประกอบรถยนต์ในชุดหน่วยประกอบ) คุณต้องให้ความสำคัญกับหน่วยอื่น ๆหน่วยงานอื่นตอนนี้มันมีค่าใช้จ่ายสูงมากในการบำรุงรักษาและอาคารโรงงานมีขนาดใหญ่มากดังนั้นจึงต้องใช้เงินเพิ่มเติมสำหรับการเช่า
ตอนนี้ดูรูปที่สอง หากคุณพบว่า บริษัท ผู้ให้บริการบางอย่างที่จะช่วยให้คุณล้อ , ที่นั่งและเครื่องยนต์ราคาถูกกว่าต้นทุนการผลิตด้วยตัวคุณเองแล้วตอนนี้คุณไม่จำเป็นต้องทำให้พวกเขาในโรงงานของคุณ คุณสามารถเช่าอาคารขนาดเล็กตอนนี้เพียงแค่สำหรับหน่วยการชุมนุมของคุณซึ่งจะช่วยลดงานบำรุงรักษาของคุณและลดค่าเช่าเพิ่มของคุณ ตอนนี้คุณสามารถมุ่งเน้นเฉพาะงานหลักของคุณ (การประกอบรถยนต์)
ตอนนี้เราสามารถพูดได้ว่าทุกการอ้างอิงสำหรับการประกอบรถได้รับการฉีดในโรงงานจากผู้ให้บริการ มันเป็นตัวอย่างของการฉีดพึ่งพา (DI) ในชีวิตจริงชีวิตจริง
ตอนนี้ในคำทางเทคนิคการฉีดพึ่งพาเป็นเทคนิคที่วัตถุหนึ่ง (หรือวิธีการแบบคงที่) หน้า ๆ การพึ่งพาของวัตถุอื่น ดังนั้นการถ่ายโอนงานในการสร้างวัตถุให้กับบุคคลอื่นและใช้การพึ่งพาโดยตรงโดยตรงเรียกว่าการฉีดพึ่งพา
นี่จะช่วยให้คุณเรียนรู้ DI ด้วยคำศัพท์ที่ซับซ้อน นี้จะแสดงเมื่อมีการใช้ DI และเมื่อควรไม่
จาก Book Apress.Spring.Persistence.with. Hibernate.Oct.2010
วัตถุประสงค์ของการฉีดพึ่งพาคือการแยกชิ้นส่วนของการแก้ไขส่วนประกอบซอฟต์แวร์ภายนอกจากตรรกะทางธุรกิจแอปพลิเคชันของคุณโดยไม่ต้องพึ่งพาการฉีดรายละเอียดของวิธีการเข้าถึงส่วนประกอบบริการที่ต้องการบริการสามารถยุ่งเหยิงด้วยรหัสของส่วนประกอบ สิ่งนี้ไม่เพียงเพิ่มโอกาสในการเกิดข้อผิดพลาดเพิ่มการขยายโค้ดและขยายความซับซ้อนในการบำรุงรักษา มันประกอบเข้าด้วยกันอย่างใกล้ชิดทำให้ยากต่อการปรับเปลี่ยนการพึ่งพาเมื่อทำการเปลี่ยนโครงสร้างหรือทดสอบ
Dependency Injection (DI) เป็นหนึ่งในรูปแบบการออกแบบซึ่งใช้คุณสมบัติพื้นฐานของ OOP - ความสัมพันธ์ในวัตถุหนึ่งกับวัตถุอื่น ในขณะที่การสืบทอดสืบทอดวัตถุหนึ่งเพื่อทำวัตถุอื่นที่ซับซ้อนและเฉพาะเจาะจงมากขึ้นความสัมพันธ์หรือการเชื่อมโยงเพียงแค่สร้างตัวชี้ไปยังวัตถุอื่นจากวัตถุหนึ่งโดยใช้คุณลักษณะ พลังของ DI นั้นรวมกับคุณสมบัติอื่น ๆ ของ OOP เช่นเดียวกับส่วนต่อประสานและการซ่อนรหัส สมมติว่าเรามีลูกค้า (สมาชิก) อยู่ในห้องสมุดซึ่งสามารถยืมหนังสือเล่มเดียวเพื่อความเรียบง่าย
ส่วนต่อประสานของหนังสือ:
package com.deepam.hidden;
public interface BookInterface {
public BookInterface setHeight(int height);
public BookInterface setPages(int pages);
public int getHeight();
public int getPages();
public String toString();
}
ต่อไปเราสามารถมีหนังสือหลายประเภท ประเภทหนึ่งคือนิยาย:
package com.deepam.hidden;
public class FictionBook implements BookInterface {
int height = 0; // height in cm
int pages = 0; // number of pages
/** constructor */
public FictionBook() {
// TODO Auto-generated constructor stub
}
@Override
public FictionBook setHeight(int height) {
this.height = height;
return this;
}
@Override
public FictionBook setPages(int pages) {
this.pages = pages;
return this;
}
@Override
public int getHeight() {
// TODO Auto-generated method stub
return height;
}
@Override
public int getPages() {
// TODO Auto-generated method stub
return pages;
}
@Override
public String toString(){
return ("height: " + height + ", " + "pages: " + pages);
}
}
ขณะนี้ผู้สมัครสมาชิกสามารถเชื่อมโยงกับหนังสือได้:
package com.deepam.hidden;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Subscriber {
BookInterface book;
/** constructor*/
public Subscriber() {
// TODO Auto-generated constructor stub
}
// injection I
public void setBook(BookInterface book) {
this.book = book;
}
// injection II
public BookInterface setBook(String bookName) {
try {
Class<?> cl = Class.forName(bookName);
Constructor<?> constructor = cl.getConstructor(); // use it for parameters in constructor
BookInterface book = (BookInterface) constructor.newInstance();
//book = (BookInterface) Class.forName(bookName).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return book;
}
public BookInterface getBook() {
return book;
}
public static void main(String[] args) {
}
}
ทั้งสามคลาสสามารถซ่อนได้เนื่องจากมีการใช้งานของตัวเอง ตอนนี้เราสามารถใช้รหัสนี้สำหรับ DI:
package com.deepam.implement;
import com.deepam.hidden.Subscriber;
import com.deepam.hidden.FictionBook;
public class CallHiddenImplBook {
public CallHiddenImplBook() {
// TODO Auto-generated constructor stub
}
public void doIt() {
Subscriber ab = new Subscriber();
// injection I
FictionBook bookI = new FictionBook();
bookI.setHeight(30); // cm
bookI.setPages(250);
ab.setBook(bookI); // inject
System.out.println("injection I " + ab.getBook().toString());
// injection II
FictionBook bookII = ((FictionBook) ab.setBook("com.deepam.hidden.FictionBook")).setHeight(5).setPages(108); // inject and set
System.out.println("injection II " + ab.getBook().toString());
}
public static void main(String[] args) {
CallHiddenImplBook kh = new CallHiddenImplBook();
kh.doIt();
}
}
มีหลายวิธีที่จะใช้การฉีดแบบพึ่งพาได้ มันเป็นไปได้ที่จะรวมเข้ากับ Singleton ฯลฯ แต่ยังอยู่ในระดับพื้นฐานมันเป็นเพียงการเชื่อมโยงตระหนักถึงโดยการสร้างคุณลักษณะของประเภทวัตถุภายในวัตถุอื่น ประโยชน์นั้นมีเฉพาะในฟีเจอร์เท่านั้นโค้ดนั้นซึ่งเราควรเขียนอีกครั้งและอีกครั้งจะถูกจัดทำและส่งให้เราล่วงหน้า นี่คือเหตุผลที่ DI เชื่อมโยงอย่างใกล้ชิดกับ Inversion of Control (IoC) ซึ่งหมายถึงว่าโปรแกรมของเราผ่านการควบคุมโมดูลที่กำลังทำงานอยู่อีกตัวหนึ่งซึ่งจะทำการฉีดถั่วลงในรหัสของเรา (แต่ละวัตถุซึ่งสามารถฉีดได้สามารถลงนามหรือพิจารณาเป็นถั่วได้) ตัวอย่างเช่นในฤดูใบไม้ผลิจะทำโดยการสร้างและการเริ่มต้น ApplicationContextภาชนะซึ่งใช้งานได้กับเรา เราเพียงแค่ในรหัสของเราสร้างบริบทและเรียกการเริ่มต้นถั่ว ในช่วงเวลานั้นการฉีดจะทำโดยอัตโนมัติ
ฉีดพึ่งพาสำหรับ 5 ปี
เมื่อคุณไปและนำสิ่งของออกจากตู้เย็นด้วยตัวคุณเองคุณอาจทำให้เกิดปัญหา คุณอาจเปิดประตูทิ้งไว้คุณอาจได้สิ่งที่แม่หรือพ่อไม่ต้องการให้คุณ คุณอาจจะกำลังมองหาสิ่งที่เราไม่มีหรือหมดอายุ
สิ่งที่คุณควรทำคือการระบุความต้องการ "ฉันต้องการเครื่องดื่มพร้อมอาหารกลางวัน" และจากนั้นเราจะทำให้แน่ใจว่าคุณมีบางอย่างเมื่อคุณนั่งลงกิน
จาก Christoffer Noring หนังสือของ Pablo Deeleman“ Learning Angular - Second Edition”:
"ในฐานะที่เป็นของเราการใช้งานที่เติบโตและวิวัฒนาการหนึ่งของเราในแต่ละหน่วยงานรหัสภายในจะต้องมีกรณีของวัตถุอื่น ๆซึ่งเป็นที่รู้จักกันดีในการอ้างอิงในโลกของวิศวกรรมซอฟต์แวร์. การกระทำของการส่งผ่านเช่นการอ้างอิงไปยังลูกค้าขึ้นเป็นที่รู้จักกันฉีด , และยังสร้างความมีส่วนร่วมของรหัสอีกกิจการหนึ่งชื่อที่หัวฉีด . the หัวฉีดจะต้องรับผิดชอบสำหรับอินสแตนซ์และความร่วมมือที่จำเป็นการอ้างอิงดังนั้นพวกเขาจึงพร้อมใช้งานในช่วงเวลาที่พวกเขาประสบความสำเร็จในการฉีดลูกค้า สิ่งนี้สำคัญมากเนื่องจากไคลเอนต์ไม่ทราบว่าจะสร้างอินสแตนซ์ของการพึ่งพาตนเองได้อย่างไรและตระหนักถึงอินเทอร์เฟซที่ใช้เพื่อใช้งานเท่านั้น "
จาก: Anton Moiseev หนังสือ“ การพัฒนาเชิงมุมพร้อม Typescript, Second Edition”:
“ในระยะสั้นDIจะช่วยให้คุณเขียนโค้ดในคู่หลวมวิธีและทำให้คุณรหัสเพิ่มเติมทดสอบและนำมาใช้ใหม่ .”
คำง่าย ๆ ในการฉีดพึ่งพา (DI) เป็นวิธีที่จะเอาการอ้างอิงหรือคับระหว่างวัตถุต่าง ๆ การพึ่งพาการฉีดให้พฤติกรรมที่เหนียวแน่นกับแต่ละวัตถุ
DI คือการดำเนินการตามหลักการ IOC ของฤดูใบไม้ผลิซึ่งบอกว่า "อย่าโทรหาเราเราจะโทรหาคุณ" การใช้โปรแกรมเมอร์ฉีดพึ่งพาไม่จำเป็นต้องสร้างวัตถุโดยใช้คำหลักใหม่
เมื่อโหลดวัตถุใน Spring container แล้วเราจะนำมันกลับมาใช้ใหม่เมื่อใดก็ตามที่เราต้องการโดยดึงวัตถุเหล่านั้นจาก Spring container โดยใช้วิธี getBean (String beanName)
การฉีดพึ่งพาเป็นหัวใจของแนวคิดที่เกี่ยวข้องกับ Spring Framework ในขณะที่การสร้างกรอบของฤดูใบไม้ผลิโครงการใด ๆ ที่อาจมีบทบาทสำคัญและที่นี่ฉีดพึ่งพามาในเหยือก
ที่จริงแล้วสมมติว่าใน java คุณสร้างคลาสที่แตกต่างกันสองคลาสเป็นคลาส A และคลาส B และฟังก์ชันใดก็ตามที่มีให้ในคลาส B ที่คุณต้องการใช้ในคลาส A ดังนั้นในขณะนั้นสามารถใช้การฉีดแบบพึ่งพาได้ ที่ซึ่งคุณสามารถสร้างออบเจ็กต์ของคลาสหนึ่งในอีกชั้นหนึ่งในลักษณะเดียวกับที่คุณสามารถฉีดทั้งคลาสในคลาสอื่นเพื่อให้สามารถเข้าถึงได้ ด้วยวิธีนี้สามารถเอาชนะการพึ่งพา
การฉีดขึ้นอยู่กับการแบ่งเป็นสองชั้นอย่างง่ายและในเวลาเดียวกันทำให้พวกเขาแยกตัวออกจากกัน