การฉีดพึ่งพาคืออะไร?


3075

มีคำถามมากมายที่โพสต์ไว้แล้วพร้อมคำถามเฉพาะเกี่ยวกับการฉีดพึ่งพาเช่นเมื่อไรที่จะใช้มันและกรอบใดบ้างที่มีให้ อย่างไรก็ตาม

การฉีดพึ่งพาคืออะไรและเมื่อใด / ทำไมควรหรือไม่ควรใช้?


ดูการอภิปรายของฉันในการพึ่งพาการฉีดที่นี่
Kevin S.

33
ฉันเห็นด้วยกับความคิดเห็นเกี่ยวกับลิงค์ ฉันเข้าใจว่าคุณอาจต้องการอ้างอิงถึงคนอื่น แต่อย่างน้อยเพิ่มเหตุผลที่คุณจะเชื่อมโยงพวกเขาและสิ่งที่ทำให้การเชื่อมโยงนี้ดีกว่าการเชื่อมโยงอื่น ๆ ฉันจะได้รับโดยใช้ Google
คริสเตียนเพน

@AR: ในทางเทคนิคแล้วการพึ่งพาการฉีดไม่ใช่รูปแบบพิเศษของ IoC ค่อนข้าง IoC เป็นเทคนิคหนึ่งที่ใช้ในการให้การฉีดพึ่งพา เทคนิคอื่น ๆ สามารถนำมาใช้เพื่อให้การฉีดพึ่งพา (แม้ว่า IoC เป็นเพียงหนึ่งในการใช้งานทั่วไป) และ IoC ใช้สำหรับปัญหาอื่น ๆ อีกมากมายเช่นกัน
ฌอน Reilly

หนึ่งในคำอธิบายที่อร่อยที่สุดที่ฉันเคยอ่านเกี่ยวกับ DI มาจาก Guice ของ Google (เด่นชัดว่าเป็นน้ำผลไม้) http://code.google.com/p/google-guice/wiki/Motivation?tm=6
Raj

136
เกี่ยวกับลิงค์โปรดจำไว้ว่าพวกมันมักจะหายไปทางเดียว มีจำนวนลิงก์ที่เพิ่มมากขึ้นในคำตอบ SO ดังนั้นไม่ว่าบทความที่เชื่อมโยงจะดีแค่ไหนมันก็ไม่ดีเลยถ้าคุณไม่สามารถค้นหาได้
DOK

คำตอบ:


1931

การพึ่งพาการฉีดจะผ่านการอ้างอิงไปยังวัตถุหรือกรอบงานอื่น ๆ(หัวฉีดพึ่งพา)

การฉีดพึ่งพาทำให้การทดสอบง่ายขึ้น ฉีดสามารถทำได้ผ่านคอนสตรัค

SomeClass() มีนวกรรมิกดังนี้

public SomeClass() {
    myObject = Factory.getObject();
}

ปัญหา : ถ้าmyObjectเกี่ยวข้องกับงานที่ซับซ้อนเช่นการเข้าถึงดิสก์หรือการเข้าถึงเครือข่ายมันเป็นเรื่องยากSomeClass()ที่จะทำแบบทดสอบหน่วย โปรแกรมเมอร์ต้องล้อเลียนmyObjectและอาจขัดขวางการโทรจากโรงงาน

ทางเลือกอื่น ๆ :

  • การส่งผ่านmyObjectเป็นอาร์กิวเมนต์ไปยังตัวสร้าง
public SomeClass (MyClass myObject) {
    this.myObject = myObject;
}

myObject สามารถส่งผ่านโดยตรงซึ่งทำให้การทดสอบง่ายขึ้น

  • ทางเลือกหนึ่งที่พบได้ทั่วไปคือการกำหนดตัวสร้างที่ไม่ทำอะไรเลย การฉีดพึ่งพาสามารถทำได้ผ่านตัวตั้งค่า (h / t @MikeVella)
  • Martin Fowlerจัดทำเอกสารทางเลือกที่สาม (h / t @MarcDix) โดยที่คลาสใช้อินเทอร์เฟซสำหรับโปรแกรมเมอร์ที่ต้องพึ่งพาอย่างชัดเจน

มันยากที่จะแยกส่วนประกอบในการทดสอบหน่วยโดยไม่ต้องฉีดพึ่งพา

ในปี 2013 เมื่อผมเขียนคำตอบนี้นี้เป็นเรื่องสำคัญในบล็อกของ Google ทดสอบ มันยังคงเป็นข้อได้เปรียบที่ใหญ่ที่สุดสำหรับฉันเนื่องจากโปรแกรมเมอร์ไม่จำเป็นต้องมีความยืดหยุ่นเป็นพิเศษในการออกแบบรันไทม์ (ตัวอย่างเช่นสำหรับตัวระบุบริการหรือรูปแบบที่คล้ายกัน) โปรแกรมเมอร์มักจะต้องแยกชั้นเรียนในระหว่างการทดสอบ


25
รับทราบว่าการอ้างอิงของ Ben Hoffstein กับบทความของ Martin Fowler นั้นมีความจำเป็นในการชี้ให้เห็นว่า 'ต้องอ่าน' ในหัวข้อนี้ฉันยอมรับคำตอบ wds เพราะจริง ๆ แล้วตอบคำถามที่นี่
AR

121
+1 สำหรับคำอธิบายและแรงจูงใจ: ทำให้การสร้างวัตถุที่ระดับขึ้นอยู่กับปัญหาของคนอื่น อีกวิธีที่จะบอกว่า DI ทำให้คลาสมีความเหนียวแน่นมากขึ้น (มีความรับผิดชอบน้อยลง)
Fuhrmanator

13
คุณบอกว่าการพึ่งพาถูกส่งผ่าน "ในตัวสร้าง" แต่เนื่องจากฉันเข้าใจว่านี่ไม่เป็นความจริงอย่างเคร่งครัด มันยังคงฉีดขึ้นอยู่กับการพึ่งพาหากการตั้งค่าการพึ่งพาเป็นคุณสมบัติหลังจากวัตถุได้รับการยกตัวอย่างถูกต้องหรือไม่
Mike Vella

1
@MikeVella ใช่ถูกต้องแล้ว มันไม่ได้สร้างความแตกต่างอย่างแท้จริงในกรณีส่วนใหญ่แม้ว่าคุณสมบัติโดยทั่วไปจะมีความยืดหยุ่นมากกว่าเล็กน้อย ฉันจะแก้ไขข้อความเล็กน้อยเพื่อชี้ให้เห็น
wds

2
หนึ่งในคำตอบที่ดีที่สุดที่ฉันเคยพบมาดังนั้นฉันสนใจที่จะปรับปรุงมัน มันหายไปรายละเอียดของรูปแบบที่สามของการฉีดพึ่งพา: ฉีดอินเตอร์เฟซ
Marc Dix

2351

คำจำกัดความที่ดีที่สุดที่ฉันเคยพบมาคือJames ฝั่ง :

"การพึ่งพาการฉีด" เป็นคำที่ 25 ดอลลาร์สำหรับแนวคิดร้อยละ 5 [... ] การพึ่งพาการฉีดหมายถึงการให้วัตถุตัวแปรอินสแตนซ์ของมัน [ ... ]

มีบทความโดย Martin Fowlerที่อาจพิสูจน์ว่ามีประโยชน์เช่นกัน

โดยทั่วไปการพึ่งพาการฉีดให้วัตถุที่วัตถุต้องการ (การอ้างอิง) แทนที่จะสร้างมันเอง มันเป็นเทคนิคที่มีประโยชน์มากสำหรับการทดสอบเพราะมันช่วยให้การพึ่งพาถูกเยาะเย้ยหรือถูกขัดจังหวะ

การพึ่งพาสามารถฉีดเข้าไปในวัตถุได้หลายวิธี (เช่นการฉีดคอนสตรัคเตอร์หรือการฉีดเซทเทอร์) เราสามารถใช้กรอบการฉีดแบบพึ่งพา (เช่นสปริง) เพื่อทำสิ่งนั้น แต่พวกเขาไม่จำเป็นต้องใช้แน่นอน คุณไม่จำเป็นต้องมีกรอบการทำงานเหล่านี้เพื่อทำการฉีด การสร้างอินสแตนซ์และการผ่านวัตถุ (การพึ่งพา) ชัดเจนว่าเป็นการฉีดที่ดีเหมือนการฉีดตามกรอบ


35
ฉันชอบคำอธิบายของบทความของเจมส์โดยเฉพาะตอนท้าย: "ถึงกระนั้นคุณต้องประหลาดใจกับวิธีการที่ใช้แนวคิดสามประการ ('TripPlanner,' 'CabAgency,' และ 'AirlineAgency') เปลี่ยนพวกมันเป็นคลาสเก้าบวก จากนั้นเพิ่มโค้ดบรรทัดกาวและ XML การกำหนดค่าหลายสิบบรรทัดก่อนที่จะเขียนแอปพลิเคชันตรรกะบรรทัดเดียว " นี่คือสิ่งที่ฉันได้เห็นบ่อยมาก (เศร้า) - ที่ฉีดพึ่งพา (ซึ่งเป็นสิ่งที่ดีต่อ se ตามที่อธิบายไว้โดยเขา) จะถูกนำไปใช้กับสิ่ง overcomplicate ที่จะได้รับการดำเนินการได้ง่ายขึ้น - สิ้นสุดการเขียน "สนับสนุน" รหัส ...
แมตต์

2
Re: "การสร้างอินสแตนซ์และวัตถุที่ผ่าน (การอ้างอิง) ชัดเจนว่าเป็นการฉีดที่ดีเหมือนการฉีดตามกรอบ" แล้วทำไมผู้คนถึงทำโครงร่างทำอย่างนั้น?
dzieciou

13
ด้วยเหตุผลเดียวกันกับที่ทุกเฟรมเวิร์กเขียน (หรืออย่างน้อยควรได้รับ) เขียน: เนื่องจากมีรหัสซ้ำจำนวนมาก / สำเร็จรูปที่ต้องถูกเขียนเมื่อคุณมีความซับซ้อน ปัญหาคือหลายครั้งที่คนจะเข้าถึงกรอบแม้ว่ามันไม่จำเป็น
Thiago Arrais

14
เทอม $ 25 สำหรับคอนเซ็ปต์ 5 เซ็นต์กำลังจะตาย นี่เป็นบทความที่ดีที่ช่วยฉันได้: codeproject.com/Articles/615139/…
Christine

@ ระบุไฟล์การกำหนดค่าเป็นเพียง "เลื่อนการตัดสินใจ" ไปสุดขีด - "เลื่อนการตัดสินใจจนกว่าจะใช้งานจริง" กริชและโดยเฉพาะอย่างยิ่งกริชมีในความคิดของฉันพบจุดหวาน "เลื่อนการตัดสินใจจนถึงเวลาแอพลิเคชันประกอบ"
Thorbjørn Ravn Andersen

645

ฉันพบตัวอย่างที่ตลกนี้ในแง่ของการมีเพศสัมพันธ์แบบหลวม :

แอปพลิเคชันใด ๆ ที่ประกอบด้วยวัตถุจำนวนมากที่ทำงานร่วมกันเพื่อทำสิ่งที่มีประโยชน์ ตามเนื้อผ้าแต่ละวัตถุมีหน้าที่รับผิดชอบในการได้รับการอ้างอิงของตัวเองไปยังวัตถุที่อ้างถึง (อ้างอิง) มันร่วมมือกับ สิ่งนี้นำไปสู่คลาสที่มีความสัมพันธ์สูงและรหัสที่ยากต่อการทดสอบ

ตัวอย่างเช่นพิจารณา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;
  }
}

ที่มา: ทำความเข้าใจกับการฉีดพึ่งพา


20
วิธีที่ฉันเข้าใจคือแทนการสร้างวัตถุใหม่เป็นส่วนหนึ่งของวัตถุอื่นเราสามารถฉีดวัตถุดังกล่าวเมื่อใดและถ้าจำเป็นดังนั้นจึงเป็นการลบการพึ่งพาวัตถุแรกของวัตถุนั้น นั่นถูกต้องใช่ไหม?
JeliBeanMachine

ฉันได้อธิบายเรื่องนี้พร้อมกับตัวอย่างร้านกาแฟที่นี่: digigene.com/design-patterns/dependency-injection-coffeeshop
Ali Nem

11
ชอบการเปรียบเทียบนี้เพราะเป็นภาษาอังกฤษธรรมดาโดยใช้การเปรียบเทียบง่าย ๆ บอกว่าฉันโตโยต้าใช้เวลามากเกินไปทางการเงินและกำลังคนในการทำรถจากการออกแบบเพื่อกลิ้งออกจากสายการประกอบแล้วถ้ามีผู้ผลิตยางรถยนต์ที่มีชื่อเสียงที่มีอยู่เหตุผลที่ผมควรจะเริ่มต้นจากรอยขีดข่วนที่จะทำให้ส่วนการผลิตยางเช่นการnewยาง? ฉันไม่. สิ่งที่ฉันต้องทำคือซื้อ (ฉีดผ่าน param) จากพวกเขาติดตั้งและวา - ลาห์! ดังนั้นเมื่อกลับมาที่การเขียนโปรแกรมสมมติว่าโครงการ C # จำเป็นต้องใช้ไลบรารี / คลาสที่มีอยู่มีสองวิธีในการรัน / ดีบักการอ้างอิงเพิ่ม 1 รายการกับโครงการทั้งหมดนี้
Jeb50

(con't), .. ไลบรารี / คลาสภายนอกหรือ 2 เพิ่มจาก DLL นอกเสียจากว่าเราจะต้องดูว่ามีอะไรอยู่ภายในคลาสภายนอกนี้การเพิ่มเป็น DLL เป็นวิธีที่ง่าย ดังนั้นตัวเลือกที่ 1 คือnewมันตัวเลือกที่ 2 คือผ่านมันเป็นพารามิเตอร์ อาจไม่ถูกต้อง แต่โง่ง่ายเข้าใจง่าย
Jeb50

1
@JeliBeanMachine (ขออภัยที่ตอบกลับมาช้ามากสำหรับความคิดเห็น .. ) ไม่ใช่ว่าเราจะลบการอ้างอิงของวัตถุแรกในวัตถุล้อหรือวัตถุแบตเตอรี่มันคือว่าเราผ่านการพึ่งพาเพื่อให้เราสามารถเปลี่ยนอินสแตนซ์หรือการใช้งานของ เมืองขึ้น ก่อนหน้า: รถยนต์มีการพึ่งพาฮาร์ดโค้ดบน NepaliRubberWheel หลัง: รถยนต์มีการพึ่งพาการฉีดในตัวอย่างของล้อ
Mikael Ohlson

263

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ไม่จำเป็นต้องรู้เกี่ยวกับการเปลี่ยนแปลง


29
คงจะดีมากถ้าคุณสามารถยกตัวอย่างรหัสเดียวกันหลังจากใช้ DI
CodyBugstein

170

คำตอบที่ได้รับการยอมรับเป็นคำตอบที่ดี - แต่ฉันต้องการเพิ่มในส่วนนี้ว่า DI นั้นเหมือนกับการหลีกเลี่ยงค่าคงที่แบบฮาร์ดโค้ดในรหัส

เมื่อคุณใช้ค่าคงที่เช่นชื่อฐานข้อมูลคุณจะย้ายอย่างรวดเร็วจากด้านในของรหัสไปยังไฟล์กำหนดค่าบางส่วนและส่งผ่านตัวแปรที่มีค่านั้นไปยังตำแหน่งที่ต้องการ เหตุผลในการทำเช่นนั้นก็คือค่าคงที่เหล่านี้มักจะเปลี่ยนบ่อยกว่าส่วนที่เหลือของรหัส ตัวอย่างเช่นหากคุณต้องการทดสอบโค้ดในฐานข้อมูลทดสอบ

DI นั้นคล้ายคลึงกับสิ่งนี้ในโลกของการเขียนโปรแกรม Object Oriented ค่าที่นั่นแทนที่จะเป็นตัวอักษรคงที่คือวัตถุทั้งหมด - แต่เหตุผลในการย้ายรหัสที่สร้างพวกเขาออกมาจากรหัสชั้นเรียนมีความคล้ายคลึงกัน - วัตถุเปลี่ยนบ่อยขึ้นแล้วรหัสที่ใช้พวกเขา กรณีที่สำคัญอย่างหนึ่งที่จำเป็นต้องมีการเปลี่ยนแปลงคือการทดสอบ


18
+1 "วัตถุเปลี่ยนบ่อยขึ้นแล้วรหัสที่ใช้" ในการพูดคุยทั่วไปให้เพิ่มทางอ้อมที่จุดไหล ขึ้นอยู่กับตำแหน่งของฟลักซ์ทิศทางจะถูกเรียกโดยชื่อต่าง ๆ !!
Chethan

139

ลองตัวอย่างง่ายๆกับคลาส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 เชื่อมต่อกับระบบบริการเดียวกันนี้


2
เพิ่งเกิดจากความอยากรู้สิ่งนี้แตกต่างจากรูปแบบกลยุทธ์หรือไม่ รูปแบบนี้ถูกห่อหุ้มอัลกอริทึมและทำให้พวกมันใช้แทนกันได้ มันให้ความรู้สึกเหมือนการพึ่งพาการฉีดและรูปแบบกลยุทธ์คล้ายกันมาก
ยาอายุวัฒนะ

110

ลองนึกภาพว่าคุณต้องการตกปลา:

  • คุณจะต้องดูแลทุกอย่างด้วยตัวเอง คุณต้องหาเรือซื้อเบ็ดตกปลาเพื่อหาเหยื่อ ฯลฯ เป็นไปได้แน่นอน แต่มันมีความรับผิดชอบมากสำหรับคุณ ในแง่ซอฟต์แวร์หมายความว่าคุณต้องค้นหาสิ่งเหล่านี้ทั้งหมด

  • ด้วยการฉีดพึ่งพาคนอื่นดูแลการเตรียมและทำให้อุปกรณ์ที่จำเป็นสำหรับคุณ คุณจะได้รับ ("ถูกฉีด") เรือเบ็ดตกปลาและเหยื่อ - ทั้งหมดพร้อมใช้งาน


59
ลองจินตนาการดูว่าคุณจ้างช่างประปาทำซ้ำห้องน้ำของคุณแล้วใครบอกว่า "เยี่ยมมากนี่คือรายการเครื่องมือและวัสดุที่ฉันต้องการให้คุณเอามาให้ฉัน" ไม่ควรที่จะเป็นงานช่างประปาหรือไม่
jscs

เพื่อให้ใครบางคนต้องการที่จะดูแลคนบางคนมันไม่มีธุรกิจที่รู้ .. แต่ยังตัดสินใจที่จะรวบรวมรายชื่อของเรือติดและเหยื่อ - แม้ว่าจะพร้อมที่จะใช้
Chookoos

22
@JoshCaswell ไม่ว่าจะเป็นงานของช่างประปา ในฐานะลูกค้าคุณต้องทำการประปา เพื่อที่คุณจะต้องช่างประปา ช่างประปาต้องการเครื่องมือมัน เพื่อให้ได้มามันได้รับการติดตั้งโดย บริษัท ท่อประปา ในฐานะลูกค้าคุณไม่ต้องการรู้ว่าช่างประปาทำอะไรหรือต้องการอะไร ในฐานะช่างประปาคุณรู้ว่าคุณต้องการอะไร แต่คุณแค่ต้องการทำงานไม่ใช่รับทุกสิ่ง ในฐานะนายจ้างช่างประปาคุณต้องรับผิดชอบในการติดตั้งช่างประปาด้วยสิ่งที่พวกเขาต้องการก่อนส่งพวกเขาไปที่บ้านของผู้คน
sara

@ ไก่ฉันเข้าใจประเด็นของคุณ ในซอฟต์แวร์ที่เรากำลังพูดถึงโรงงานถูกต้องหรือไม่ แต่โดยปกติ DI ยังหมายถึงคลาสไม่ได้ใช้โรงงานเนื่องจากมันยังไม่ถูกฉีด คุณลูกค้าจะต้องติดต่อนายจ้าง (โรงงาน) เพื่อมอบเครื่องมือให้กับคุณเพื่อให้คุณสามารถส่งไปยังช่างประปา นั่นไม่ใช่วิธีการทำงานในโปรแกรมจริงเหรอ? ดังนั้นในขณะที่ลูกค้า (การเรียกคลาส / ฟังก์ชั่น / อะไรก็ตาม) ไม่จำเป็นต้องจัดหาเครื่องมือพวกเขายังคงต้องเป็นคนกลางเพื่อให้แน่ใจว่าพวกเขาทำมันให้กับช่างประปา (คลาสฉีด) จากนายจ้าง (โรงงาน)
KingOfAllTrades

1
@ KingOfAllTrades: แน่นอนในบางจุดคุณต้องมีคนจ้างและติดตั้งช่างประปาหรือคุณไม่มีช่างประปา แต่คุณไม่มีลูกค้าที่ทำมัน ลูกค้าเพียงแค่ขอช่างประปาและได้รับสิ่งที่เขาต้องการแล้วในการทำงานของเขา ด้วย DI ในที่สุดคุณก็ยังมีรหัสบางส่วนเพื่อเติมเต็มการอ้างอิง แต่คุณแยกมันออกจากโค้ดที่ใช้งานได้จริง หากคุณนำมันไปใช้อย่างเต็มที่วัตถุของคุณก็จะรู้จักการอ้างอิงและการสร้างวัตถุกราฟจะเกิดขึ้นภายนอกซึ่งมักจะอยู่ในรหัสเริ่ม
cHao

102

นี่คือคำอธิบายที่ง่ายที่สุดเกี่ยวกับการฉีดพึ่งพาและคอนเทนเนอร์ฉีดพึ่งพาฉันเคยเห็น:

โดยไม่ต้องพึ่งพาการฉีด

  • แอปพลิเคชั่นต้องการ Foo (เช่นคอนโทรลเลอร์) ดังนั้น:
  • แอพลิเคชันสร้าง Foo
  • แอปพลิเคชันโทร Foo
    • Foo ต้องการ Bar (เช่นบริการ) ดังนั้น:
    • ฟูสร้างบาร์
    • ฟูเรียกบาร์
      • Bar ต้องการ Bim (บริการ, ที่เก็บ, ... ) ดังนั้น:
      • บาร์สร้างบิม
      • บาร์ทำอะไรบางอย่าง

ด้วยการฉีดพึ่งพา

  • แอปพลิเคชั่นต้องการ Foo ซึ่งต้องการ Bar ซึ่งต้องการ Bim ดังนั้น:
  • แอพลิเคชันสร้างบิม
  • แอพลิเคชันสร้างแถบและให้ Bim
  • แอพลิเคชันสร้าง Foo และให้บาร์
  • แอปพลิเคชันโทร Foo
    • ฟูเรียกบาร์
      • บาร์ทำอะไรบางอย่าง

ใช้คอนเทนเนอร์ฉีดพึ่งพา

  • แอปพลิเคชั่นต้องการฟูดังนั้น:
  • แอปพลิเคชันรับ Foo จากคอนเทนเนอร์ดังนั้น:
    • คอนเทนเนอร์สร้างบิม
    • Container สร้าง Bar และให้ Bim
    • Container สร้าง Foo และให้ Bar
  • แอปพลิเคชันโทร Foo
    • ฟูเรียกบาร์
      • บาร์ทำอะไรบางอย่าง

การฉีดพึ่งพาและภาชนะฉีดขึ้นอยู่กับสิ่งที่แตกต่าง:

  • พึ่งพาการฉีดเป็นวิธีการเขียนรหัสที่ดีกว่า
  • DI Container เป็นเครื่องมือที่ช่วยในการฉีดการพึ่งพา

คุณไม่จำเป็นต้องใช้ตู้คอนเทนเนอร์เพื่อทำการฉีด อย่างไรก็ตามคอนเทนเนอร์สามารถช่วยคุณได้



55

"การฉีดพึ่งพา" ไม่ได้หมายถึงเพียงแค่ใช้การกำหนดพารามิเตอร์แบบสร้างและเซ็ตสาธารณะ?

บทความเจมส์ชอร์แสดงให้เห็นตัวอย่างต่อไปนี้สำหรับการเปรียบเทียบ

ตัวสร้างโดยไม่ต้องพึ่งพาการฉีด:

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(); 
    ... 
  } 
}

แน่นอนในรุ่น DI คุณจะไม่ต้องการเริ่มต้นวัตถุ myDatabase ในตัวสร้างอาร์กิวเมนต์ไม่? ดูเหมือนไม่มีจุดและจะให้ข้อยกเว้นหากคุณพยายามโทร DoStuff โดยไม่เรียก Constructor ที่โอเวอร์โหลด?
Matt Wilko

เฉพาะในกรณีที่new DatabaseThingie()ไม่ได้สร้างอินสแตนซ์ myDatabase ที่ถูกต้อง
JaneGoodall

40

เพื่อให้แนวคิดการพึ่งพาการฉีดง่ายต่อการเข้าใจ ลองมาตัวอย่างของปุ่มสลับเพื่อสลับ (เปิด / ปิด) หลอดไฟ

โดยไม่ต้องพึ่งพาการฉีด

สวิตช์จำเป็นต้องทราบล่วงหน้าว่าหลอดไฟใดที่ฉันเชื่อมต่ออยู่ (การอ้างอิงแบบฮาร์ดโค้ด) ดังนั้น,

สวิตช์ -> 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(); 
    ... 
  } 
}`

36

การพึ่งพาการฉีด (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บนโรงงานได้ตามต้องการและกำจัดอินสแตนซ์เหล่านี้เมื่อเสร็จสมบูรณ์
  • หรือการควบคุมอินสแตนซ์ของการพึ่งพาอายุการใช้งานสามารถส่งไปยังคอนเทนเนอร์ IoC (เพิ่มเติมเกี่ยวกับเรื่องนี้ด้านล่าง)

ควรใช้ 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อินสแตนซ์" )
  • นโยบายสามารถตั้งค่าสำหรับการจัดการอายุการใช้งานของการอ้างอิงแต่ละรายการเช่นการสร้างวัตถุใหม่สำหรับแต่ละอินสแตนซ์ผู้บริโภคเพื่อแบ่งปันอินสแตนซ์การพึ่งพาแบบซิงเกิลในผู้บริโภคทั้งหมดเพื่อแบ่งปันอินสแตนซ์การพึ่งพาเดียวกันผ่านเธรดเดียวกันเท่านั้น
  • ใน. Net คอนเทนเนอร์ IoC ตระหนักถึงโปรโตคอลเช่นนั้นIDisposableและจะรับผิดชอบเกี่ยวDisposingกับการพึ่งพาซึ่งสอดคล้องกับการจัดการอายุการใช้งานที่กำหนดไว้

โดยทั่วไปเมื่อคอนเทนเนอร์ IoC ได้รับการกำหนดค่า / bootstrapped พวกเขาทำงานได้อย่างราบรื่นในพื้นหลังทำให้ coder มุ่งเน้นไปที่รหัสในมือมากกว่ากังวลเกี่ยวกับการพึ่งพา

กุญแจสำคัญในรหัส DI ง่ายคือการหลีกเลี่ยงการมีเพศสัมพันธ์แบบคงที่ของชั้นเรียนและไม่ใช้ใหม่ () สำหรับการสร้างการพึ่งพา

ตามตัวอย่างข้างบนการแยกการพึ่งพาต้องอาศัยความพยายามในการออกแบบและสำหรับนักพัฒนามีการเปลี่ยนกระบวนทัศน์ที่จำเป็นในการทำลายนิสัยของการnewพึ่งพาโดยตรงและแทนที่จะไว้วางใจคอนเทนเนอร์เพื่อจัดการการพึ่งพา

แต่คุณประโยชน์มากมายโดยเฉพาะอย่างยิ่งในความสามารถในการทดสอบระดับความสนใจ

หมายเหตุ : การสร้าง / การทำแผนที่ / ฉาย (ผ่านnew ..()) ของประมาณการ POCO / POJO / อันดับ DTOs / Entity กราฟ / ไม่ประสงค์ออกนาม JSON, et al - คือ "เท่านั้นข้อมูล" เรียนหรือระเบียน - การใช้หรือกลับมาจากวิธีการที่จะไม่ได้รับการยกย่องเป็นพึ่งพา (ใน ความหมาย UML) และไม่อยู่ภายใต้ DI การใช้newโปรเจ็กต์เหล่านี้ก็ใช้ได้


1
ปัญหาคือ DIP! = DI กรมทรัพย์สินทางปัญญาเป็นเรื่องเกี่ยวกับการแยกส่วนที่เป็นนามธรรมออกจากการใช้งาน: A. โมดูลระดับสูงไม่ควรขึ้นอยู่กับโมดูลระดับต่ำ ทั้งสองควรขึ้นอยู่กับ abstractions B. บทคัดย่อไม่ควรขึ้นอยู่กับรายละเอียด รายละเอียดควรขึ้นอยู่กับ abstractions DI เป็นวิธีในการแยกการสร้างวัตถุออกจากการใช้วัตถุ
Ricardo Rivaldo

ใช่ความแตกต่างถูกระบุไว้อย่างชัดเจนในวรรค 2 ของฉัน"DI หนึ่งในการนำไปใช้ที่เป็นไปได้ของกรมทรัพย์สินทางปัญญา"ในกระบวนทัศน์ SOLID ของลุงบ็อบ ฉันได้ทำให้ชัดเจนในโพสต์ก่อนหน้านี้
StuartLC

25

จุดรวมของการพึ่งพาการฉีด (DI) คือการทำให้ซอร์สโค้ดแอปพลิเคชันสะอาดและเสถียร :

  • ทำความสะอาดของรหัสเริ่มต้นการพึ่งพา
  • มีความเสถียรโดยไม่คำนึงถึงการใช้

ในทางปฏิบัติทุกรูปแบบการออกแบบจะแยกข้อกังวลออกเพื่อให้การเปลี่ยนแปลงในอนาคตมีผลต่อไฟล์ขั้นต่ำ

โดเมนเฉพาะของ 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โดยใช้หรือtarxz

ตัวอย่าง: การลบ DI

หาก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

แต่มันเป็นวิธีการทั่วไปในการแยกข้อกังวลของ:

  • การพัฒนาแอพพลิเคชั่น( วงจรชีวิตของการปลดปล่อยซอร์สโค้ดเดียว )
  • การปรับใช้แอปพลิเคชัน( สภาพแวดล้อมเป้าหมายหลายเป้าหมายพร้อมวงจรชีวิตอิสระ)

การใช้การกำหนดค่าเฉพาะกับการค้นหาการพึ่งพาไม่ได้ช่วยเนื่องจากจำนวนพารามิเตอร์การกำหนดค่าอาจเปลี่ยนแปลงตามการอ้างอิง (เช่นชนิดการพิสูจน์ตัวตนใหม่) รวมทั้งจำนวนประเภทการพึ่งพาที่รองรับ (เช่นชนิดฐานข้อมูลใหม่)


ฉันจะเพิ่มความสามารถในการทำให้คลาสเฉพาะ (การทดสอบ) เสร็จสมบูรณ์โดยไม่ต้องทำการอ้างอิงให้เสร็จสมบูรณ์ซึ่งเป็นจุดประสงค์สำหรับ DI
David

22

คำตอบทั้งหมดข้างต้นเป็นสิ่งที่ดีเป้าหมายของฉันคือการอธิบายแนวคิดด้วยวิธีง่าย ๆ เพื่อให้ทุกคนที่ไม่มีความรู้ด้านการเขียนโปรแกรมสามารถเข้าใจแนวคิดได้

การฉีดพึ่งพาเป็นหนึ่งในรูปแบบการออกแบบที่ช่วยให้เราสร้างระบบที่ซับซ้อนในลักษณะที่ง่ายขึ้น

เราสามารถเห็นความหลากหลายของรูปแบบนี้ในชีวิตประจำวันของเรา ตัวอย่างบางส่วน ได้แก่ เครื่องบันทึกเทป, VCD, ซีดีไดรฟ์เป็นต้น

เครื่องบันทึกเทปพกพาแบบรีลต่อรีล, กลางศตวรรษที่ 20

ภาพด้านบนเป็นภาพของเครื่องบันทึกเทปแบบพกพาที่ม้วนเก็บม้วนในช่วงกลางศตวรรษที่ 20 แหล่ง

ความตั้งใจหลักของเครื่องบันทึกเทปคือการบันทึกหรือเล่นเสียง

ในขณะที่การออกแบบระบบมันต้องใช้รอกเพื่อบันทึกหรือเล่นเสียงหรือเพลง มีสองความเป็นไปได้สำหรับการออกแบบระบบนี้

  1. เราสามารถวางรีลภายในเครื่อง
  2. เราสามารถให้ตะขอสำหรับรอกที่สามารถวาง

ถ้าเราใช้อันแรกเราต้องเปิดเครื่องเพื่อเปลี่ยนล้อ หากเราเลือกที่สองนั่นคือการวางเบ็ดสำหรับรีลเราจะได้รับประโยชน์เพิ่มเติมจากการเล่นเพลงใด ๆ โดยการเปลี่ยนรีล และยังลดฟังก์ชั่นเฉพาะการเล่นอะไรก็ตามที่อยู่ในรอก

เช่นการฉีดพึ่งพาที่ชาญฉลาดเป็นกระบวนการของการส่งออกการพึ่งพาที่จะมุ่งเน้นเฉพาะฟังก์ชั่นที่เฉพาะเจาะจงขององค์ประกอบเพื่อให้ส่วนประกอบที่เป็นอิสระสามารถเชื่อมต่อกันเพื่อสร้างระบบที่ซับซ้อน

ประโยชน์หลักที่เราประสบความสำเร็จโดยใช้การฉีดพึ่งพา

  • การติดต่อกันสูงและการมีเพศสัมพันธ์หลวม
  • การพึ่งพาจากภายนอกและมองหาเพียงความรับผิดชอบ
  • ทำสิ่งต่าง ๆ เป็นส่วนประกอบและรวมเข้าด้วยกันเพื่อสร้างระบบขนาดใหญ่ที่มีความสามารถสูง
  • ช่วยในการพัฒนาส่วนประกอบที่มีคุณภาพสูงเนื่องจากได้รับการพัฒนาอย่างอิสระและได้รับการทดสอบอย่างเหมาะสม
  • ช่วยในการเปลี่ยนส่วนประกอบด้วยอีกถ้าล้มเหลว

วันนี้แนวคิดเหล่านี้เป็นพื้นฐานของกรอบที่รู้จักกันดีในโลกการเขียนโปรแกรม 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


17

ฉีดพึ่งพาคืออะไร?

Dependency Injection (DI) หมายถึงการแยกวัตถุที่ขึ้นอยู่กับแต่ละอื่น ๆ สมมติว่า object A ขึ้นอยู่กับ Object B ดังนั้นแนวคิดคือการแยกวัตถุเหล่านี้ออกจากกัน เราไม่จำเป็นต้องใช้รหัสยากกับวัตถุที่ใช้คำหลักใหม่แทนที่จะแบ่งปันการพึ่งพาวัตถุที่รันไทม์ทั้งๆที่รวบรวมเวลา ถ้าเราพูดถึง

วิธีการพึ่งพาการฉีดทำงานในฤดูใบไม้ผลิ:

เราไม่จำเป็นต้องรหัสยากวัตถุโดยใช้คำหลักใหม่ค่อนข้างกำหนดถั่วพึ่งพาในไฟล์การกำหนดค่า ภาชนะสปริงจะรับผิดชอบการเชื่อมต่อทั้งหมด

การผกผันของการควบคุม (IOC)

IOC เป็นแนวคิดทั่วไปและสามารถแสดงออกได้หลายวิธีและการพึ่งพาการฉีดเป็นตัวอย่างที่ชัดเจนของ IOC

การพึ่งพาการฉีดสองประเภท:

  1. ตัวสร้างการฉีด
  2. Setter Injection

1. การฉีดพึ่งพาคอนสตรัค:

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>

2. การฉีดพึ่งพา Setter-based:

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 เป็นการอ้างอิงที่จำเป็น


15

การเปรียบเทียบที่ดีที่สุดที่ฉันนึกได้คือศัลยแพทย์และผู้ช่วยของเขาในโรงละครผ่าตัดซึ่งศัลยแพทย์เป็นบุคคลหลักและผู้ช่วยของเขาซึ่งเป็นผู้ให้การผ่าตัดในเวลาที่เขาต้องการเพื่อให้ศัลยแพทย์สามารถตั้งสมาธิได้ สิ่งที่เขาทำได้ดีที่สุด (การผ่าตัด) หากไม่มีผู้ช่วยศัลยแพทย์จะต้องนำส่วนประกอบมาเองทุกครั้งที่ต้องการ

DI สำหรับระยะสั้นเป็นเทคนิคที่จะลบความรับผิดชอบเพิ่มเติมทั่วไป (ภาระ) ในส่วนประกอบเพื่อดึงส่วนประกอบที่ขึ้นอยู่กับโดยให้พวกเขาไป

DI นำคุณใกล้ชิดกับหลักการ Single รับผิดชอบ (อาร์) surgeon who can concentrate on surgeryเช่น

จะใช้ DI เมื่อใด: ฉันแนะนำให้ใช้ DI ในโครงการผลิตเกือบทั้งหมด (เล็ก / ใหญ่) โดยเฉพาะในสภาพแวดล้อมทางธุรกิจที่เปลี่ยนแปลง :)

ทำไม: เพราะคุณต้องการให้รหัสของคุณสามารถทดสอบได้ง่าย ๆ เลียนแบบและอื่น ๆ เพื่อให้คุณสามารถทดสอบการเปลี่ยนแปลงได้อย่างรวดเร็วและผลักดันมันออกสู่ตลาด นอกจากทำไมคุณจะไม่เมื่อคุณมีเครื่องมือ / กรอบฟรีที่ยอดเยี่ยมมากมายที่จะสนับสนุนคุณในการเดินทางไปยัง codebase ซึ่งคุณสามารถควบคุมได้มากขึ้น


@WindRider ขอบคุณ ฉันไม่เห็นด้วยเพิ่มเติม ชีวิตมนุษย์และร่างกายมนุษย์เป็นตัวอย่างที่ยอดเยี่ยมของความเป็นเลิศด้านการออกแบบ ... กระดูกสันหลังเป็นตัวอย่างที่ยอดเยี่ยมของ ESB:) ...
Anwar Husain

15

ตัวอย่างเช่นเรามี 2 ชั้นและClient จะใช้ServiceClientService

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ที่อ้างถึง( ) ที่จะใช้มัน


13

มันหมายความว่าวัตถุควรมีการอ้างอิงมากเท่าที่จำเป็นในการทำงานของพวกเขาและการพึ่งพาควรจะน้อย นอกจากนี้การพึ่งพาของวัตถุควรอยู่ในส่วนต่อประสานและไม่ใช่วัตถุที่เป็นรูปธรรมเมื่อเป็นไปได้ (วัตถุที่เป็นรูปธรรมคือวัตถุใด ๆ ที่สร้างขึ้นด้วยคำหลักใหม่) การมีเพศสัมพันธ์แบบหลวมช่วยให้สามารถนำกลับมาใช้ใหม่ได้ง่ายขึ้นบำรุงรักษาง่ายขึ้น

“ การพึ่งพาการฉีด” (DI) หรือที่เรียกว่า“ การกลับกันของการควบคุม” (IoC) สามารถใช้เป็นเทคนิคในการกระตุ้นการมีเพศสัมพันธ์แบบหลวม ๆ

มีสองวิธีหลักในการนำ DI ไปใช้:

  1. ตัวสร้างการฉีด
  2. ฉีด Setter

ตัวสร้างการฉีด

มันเป็นเทคนิคของการส่งผ่านการพึ่งพาวัตถุไปยังตัวสร้าง

โปรดสังเกตว่านวกรรมิกยอมรับส่วนต่อประสานไม่ใช่วัตถุที่เป็นรูปธรรม นอกจากนี้โปรดทราบว่ามีการโยนข้อยกเว้นหากพารามิเตอร์ orderDao เป็นโมฆะ สิ่งนี้เน้นความสำคัญของการได้รับการอ้างอิงที่ถูกต้อง ในความคิดของฉันการสร้างคอนสตรัคชันนั้นเป็นกลไกที่ต้องการสำหรับการพึ่งพาวัตถุ เป็นที่ชัดเจนสำหรับนักพัฒนาในขณะที่เรียกใช้วัตถุที่ต้องมีการอ้างอิงไปยังวัตถุ“ บุคคล” สำหรับการดำเนินการที่เหมาะสม

Setter Injection

แต่ลองพิจารณาตัวอย่างต่อไปนี้ ... สมมติว่าคุณมีคลาสที่มีสิบวิธีที่ไม่มีการพึ่งพา แต่คุณกำลังเพิ่มวิธีการใหม่ที่มีการพึ่งพา IDAO คุณสามารถเปลี่ยน Constructor ให้ใช้ Constructor Injection ได้ แต่สิ่งนี้อาจบังคับให้คุณเปลี่ยนการเรียก Constructor ทั้งหมดได้ อีกวิธีหนึ่งคุณสามารถเพิ่มตัวสร้างใหม่ที่ใช้การพึ่งพา แต่แล้วนักพัฒนาจะรู้ได้อย่างไรว่าจะใช้ตัวสร้างตัวหนึ่งทับอีกตัวหนึ่งได้อย่างไร ในที่สุดหากการพึ่งพามีราคาแพงมากในการสร้างทำไมมันควรจะถูกสร้างขึ้นและส่งผ่านไปยังตัวสร้างเมื่อมันอาจถูกนำมาใช้เพียงเล็กน้อยเท่านั้น? “ Setter Injection” เป็นอีกหนึ่งเทคนิค DI ที่สามารถใช้ในสถานการณ์เช่นนี้

Setter Injection ไม่ได้บังคับให้ต้องพึ่งพาการส่งผ่านไปยังตัวสร้าง แต่การอ้างอิงจะถูกตั้งค่าไปยังคุณสมบัติสาธารณะที่ถูกเปิดเผยโดยวัตถุที่ต้องการ ตามที่ระบุไว้ก่อนหน้านี้แรงจูงใจหลักในการทำสิ่งนี้ ได้แก่ :

  1. สนับสนุนการฉีดพึ่งพาโดยไม่ต้องปรับเปลี่ยนนวกรรมิกของคลาสดั้งเดิม
  2. การอนุญาตให้สร้างทรัพยากรหรือบริการที่มีราคาแพงให้ช้าที่สุดเท่าที่จะเป็นไปได้และเมื่อจำเป็นเท่านั้น

นี่คือตัวอย่างลักษณะของโค้ดด้านบน:

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;

3
ฉันคิดว่าย่อหน้าแรกของคุณอยู่ห่างจากคำถามและไม่ได้นิยาม DI ทั้งหมด (เช่นคุณกำลังพยายามนิยาม SOLID ไม่ใช่ DI) ในทางเทคนิคแม้ว่าคุณจะมี 100 การพึ่งพาคุณก็ยังสามารถใช้การฉีดพึ่งพาได้ ในทำนองเดียวกันก็เป็นไปได้ที่จะฉีดขึ้นรูปที่เป็นรูปธรรม - มันยังคงฉีดขึ้นอยู่กับ
Jay Sullivan

10

ฉันคิดว่าเนื่องจากทุกคนเขียน DI ให้ฉันถามคำถามสองสามข้อ ..

  1. เมื่อคุณมีการกำหนดค่า DI ซึ่งการใช้งานจริงทั้งหมด (ไม่ใช่อินเทอร์เฟซ) ที่กำลังจะถูกฉีดเข้าไปในคลาส
  2. ถ้าฉันต้องการเปลี่ยนวัตถุตอนรันไทม์ ตัวอย่างเช่นการกำหนดค่าของฉันแจ้งว่าเมื่อฉันสร้างอินสแตนซ์ MyController ให้ฉีด FileLogger เป็น ILogger แต่ฉันอาจต้องการฉีด DatabaseLogger
  3. ทุกครั้งที่ฉันต้องการเปลี่ยนสิ่งที่วัตถุ AClass ของฉันต้องการตอนนี้ฉันต้องดูเป็นสองสถานที่ - ตัวเองชั้นเรียนและไฟล์การกำหนดค่า สิ่งนั้นทำให้ชีวิตง่ายขึ้นได้อย่างไร
  4. หาก Aproperty ของ AClass ไม่ถูกส่งออกไปเยาะเย้ยมันยากกว่านี้ไหม?
  5. กลับไปที่คำถามแรก หากใช้วัตถุใหม่ () ไม่ดีเราจะนำการติดตั้งไปใช้งานอย่างไรและไม่ใช่ส่วนต่อประสาน ฉันคิดว่าคุณหลายคนกำลังพูดว่าเรากำลังฉีดอินเทอร์เฟซ แต่การกำหนดค่าทำให้คุณระบุการใช้อินเทอร์เฟซนั้น .. ที่รันไทม์ .. มันฮาร์ดโค้ดในระหว่างการคอมไพล์

นี้จะขึ้นอยู่กับคำตอบ @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 และหมายเหตุประกอบเป็นซอร์สโค้ดด้วย) ปัญหาคือการลดจานหม้อไอน้ำนี้เป็นเรื่องจริงในกรณีที่ง่ายมาก (หนึ่งอินสแตนซ์ต่อคลาสและคล้ายกัน) บางครั้งในโลกแห่งความเป็นจริงการเลือกวัตถุบริการที่เหมาะสมนั้นไม่ใช่เรื่องง่ายเหมือนกับการแมปคลาสกับวัตถุเดี่ยว


8

คำตอบที่ได้รับความนิยมนั้นไม่ช่วยเหลือเพราะพวกเขากำหนดการฉีดยาเสพติดในแบบที่ไม่มีประโยชน์ มาตกลงกันว่าด้วย "การพึ่งพา" เราหมายถึงวัตถุอื่นที่มีอยู่แล้วที่วัตถุ 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 ให้พวกเขา)

โดยปกติระดับกลางจะให้โรงงานสำหรับวัตถุที่สร้างขึ้นซึ่งจะต้องจัดเตรียมบทบาทที่แต่ละประเภทของวัตถุที่ร้องขอต้องเป็นไปตาม นั่นเป็นเพราะการมีระดับกลางที่ซ่อนรายละเอียดของการก่อสร้างคุณได้รับโทษที่เป็นนามธรรมที่กำหนดโดยโรงงานดังนั้นคุณอาจใช้โรงงาน


8

การพึ่งพาการฉีดหมายถึงวิธี (จริงแล้วทางใดทางหนึ่ง ) สำหรับส่วนหนึ่งของรหัส (เช่นคลาส) เพื่อเข้าถึงการอ้างอิง (ส่วนอื่น ๆ ของรหัสเช่นคลาสอื่น ๆ มันขึ้นอยู่กับ) ในรูปแบบโมดูลาร์โดยไม่ต้อง hardcoded ดังนั้น พวกเขาสามารถเปลี่ยนหรือ overriden ได้อย่างอิสระหรือแม้กระทั่งโหลดในเวลาอื่นตามความจำเป็น)

(และ ps ใช่มันกลายเป็นชื่อที่เกินจริงไป 25 $ สำหรับแนวคิดที่ค่อนข้างง่าย).25เซนต์ของฉัน


8

ฉันรู้ว่ามีคำตอบมากมายอยู่แล้ว แต่ฉันพบว่าสิ่งนี้มีประโยชน์มาก: 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อินสแตนซ์


1
DI จะไม่ส่งต่อคุณด้วยอินเทอร์เฟซ DataSourceImp ที่สร้างขึ้นแล้วหรือไม่
PmanAce

6

การพึ่งพาการฉีดเป็นวิธีหนึ่งที่เป็นไปได้สำหรับสิ่งที่โดยทั่วไปอาจเรียกว่าข้อกำหนด "การทำให้งงงวยการพึ่งพา" Obfuscation พึ่งพาเป็นวิธีการเอาธรรมชาติ 'ชัดเจน' ออกจากกระบวนการของการให้การอ้างอิงไปยังคลาสที่ต้องใช้และดังนั้นจึง obfuscating ในบางวิธีบทบัญญัติของการอ้างอิงดังกล่าวกับคลาสดังกล่าว นี่ไม่จำเป็นว่าจะเป็นสิ่งเลวร้าย ในความเป็นจริงโดยทำให้งงงวยลักษณะที่มีการพึ่งพาให้กับชั้นเรียนแล้วบางสิ่งนอกชั้นเรียนมีหน้าที่รับผิดชอบในการสร้างการพึ่งพาซึ่งหมายความว่าในสถานการณ์ต่าง ๆ การใช้งานที่แตกต่างกันของการพึ่งพาสามารถให้กับชั้นเรียนโดยไม่ต้องทำการเปลี่ยนแปลงใด ๆ เข้าชั้นเรียน สิ่งนี้เหมาะอย่างยิ่งสำหรับการสลับระหว่างโหมดการผลิตและการทดสอบ (เช่นใช้การพึ่งพาบริการ 'จำลอง')

น่าเสียดายที่ส่วนที่ไม่ดีคือบางคนคิดว่าคุณจำเป็นต้องมีกรอบการทำงานเฉพาะด้านเพื่อสร้างความสับสนให้แก่ผู้พึ่งพาและคุณก็เป็นโปรแกรมเมอร์ที่ 'ด้อยกว่า' ถ้าคุณเลือกที่จะไม่ใช้เฟรมเวิร์กเฉพาะเพื่อทำมัน อีกเรื่องที่น่ารำคาญอย่างมากที่เชื่อกันโดยหลาย ๆ คนก็คือการฉีดการพึ่งพาเป็นวิธีเดียวที่จะทำให้เกิดความสับสนในการพึ่งพา นี่เป็นตัวอย่างที่ชัดเจนและมีประวัติในอดีตและเห็นได้ชัดว่าผิด 100% แต่คุณจะมีปัญหาในการโน้มน้าวใจคนบางคนว่ามีทางเลือกในการฉีดยาพึ่งพาสำหรับข้อกำหนดการทำให้งงงวยของผู้พึ่งพา

โปรแกรมเมอร์เข้าใจความต้องการในการทำให้ยุ่งเหยิงของผู้พึ่งพาอาศัยกันมานานหลายปีและมีทางเลือกมากมายที่พัฒนาขึ้นทั้งก่อนและหลังการฉีด มีรูปแบบของโรงงาน แต่ยังมีตัวเลือกมากมายโดยใช้ ThreadLocal ที่ไม่จำเป็นต้องฉีดอินสแตนซ์เฉพาะ - การพึ่งพานั้นจะถูกฉีดเข้าไปในเธรดอย่างมีประสิทธิภาพซึ่งมีประโยชน์ในการทำให้วัตถุพร้อมใช้งานใด ๆคลาสที่ต้องการโดยไม่ต้องเพิ่มคำอธิบายประกอบในคลาสที่ต้องการและตั้งค่า 'กาว' XML ที่ซับซ้อนเพื่อให้มันเกิดขึ้น เมื่อการพึ่งพาของคุณเป็นสิ่งที่จำเป็นสำหรับการคงอยู่ (JPA / JDO หรืออะไรก็ตาม) มันจะช่วยให้คุณบรรลุ 'การมีอยู่อย่างต่อเนื่อง' ได้ง่ายขึ้นมากและด้วยโมเดลโดเมนและคลาสโมเดลธุรกิจที่ประกอบขึ้นจาก POJOs ทั้งหมด


5

จากหนังสือเล่มนี้ ' ผู้พัฒนา Java ที่มีเหตุผลดี: เทคนิคสำคัญของ Java 7 และการเขียนโปรแกรมหลายภาษา

DI เป็นรูปแบบเฉพาะของ IoC โดยกระบวนการค้นหาการพึ่งพาของคุณอยู่นอกการควบคุมโดยตรงของรหัสที่คุณกำลังใช้งานในปัจจุบัน


5

ก่อนที่จะไปอธิบายรายละเอียดทางเทคนิคก่อนเห็นภาพด้วยตัวอย่างในชีวิตจริงเพราะคุณจะได้พบกับข้อมูลทางเทคนิคมากมายที่จะเรียนรู้การฉีดพึ่งพา แต่เวลาสูงสุดที่คนอย่างฉันไม่สามารถได้รับแนวคิดหลักของมัน

ในภาพแรกสมมติว่าคุณมีโรงงานผลิตรถยนต์ที่รวมตัวกันเป็นจำนวนมาก รถที่ถูกสร้างขึ้นจริงในหน่วยการชุมนุมแต่ต้องเครื่องยนต์ , ที่นั่งเช่นเดียวกับล้อ ดังนั้นแอสเซมบลียูนิตจะขึ้นอยู่กับหน่วยทั้งหมดเหล่านี้และพวกเขาจะขึ้นอยู่กับโรงงาน

คุณสามารถรู้สึกว่าตอนนี้มันซับซ้อนเกินไปที่จะรักษางานทั้งหมดในโรงงานนี้เพราะพร้อมกับงานหลัก (การประกอบรถยนต์ในชุดหน่วยประกอบ) คุณต้องให้ความสำคัญกับหน่วยอื่น ๆหน่วยงานอื่นตอนนี้มันมีค่าใช้จ่ายสูงมากในการบำรุงรักษาและอาคารโรงงานมีขนาดใหญ่มากดังนั้นจึงต้องใช้เงินเพิ่มเติมสำหรับการเช่า

ตอนนี้ดูรูปที่สอง หากคุณพบว่า บริษัท ผู้ให้บริการบางอย่างที่จะช่วยให้คุณล้อ , ที่นั่งและเครื่องยนต์ราคาถูกกว่าต้นทุนการผลิตด้วยตัวคุณเองแล้วตอนนี้คุณไม่จำเป็นต้องทำให้พวกเขาในโรงงานของคุณ คุณสามารถเช่าอาคารขนาดเล็กตอนนี้เพียงแค่สำหรับหน่วยการชุมนุมของคุณซึ่งจะช่วยลดงานบำรุงรักษาของคุณและลดค่าเช่าเพิ่มของคุณ ตอนนี้คุณสามารถมุ่งเน้นเฉพาะงานหลักของคุณ (การประกอบรถยนต์)

ตอนนี้เราสามารถพูดได้ว่าทุกการอ้างอิงสำหรับการประกอบรถได้รับการฉีดในโรงงานจากผู้ให้บริการ มันเป็นตัวอย่างของการฉีดพึ่งพา (DI) ในชีวิตจริงชีวิตจริง

ตอนนี้ในคำทางเทคนิคการฉีดพึ่งพาเป็นเทคนิคที่วัตถุหนึ่ง (หรือวิธีการแบบคงที่) หน้า ๆ การพึ่งพาของวัตถุอื่น ดังนั้นการถ่ายโอนงานในการสร้างวัตถุให้กับบุคคลอื่นและใช้การพึ่งพาโดยตรงโดยตรงเรียกว่าการฉีดพึ่งพา

นี่จะช่วยให้คุณเรียนรู้ DI ด้วยคำศัพท์ที่ซับซ้อน นี้จะแสดงเมื่อมีการใช้ DI และเมื่อควรไม่

ทั้งหมดในโรงงานรถยนต์แห่งเดียว.

โรงงานรถยนต์ที่เรียบง่าย


1
คำตอบที่ชัดเจนที่สุดจาก 40 ish ตัวอย่างและรูปภาพในชีวิตจริง +1 ควรเป็นคำตอบที่ยอมรับได้
Marche Remi

4

จาก Book Apress.Spring.Persistence.with. Hibernate.Oct.2010

วัตถุประสงค์ของการฉีดพึ่งพาคือการแยกชิ้นส่วนของการแก้ไขส่วนประกอบซอฟต์แวร์ภายนอกจากตรรกะทางธุรกิจแอปพลิเคชันของคุณโดยไม่ต้องพึ่งพาการฉีดรายละเอียดของวิธีการเข้าถึงส่วนประกอบบริการที่ต้องการบริการสามารถยุ่งเหยิงด้วยรหัสของส่วนประกอบ สิ่งนี้ไม่เพียงเพิ่มโอกาสในการเกิดข้อผิดพลาดเพิ่มการขยายโค้ดและขยายความซับซ้อนในการบำรุงรักษา มันประกอบเข้าด้วยกันอย่างใกล้ชิดทำให้ยากต่อการปรับเปลี่ยนการพึ่งพาเมื่อทำการเปลี่ยนโครงสร้างหรือทดสอบ


4

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ภาชนะซึ่งใช้งานได้กับเรา เราเพียงแค่ในรหัสของเราสร้างบริบทและเรียกการเริ่มต้นถั่ว ในช่วงเวลานั้นการฉีดจะทำโดยอัตโนมัติ


4

ฉีดพึ่งพาสำหรับ 5 ปี

เมื่อคุณไปและนำสิ่งของออกจากตู้เย็นด้วยตัวคุณเองคุณอาจทำให้เกิดปัญหา คุณอาจเปิดประตูทิ้งไว้คุณอาจได้สิ่งที่แม่หรือพ่อไม่ต้องการให้คุณ คุณอาจจะกำลังมองหาสิ่งที่เราไม่มีหรือหมดอายุ

สิ่งที่คุณควรทำคือการระบุความต้องการ "ฉันต้องการเครื่องดื่มพร้อมอาหารกลางวัน" และจากนั้นเราจะทำให้แน่ใจว่าคุณมีบางอย่างเมื่อคุณนั่งลงกิน


1
นี่เป็นคำตอบของผู้ปกครองอย่างชัดเจน ;)
Marche Remi

4

จาก Christoffer Noring หนังสือของ Pablo Deeleman“ Learning Angular - Second Edition”:

"ในฐานะที่เป็นของเราการใช้งานที่เติบโตและวิวัฒนาการหนึ่งของเราในแต่ละหน่วยงานรหัสภายในจะต้องมีกรณีของวัตถุอื่น ๆซึ่งเป็นที่รู้จักกันดีในการอ้างอิงในโลกของวิศวกรรมซอฟต์แวร์. การกระทำของการส่งผ่านเช่นการอ้างอิงไปยังลูกค้าขึ้นเป็นที่รู้จักกันฉีด , และยังสร้างความมีส่วนร่วมของรหัสอีกกิจการหนึ่งชื่อที่หัวฉีด . the หัวฉีดจะต้องรับผิดชอบสำหรับอินสแตนซ์และความร่วมมือที่จำเป็นการอ้างอิงดังนั้นพวกเขาจึงพร้อมใช้งานในช่วงเวลาที่พวกเขาประสบความสำเร็จในการฉีดลูกค้า สิ่งนี้สำคัญมากเนื่องจากไคลเอนต์ไม่ทราบว่าจะสร้างอินสแตนซ์ของการพึ่งพาตนเองได้อย่างไรและตระหนักถึงอินเทอร์เฟซที่ใช้เพื่อใช้งานเท่านั้น "

จาก: Anton Moiseev หนังสือ“ การพัฒนาเชิงมุมพร้อม Typescript, Second Edition”:

“ในระยะสั้นDIจะช่วยให้คุณเขียนโค้ดในคู่หลวมวิธีและทำให้คุณรหัสเพิ่มเติมทดสอบและนำมาใช้ใหม่ .”


3

คำง่าย ๆ ในการฉีดพึ่งพา (DI) เป็นวิธีที่จะเอาการอ้างอิงหรือคับระหว่างวัตถุต่าง ๆ การพึ่งพาการฉีดให้พฤติกรรมที่เหนียวแน่นกับแต่ละวัตถุ

DI คือการดำเนินการตามหลักการ IOC ของฤดูใบไม้ผลิซึ่งบอกว่า "อย่าโทรหาเราเราจะโทรหาคุณ" การใช้โปรแกรมเมอร์ฉีดพึ่งพาไม่จำเป็นต้องสร้างวัตถุโดยใช้คำหลักใหม่

เมื่อโหลดวัตถุใน Spring container แล้วเราจะนำมันกลับมาใช้ใหม่เมื่อใดก็ตามที่เราต้องการโดยดึงวัตถุเหล่านั้นจาก Spring container โดยใช้วิธี getBean (String beanName)


3

การฉีดพึ่งพาเป็นหัวใจของแนวคิดที่เกี่ยวข้องกับ Spring Framework ในขณะที่การสร้างกรอบของฤดูใบไม้ผลิโครงการใด ๆ ที่อาจมีบทบาทสำคัญและที่นี่ฉีดพึ่งพามาในเหยือก

ที่จริงแล้วสมมติว่าใน java คุณสร้างคลาสที่แตกต่างกันสองคลาสเป็นคลาส A และคลาส B และฟังก์ชันใดก็ตามที่มีให้ในคลาส B ที่คุณต้องการใช้ในคลาส A ดังนั้นในขณะนั้นสามารถใช้การฉีดแบบพึ่งพาได้ ที่ซึ่งคุณสามารถสร้างออบเจ็กต์ของคลาสหนึ่งในอีกชั้นหนึ่งในลักษณะเดียวกับที่คุณสามารถฉีดทั้งคลาสในคลาสอื่นเพื่อให้สามารถเข้าถึงได้ ด้วยวิธีนี้สามารถเอาชนะการพึ่งพา

การฉีดขึ้นอยู่กับการแบ่งเป็นสองชั้นอย่างง่ายและในเวลาเดียวกันทำให้พวกเขาแยกตัวออกจากกัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.