ฉันจะไปถึงจุดที่คุณตัวเลข แต่แรกมีสิ่งที่คุณควรจะระมัดระวังมาก: ไม่ conflate วิธีการที่ผู้บริโภคใช้ห้องสมุดที่มีวิธีการที่ห้องสมุดมีการดำเนินการ ตัวอย่างที่ดีของสิ่งนี้คือ Entity Framework (ซึ่งคุณอ้างว่าเป็นไลบรารีที่ดี) และ MVC ของ ASP.NET ทั้งสองอย่างนี้ทำสิ่งที่น่ากลัวมากภายใต้ประทุนด้วยตัวอย่างเช่นการสะท้อนซึ่งจะไม่ถือว่าเป็นการออกแบบที่ดีอย่างแน่นอนถ้าคุณแพร่กระจายผ่านรหัสประจำวัน
ห้องสมุดเหล่านี้ไม่โปร่งใสในการทำงานหรือสิ่งที่พวกเขากำลังทำอยู่เบื้องหลัง แต่ที่ไม่ได้รับบาดเจ็บเพราะพวกเขาสนับสนุนหลักการเขียนโปรแกรมที่ดีของพวกเขาในการบริโภค ดังนั้นเมื่อใดก็ตามที่คุณพูดถึงห้องสมุดเช่นนี้โปรดจำไว้ว่าในฐานะผู้บริโภคห้องสมุดคุณไม่ต้องกังวลเกี่ยวกับการนำไปใช้หรือบำรุงรักษา คุณควรกังวลว่ามันจะช่วยหรือขัดขวางรหัสที่คุณเขียนซึ่งใช้ไลบรารีได้อย่างไร อย่าทำให้แนวคิดเหล่านี้สับสน!
ดังนั้นเพื่อผ่านจุด:
ทันทีที่เราไปถึงสิ่งที่ฉันสามารถสันนิษฐานได้ว่าเป็นตัวอย่างของข้างต้น คุณบอกว่าคอนเทนเนอร์ IOC ตั้งค่าการพึ่งพาส่วนใหญ่เป็นแบบคงที่ บางทีรายละเอียดการนำไปปฏิบัติบางอย่างเกี่ยวกับวิธีการทำงานของพวกเขารวมถึงที่เก็บข้อมูลแบบสแตติก (แม้ว่าพวกเขามีแนวโน้มที่จะมีอินสแตนซ์ของวัตถุเช่น Ninject IKernelเป็นที่เก็บข้อมูลหลักของพวกเขา แต่นี่ไม่ใช่ความกังวลของคุณ!
ที่จริงแล้วคอนเทนเนอร์ IoC นั้นมีความชัดเจนโดยมีขอบเขตเหมือนกับการฉีดยาเสพติดของคนจน (ฉันจะเปรียบเทียบคอนเทนเนอร์ IoC กับ DI ของคนจนเพราะการเปรียบเทียบกับ DI ไม่น่าจะไม่ยุติธรรมและทำให้สับสนและเพื่อความชัดเจนฉันไม่ได้ใช้ "คนจนคนดี" เป็นคำดูถูก)
ใน DI ของชายยากจนคุณต้องยกตัวอย่างการพึ่งพาตนเองด้วยตนเองจากนั้นอัดมันเข้าไปในชั้นเรียนที่ต้องการ ณ จุดที่คุณสร้างการพึ่งพาคุณจะต้องเลือกว่าจะทำอย่างไรกับมัน - เก็บไว้ในตัวแปรที่มีการกำหนดขอบเขตแบบโลคอลตัวแปรคลาสตัวแปรแบบสแตติกไม่ได้เก็บไว้เลย คุณสามารถส่งอินสแตนซ์เดียวกันไปยังคลาสต่างๆได้หรือคุณสามารถสร้างคลาสใหม่สำหรับแต่ละคลาสได้ อะไรก็ตาม ประเด็นคือเพื่อดูว่าเกิดอะไรขึ้นคุณมองไปที่จุดที่ใกล้กับรูทของแอปพลิเคชันซึ่งสร้างการพึ่งพา
ตอนนี้คอนเทนเนอร์ IoC ล่ะ? คุณทำเช่นเดียวกัน! อีกครั้งโดยจะ Ninject ศัพท์คุณมองไปที่ที่มีผลผูกพันการตั้งค่าและหาไม่ว่าจะพูดอะไรบางอย่างเช่นInTransientScope, InSingletonScopeหรืออะไรก็ตาม หากมีสิ่งใดที่อาจชัดเจนกว่านี้เพราะคุณมีรหัสอยู่ตรงนั้นประกาศขอบเขตของมันแทนที่จะต้องมองหาวิธีการติดตามสิ่งที่เกิดขึ้นกับวัตถุบางอย่าง (วัตถุอาจถูกกำหนดขอบเขตไปยังบล็อก แต่มันใช้หลายครั้งในนั้น บล็อกหรือเพียงแค่ครั้งเดียวเช่น) ดังนั้นบางทีคุณอาจถูกรังเกียจโดยความคิดที่จะต้องใช้คุณสมบัติบนคอนเทนเนอร์ IoC แทนที่จะเป็นภาษาดิบเพื่อกำหนดขอบเขต แต่ตราบใดที่คุณเชื่อถือห้องสมุด IoC ของคุณ - ซึ่งคุณควร! - ไม่มีข้อเสียที่แท้จริง .
ฉันยังไม่รู้จริงๆว่าคุณกำลังพูดถึงอะไรที่นี่ คอนเทนเนอร์ IoC ดูคุณสมบัติส่วนตัวเป็นส่วนหนึ่งของการนำไปใช้ภายในหรือไม่ ฉันไม่รู้ว่าทำไมพวกเขาถึงจะทำเช่นนั้น แต่ถ้าเป็นเช่นนั้นไม่ใช่ความกังวลของคุณที่จะนำไลบรารี่มาใช้งาน
หรือบางทีพวกเขาเปิดเผยความสามารถเช่นการฉีดเข้าไปใน setters ส่วนตัว? สุจริตฉันไม่เคยพบสิ่งนี้และฉันสงสัยว่านี่เป็นคุณสมบัติทั่วไปหรือไม่ แต่ถึงแม้ว่าจะอยู่ที่นั่นมันเป็นกรณีง่าย ๆ ของเครื่องมือที่สามารถนำไปใช้ในทางที่ผิด โปรดจำไว้ว่าแม้จะไม่มีคอนเทนเนอร์ IoC แต่เป็นเพียงโค้ดสองสามบรรทัดในการเข้าถึงและแก้ไขคุณสมบัติส่วนตัว มันเป็นสิ่งที่คุณไม่ควรทำ แต่ก็ไม่ได้หมายความว่า. NET นั้นไม่ดีสำหรับการเปิดเผยความสามารถ หากมีคนเห็นได้ชัดและใช้เครื่องมือในทางที่ผิดนั่นเป็นความผิดของบุคคลไม่ใช่เครื่องมือ
จุดสูงสุดที่นี่คล้ายกับ 2 เวลาส่วนใหญ่คอนเทนเนอร์ IoC ใช้ตัวสร้าง! Setter injection มีไว้สำหรับสถานการณ์ที่เฉพาะเจาะจงซึ่งไม่สามารถใช้การสร้างคอนสตรัคเตอร์ได้ ใครก็ตามที่ใช้ setter injection ตลอดเวลาเพื่อปกปิดจำนวนการพึ่งพาที่ถูกส่งผ่านเข้ามา นั่นไม่ใช่ความผิดของเครื่องมือ แต่เป็นของพวกเขา
ทีนี้ถ้านี่เป็นความผิดพลาดง่าย ๆ ที่จะทำอย่างไร้เดียงสาและสิ่งที่ IoC ให้การสนับสนุนนั้นโอเคคุณอาจมีประเด็น มันเหมือนกับการทำให้สมาชิกทุกคนในชั้นเรียนของฉันเปิดเผยต่อหน้าคนอื่นเมื่อพวกเขาปรับเปลี่ยนสิ่งที่พวกเขาไม่ควรใช่ไหม แต่ใครก็ตามที่ใช้ setter injection เพื่อปกปิดการฝ่าฝืน SRP อาจเพิกเฉยหรือเพิกเฉยต่อหลักการออกแบบพื้นฐานโดยสิ้นเชิง มันไม่มีเหตุผลที่จะกล่าวโทษเรื่องนี้ในคอนเทนเนอร์ IoC
นี่เป็นเรื่องจริงโดยเฉพาะอย่างยิ่งเพราะเป็นสิ่งที่คุณสามารถทำได้อย่างง่ายดายด้วย DI ของชายยากจน:
var myObject
= new MyTerriblyLargeObject { DependencyA = new Thing(), DependencyB = new Widget(), Dependency C = new Repository(), ... };
ดังนั้นความกังวลนี้ดูเหมือนจะสมบูรณ์โดยสิ้นเชิงว่าจะใช้คอนเทนเนอร์ IoC หรือไม่
การเปลี่ยนวิธีการที่คลาสของคุณทำงานร่วมกันไม่ใช่การละเมิด OCP ถ้าเป็นเช่นนั้นการผกผันของการพึ่งพาทั้งหมดจะสนับสนุนให้มีการละเมิด OCP หากเป็นกรณีนี้พวกเขาจะไม่อยู่ในคำย่อของ SOLID เดียวกัน!
นอกจากนี้ไม่มีคะแนน a) หรือ b) ใกล้เคียงกับการทำอะไรกับ OCP ฉันไม่รู้ด้วยซ้ำว่าจะตอบคำถามเหล่านี้อย่างไรเกี่ยวกับ OCP
สิ่งเดียวที่ฉันเดาได้ก็คือคุณคิดว่า OCP เป็นสิ่งที่เกี่ยวข้องกับพฤติกรรมที่ไม่ถูกเปลี่ยนแปลงในขณะใช้งานจริงหรือเกี่ยวกับที่ที่รหัสควบคุมวงจรชีวิตของการพึ่งพา มันไม่ใช่. OCP ไม่เกี่ยวกับการแก้ไขโค้ดที่มีอยู่เมื่อมีการเพิ่มหรือเปลี่ยนแปลงข้อกำหนด มันคือทั้งหมดที่เกี่ยวกับการเขียนโค้ดไม่ใช่ว่าโค้ดที่คุณเขียนนั้นติดกาวเข้าด้วยกันอย่างไร (แน่นอนว่าการแต่งงานกันแบบหลวม ๆ นั้นเป็นส่วนสำคัญในการบรรลุ OCP)
และสิ่งสุดท้ายที่คุณพูดว่า:
แต่ฉันไม่สามารถไว้วางใจแบบเดียวกันนี้ได้ในเครื่องมือของบุคคลที่สามซึ่งกำลังเปลี่ยนรหัสที่คอมไพล์ของฉันเพื่อแก้ไขปัญหากับสิ่งที่ฉันไม่สามารถทำได้ด้วยตนเอง
ใช่คุณสามารถ ไม่มีเหตุผลใดที่คุณจะคิดว่าเครื่องมือเหล่านี้ที่พึ่งพาโครงการจำนวนมหาศาลนั้นมีพฤติกรรมที่ไม่คาดคิดหรือมีพฤติกรรมที่ไม่คาดคิดมากกว่าห้องสมุดบุคคลที่สามอื่น ๆ
ภาคผนวก
ฉันเพิ่งสังเกตว่าย่อหน้าแนะนำของคุณสามารถใช้ที่อยู่ได้บ้าง คุณพูดอย่างเย้ยหยันว่าภาชนะบรรจุ IoC "ไม่ได้ใช้เทคนิคลับบางอย่างที่เราไม่เคยได้ยิน" เพื่อหลีกเลี่ยงความยุ่งเหยิงรหัสซ้ำได้ง่ายเพื่อสร้างกราฟการพึ่งพา และคุณพูดถูกสิ่งที่พวกเขากำลังทำคือการพูดถึงสิ่งเหล่านี้ด้วยเทคนิคพื้นฐานแบบเดียวกับที่โปรแกรมเมอร์เราทำอยู่เสมอ
ให้ฉันพูดคุยกับคุณในสถานการณ์สมมติ คุณในฐานะโปรแกรมเมอร์รวบรวมแอปพลิเคชั่นขนาดใหญ่และที่จุดเริ่มต้นที่คุณกำลังสร้างกราฟวัตถุของคุณคุณสังเกตเห็นว่าคุณมีรหัสที่ค่อนข้างยุ่ง มีคลาสไม่กี่คลาสที่ใช้ซ้ำแล้วซ้ำอีกและทุกครั้งที่คุณสร้างหนึ่งในนั้นคุณต้องสร้างสายโซ่ทั้งหมดของการพึ่งพาภายใต้พวกเขาอีกครั้ง นอกจากนี้คุณยังพบว่าคุณไม่มีวิธีที่ชัดเจนในการประกาศหรือควบคุมวงจรชีวิตของการอ้างอิงยกเว้นด้วยรหัสที่กำหนดเองสำหรับแต่ละรายการ รหัสของคุณไม่มีโครงสร้างและเต็มไปด้วยการซ้ำซ้อน นี่คือความยุ่งเหยิงที่คุณพูดถึงในย่อหน้าแนะนำของคุณ
ดังนั้นก่อนอื่นคุณจะเริ่ม refactor บิต - โดยที่บางโค้ดที่ทำซ้ำนั้นมีโครงสร้างเพียงพอที่คุณจะดึงมันออกมาเป็นเมธอดตัวช่วยและอื่น ๆ แต่จากนั้นคุณเริ่มคิดว่า - นี่เป็นปัญหาที่ฉันอาจจัดการในแง่ทั่วไปหรือไม่ซึ่งไม่เฉพาะเจาะจงกับโครงการนี้ แต่สามารถช่วยคุณในโครงการในอนาคตทั้งหมดของคุณหรือไม่
ดังนั้นคุณนั่งลงและคิดเกี่ยวกับมันและตัดสินใจว่าควรมีชั้นเรียนที่สามารถแก้ไขการพึ่งพา และคุณร่างวิธีการสาธารณะที่ต้องการ:
void Bind(Type interfaceType, Type concreteType, bool singleton);
T Resolve<T>();
Bindพูดว่า "ที่คุณเห็นอาร์กิวเมนต์ตัวสร้างประเภทinterfaceTypeผ่านในตัวอย่างconcreteType" singletonพารามิเตอร์เพิ่มเติมบอกว่าจะใช้อินสแตนซ์เดียวกันของconcreteTypeแต่ละครั้งหรือสร้างใหม่เสมอ
Resolveจะพยายามสร้างTด้วยตัวสร้างใด ๆ ที่สามารถค้นหาว่าอาร์กิวเมนต์ใดเป็นชนิดทั้งหมดที่ถูกผูกไว้ก่อนหน้านี้ นอกจากนี้ยังสามารถเรียกตัวเองซ้ำเพื่อแก้ปัญหาการพึ่งพาตลอดทางลง หากไม่สามารถแก้ไขอินสแตนซ์ได้เนื่องจากไม่ใช่ว่าทุกอย่างถูกผูกไว้มันจะส่งข้อยกเว้น
คุณสามารถลองใช้สิ่งนี้ด้วยตัวคุณเองแล้วคุณจะพบว่าคุณต้องมีการไตร่ตรองและมีการแคชสำหรับการผูกที่singletonจริง แต่ก็ไม่มีอะไรรุนแรงหรือน่ากลัวอย่างแน่นอน และเมื่อคุณทำเสร็จแล้วvoilaคุณจะมีแกนหลักของคอนเทนเนอร์ IoC ของคุณเอง! มันน่ากลัวจริงๆเหรอ? ข้อแตกต่างที่แท้จริงระหว่างสิ่งนี้กับ Ninject หรือ StructureMap หรือ Castle Windsor หรือสิ่งที่คุณต้องการก็คือสิ่งเหล่านั้นมีฟังก์ชั่นการใช้งานที่ครอบคลุมมากกว่า (มาก!) กรณีการใช้งานที่รุ่นพื้นฐานนี้จะไม่เพียงพอ แต่ในหัวใจของมันสิ่งที่คุณมีคือสิ่งสำคัญของคอนเทนเนอร์ IoC
IOCและExplicitness of codeเป็นสิ่งที่ฉันมีปัญหา แมนนวล DI สามารถกลายเป็นงานที่น่าเบื่อได้ง่าย แต่อย่างน้อยมันก็ทำให้การไหลของโปรแกรมชัดเจนขึ้นเองในที่เดียว - "คุณได้สิ่งที่คุณเห็นคุณเห็นว่าคุณได้อะไร" ในขณะที่การผูกและการประกาศคอนเทนเนอร์ของ IOC สามารถกลายเป็นโปรแกรมคู่ขนานที่ซ่อนอยู่ได้อย่างง่ายดาย