เมื่อเร็ว ๆ นี้ฉันได้สัมผัสกับหลักการหรือรูปแบบการเขียนโปรแกรมเช่น IOC (การผกผันของการควบคุม), DI (การฉีดขึ้นอยู่กับการพึ่งพา) และนี่คือแก่นแท้ของฤดูใบไม้ผลิ Java ที่มีชื่อเสียง struts ฯลฯ เพื่อตอบสนองต่อสิ่งนี้ฉันตรวจสอบรายการต่าง ๆ ในวิกิพีเดีย หลังจากอ่านพวกเขาฉันมีความเข้าใจ ตอนนี้ฉันจะรวมคำอธิบายในหนังสือและการประมวลผลของฉันเองและจัดเรียงพวกเขาออกไปดังนี้:
EG1
คำอธิบายปัญหา:
พัฒนาระบบที่สามารถสร้างรายงานในรูปแบบ Excel หรือ PDF ตามข้อกำหนดที่แตกต่างกันเช่นรายงานรายวันรายงานรายเดือน ฯลฯ
สารละลาย:
ตามหลักการของ "การเขียนโปรแกรมเชิงอินเตอร์เฟส" อินเตอร์เฟสและการใช้งานควรถูกแยกออกนั่นคือฟังก์ชั่นของการสร้างรายงานจะถูกแยกออกเป็นรายงานอินเตอร์เฟสทั่วไปและสองการใช้งานที่สร้างรายงาน Excel และ PDF ในรูปแบบ Excel และ PDF จากนั้นไคลเอนต์จะได้รับฟังก์ชั่นการพิมพ์รายงานที่สอดคล้องกันผ่านผู้ให้บริการรายงาน
วิธีการดำเนินการ:
ตามข้างต้นได้รับแผนภาพคลาสต่อไปนี้:
การใช้รหัส:
Interface ReportGenerator {โมฆะสาธารณะสร้าง (ตารางตาราง); } คลาส ExcelGenerator ใช้ ReportGenerator {โมฆะสาธารณะสร้าง (ตารางตาราง) {System.out.println ("สร้างรายงาน Excel ... "); }} คลาส pdfgenerator ใช้ ReportGenerator {โมฆะสาธารณะสร้าง (ตารางตาราง) {System.out.println ("สร้างรายงาน PDF ... "); }} class Reportservice {// รับผิดชอบในการสร้างตัวสร้างรายงานสำหรับความต้องการเฉพาะ reportGenerator ตัวสร้าง = pdfgenerator ใหม่ (); // reportgenerator reportgenerator ส่วนตัว = new ExcelGenerator (); โมฆะสาธารณะ getDailyReport (วันที่) {table.setDate (วันที่); // ... generator.generate (ตาราง); } โมฆะสาธารณะ getMonthlyReport (เดือนเดือน) {table.setMonth (เดือน); // ... generator.generate (ตาราง); }} ไคลเอนต์คลาสสาธารณะ {โมฆะคงที่สาธารณะหลัก (String [] args) {reportservice reportservice = new reportservice (); Reportservice.getDailyReport (วันที่ใหม่ ()); //reportservice.getMonthlyReport( วันที่ใหม่ ()); -
EG2
คำอธิบายปัญหา:
ดังที่แสดงในความคิดเห็นในรหัสด้านบนตัวสร้างรายงานเฉพาะถูกสร้างขึ้นโดย hard-coded ภายในในคลาส Reportservice เพื่อให้รายงานการใช้งานได้โดยตรงกับ PDFGenerator หรือ ExcelGenerator และการมีเพศสัมพันธ์ที่ชัดเจนนี้จะต้องถูกกำจัด
วิธีแก้ปัญหา: แนะนำคอนเทนเนอร์เพื่อแนะนำตัวจัดการระดับกลางนั่นคือคอนเทนเนอร์ (คอนเทนเนอร์) ซึ่งจัดการวัตถุที่เกี่ยวข้องในระบบการรายงานอย่างสม่ำเสมอ (นี่คือส่วนประกอบเราเรียกมันว่าถั่ว) รวมถึงรายงานและ xxgenerators ต่างๆ ที่นี่คุณใช้อินสแตนซ์ HashMap ในรูปแบบของคู่คีย์-ค่าเพื่อบันทึกถั่วเหล่านี้
วิธีการดำเนินการ:
ได้รับแผนภาพระดับดังนี้:
การใช้รหัส:
คลาสคอนเทนเนอร์ {// บันทึกส่วนประกอบที่จำเป็นต่าง ๆ ในคู่คีย์-ค่าถั่วแผนที่คงที่ส่วนตัว <สตริงวัตถุ> ถั่ว; Public Container () {beans = new hashmap <string, object> (); // สร้างและบันทึกรายงานตัวสร้างรายงานเฉพาะ reportGenerator ReportGenerator = ใหม่ pdfgenerator (); Beans.put ("ReportGenerator", ReportGenerator); // รับและจัดการการอ้างอิงของ Reportservice Reportservice Reportservice = Newsserveservice (); beans.put ("Reportservice", Reportservice); } วัตถุคงที่สาธารณะ getBean (รหัสสตริง) {return beans.get (id); }} class Reportservice {// กำจัดความสัมพันธ์ระหว่างการมีเพศสัมพันธ์ที่แน่นหนาและแทนที่ด้วยคอนเทนเนอร์ // reportgenerator private container // private reportgenerator = pdfgenerator ใหม่ (); Private ReportGenerator Generator Generator = (ReportGenerator) container.getBean ("ReportGenerator"); โมฆะสาธารณะ getDailyReport (วันที่) {table.setDate (วันที่); generator.generate (ตาราง); } โมฆะสาธารณะ getMonthlyReport (เดือนเดือน) {table.setMonth (เดือน); generator.generate (ตาราง); }} ไคลเอนต์คลาสสาธารณะ {โมฆะคงที่สาธารณะหลัก (String [] args) {container container = new container (); Reportservice Reportservice = (Reportservice) container.getBean ("Reportservice"); Reportservice.getDailyReport (วันที่ใหม่ ()); //reportservice.getMonthlyReport( วันที่ใหม่ ()); -
แผนภูมิเวลามีดังนี้:
ผล:
ดังที่แสดงไว้ข้างต้นรายงานการรายงานจะไม่เชื่อมโยงโดยตรงกับผู้รายงานเฉพาะอีกต่อไป มันใช้คอนเทนเนอร์เพื่อแยกอินเทอร์เฟซและการใช้งานเพื่อปรับปรุงความสามารถในการใช้ซ้ำของถั่วส่วนประกอบของระบบ ในเวลานี้คุณยังสามารถใช้ไฟล์การกำหนดค่าเพื่อรับคำจำกัดความของส่วนประกอบเฉพาะในคอนเทนเนอร์แบบเรียลไทม์
EG3
คำอธิบายปัญหา:
อย่างไรก็ตามหากคุณดูแผนภาพคลาสข้างต้นมันเป็นเรื่องง่ายที่จะพบว่ามีความสัมพันธ์แบบสองทางระหว่างการรายงานและคอนเทนเนอร์และพวกเขามีการพึ่งพาซึ่งกันและกัน และหากคุณต้องการใช้ Reportservice ซ้ำมันก็ขึ้นอยู่กับตรรกะการค้นหาที่เฉพาะเจาะจงของคอนเทนเนอร์เดียว หากคอนเทนเนอร์อื่นมีกลไกการค้นหาส่วนประกอบที่แตกต่างกัน (เช่น JNDI) การใช้รายงานใหม่ในเวลานี้หมายความว่าตรรกะการค้นหาภายในของคอนเทนเนอร์จะต้องได้รับการแก้ไข
วิธีแก้ปัญหา: แนะนำตัวระบุบริการ
การแนะนำตัวระบุตำแหน่งบริการเลเยอร์ทางอ้อมอีกครั้งใช้เพื่อให้อินเทอร์เฟซสำหรับตรรกะการค้นหาส่วนประกอบ โปรดดูคำอธิบายใน Wikipedia หรือคำอธิบายของมันโดย Java EE 1 และ 2 สิ่งนี้จะช่วยให้สามารถแยกการเปลี่ยนแปลงที่เป็นไปได้ที่เป็นไปได้
วิธีการดำเนินการ:
แผนภาพคลาสมีดังนี้:
การใช้รหัส:
// ในแอปพลิเคชันจริงคุณสามารถใช้อินเทอร์เฟซเพื่อจัดเตรียม ServiceLocator คลาสอินเตอร์เฟสแบบครบวงจร {คอนเทนเนอร์คอนเทนเนอร์แบบคงที่ส่วนตัว = ใหม่คอนเทนเนอร์ (); Public Static ReportGenerator getReportGenerator () {return (reportGenerator) container.getBean ("ReportGeneraator"); }} class Reportservice {Private ReportGenerator ReportGenerator = ServicElocator.getReportGenerator (); -EG4
คำอธิบายปัญหา:
อย่างไรก็ตามไม่ว่าจะเป็นการแนะนำคอนเทนเนอร์หรือการใช้ตัวระบุตำแหน่งบริการรายงานนั้น 'ใช้งาน' ในการค้นหาและสร้างส่วนประกอบเฉพาะซึ่งหมายความว่าในฐานะลูกค้ารายงานการรายงานจะต้องชัดเจนเกี่ยวกับสิ่งที่ต้องการ ในทันใดนั้นจะต้องมีการเพิ่มรายละเอียดเชิงตรรกะที่เฉพาะเจาะจงเนื่องจากสิ่งที่อยู่ที่ไหนและอย่างไร
ตัวอย่างเช่นในวิธีการใช้งานก่อนหน้าของ 'แนะนำคอนเทนเนอร์' มีรหัสต่อไปนี้:
Class Reportservice {// กำจัดความสัมพันธ์ที่มีเพศสัมพันธ์ที่แน่นหนาและแทนที่ด้วยคอนเทนเนอร์ // private reportGenerator generator = pdfgenerator ใหม่ (); // ค้นหา private reportGenerator generator = (reportGenerator) คอนเทนเนอร์. getBean ("ReportGenerator");
ในวิธีการใช้งานของ 'แนะนำตัวระบุตำแหน่ง' มีรหัสต่อไปนี้:
Class ServiceLocator {Privatestatic Container Container = New Container (); PublicStatic ReportGenerator getReportGenerator () {// มันยังคงเป็น container.getBean () เพียงผู้แทนจะใช้ Return (ReportGenerator) container.getBean ("ReportGeneraator"); }} class Reportservice {// reportservice ในท้ายที่สุดมันยังคง 'การค้นหาและมอบหมายอย่างแข็งขัน' และมอบหมายให้ ServiceLocator Private ReportGenerator ReportGenerator = servicelocator.getReportGenerator (); -
สารละลาย:
ในกรณีนี้การเปลี่ยน 'แอคทีฟ' เป็น 'แฝง' จะลดความรู้ภายในของรายงาน (เช่นตรรกะของการค้นหาส่วนประกอบ) อย่างไม่ต้องสงสัย ตามหลักการของการผกผันของการควบคุม (IOC) การดึงนี้ (ดึง, ใช้งาน) ถูกแปลงเป็นโหมดกด (กด, passive)
ตัวอย่างเช่นการสมัครสมาชิก RSS ที่เรามักจะใช้คือแอปพลิเคชันแบบพุชซึ่งช่วยให้เราทราบถึงความยุ่งยากในการเข้าสู่เว็บไซต์ที่เราโปรดปรานหลายครั้งต่อวันเพื่อรับการอัปเดตบทความอย่างแข็งขัน
การฉีดพึ่งพา (DI) ได้รับการต้อนรับแบบพาสซีฟประเภทนี้และลดปัญหาของลูกค้า (ที่นี่รายงาน) ตัวเองมีตรรกะที่ซับซ้อนและรู้มากเกินไป
วิธีการดำเนินการ:
เนื่องจากเราต้องการเป็นแผนกต้อนรับ 'พาสซีฟ' เราจึงควรกลับไปที่ตัวอย่างคอนเทนเนอร์โดยไม่ต้องใช้โหมดตัวระบุตำแหน่งบริการ ไดอะแกรมคลาสที่ได้รับการแก้ไขนั้นได้มาจากนี้ดังนี้:
แผนภาพคลาสดั้งเดิมมีดังนี้ คุณสามารถตรวจสอบและให้ความสนใจกับพรอมต์ความคิดเห็น:
การใช้รหัส:
เพื่อให้ตัวอย่างที่จะรวบรวมและเรียกใช้และเพื่อใช้ผลลัพธ์การรันของรหัสการติดตามเพื่อยกตัวอย่างแผนผังคลาสทั้งหมดอย่างชัดเจนและร่วมมือกันอย่างชัดเจนตามลำดับคำสั่งการพิมพ์ที่มีหมายเลขจำนวนมากและสองคลาสที่ไม่มีนัยสำคัญถูกเพิ่มเข้าไปในตัวสร้างของแต่ละชั้นเรียน
นำเข้า java.util.date; นำเข้า java.util.hashmap; นำเข้า java.util.map; // เพื่อที่จะสามารถรวบรวมและเรียกใช้มีคลาสที่ไม่มีนัยสำคัญอีกสองคลาส เดือนคลาส {} ตารางคลาส {publicvoid setDate (วันที่) {} publicVoid setMonth (เดือนเดือน) {}} // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ของ Excelgenerator ... "); } PublicVoid สร้าง (ตารางตาราง) {system.out.println ("สร้างรายงาน Excel ... "); }} คลาส pdfgenerator ใช้ reportGenerator {pdfgenerator () {system.out.println ("2 ... เริ่มต้นการเริ่มต้นของ pdfgenerator ... "); } PublicVoid สร้าง (ตารางตาราง) {System.out.println ("สร้างรายงาน PDF ... "); - - รับและจัดการการอ้างอิงของ Reportservice Reportservice Reportservice = New Reportservice (); // ฉีดอินสแตนซ์ reportGenerator เฉพาะที่สร้างขึ้นข้างต้น reportservice.setReportGenerator (ReportGenerator); beans.put ("Reportservice", Reportservice); System.out.println ("5 ... คอนเทนเนอร์เริ่มต้นสิ้นสุด ... "); } PublicStatic Object getBean (string id) {system.out.println ("ส่วนประกอบรับใช้ล่าสุด ... getBean () ->" + id + "... "); returnbeans.get (id); }} class Reportservice {// private reportGenerator generator = pdfgenerator ใหม่ (); // กำจัดความสัมพันธ์ระหว่างคัปปลิ้งที่แน่นหนาด้านบนและแทนที่ด้วยคอนเทนเนอร์ // private ReportGenerator Generator = (ReportGenerator) คอนเทนเนอร์ // .getBean ("ReportGenerator"); // ลบการค้นหา "ใช้งาน" ด้านบนและจัดเตรียมฟิลด์ส่วนตัวเพื่อบันทึกตัวสร้างรายงานส่วนตัวที่ฉีดออกจากภายนอก // inject publicVoid จากภายนอกใน Setter Way SetReportGenerator (ReportGenerator Generator) {System.out.println ("4 ... เริ่มรายงานการฉีดยา ... "); this.generator = เครื่องกำเนิดไฟฟ้า; } ตารางตารางส่วนตัว = ตารางใหม่ (); Public Reportservice () {system.out.println ("3 ... เริ่มต้นการเริ่มต้นรายงานการรายงาน ... "); } publicVoid getDailyReport (วันที่วันที่) {table.setDate (วันที่); generator.generate (ตาราง); } PublicVoid getMonthlyReport (เดือนเดือน) {table.setMonth (เดือน); generator.generate (ตาราง); }} ไคลเอนต์สาธารณะ {publicStaticVoid Main (String [] args) {// เริ่มต้นคอนเทนเนอร์คอนเทนเนอร์ใหม่ (); Reportservice Reportservice = (Reportservice) คอนเทนเนอร์. getBean ("Reportservice"); Reportservice.getDailyReport (วันที่ใหม่ ()); // Reportservice.getMonthlyReport (วันที่ใหม่ ()); -
ผลการทำงาน:
1 ... เริ่มต้นคอนเทนเนอร์เริ่มต้น ... 2 ... เริ่มต้นการเริ่มต้น pdfgenerator ... 3 ... เริ่มต้นการเริ่มต้นรายงานการรายงาน ... 4 ... เริ่มรายงานการฉีดวัคซีน ... 5 ... การเริ่มต้นคอนเทนเนอร์เริ่มต้น ... ในที่สุดก็ได้รับส่วนประกอบบริการ ...
สังเกต:
1. ตามลำดับการพิมพ์ของผลลัพธ์การรันข้างต้นจะเห็นได้ว่าตัวเลขเฉพาะที่เพิ่มเข้ามาในรหัสมีความสมเหตุสมผลซึ่งจำลองกระบวนการดำเนินการโปรแกรมดังนั้นจึงไม่ดึงไดอะแกรมลำดับอีกต่อไป
2. โปรดทราบว่าการใช้ IOC และ DI ในตัวอย่างนี้ขึ้นอยู่กับรายงานการรายงานเป็นไคลเอนต์ (เช่นส่วนประกอบ demander) และรหัสทดสอบในคลาสไคลเอ็นต์ Main () ในรหัสคือผู้ใช้ปลายทางขององค์ประกอบบริการ แต่สิ่งที่ต้องการไม่ใช่ส่วนประกอบ แต่บริการที่ส่วนประกอบมี
3. ในความเป็นจริงในคลิปกล่องสปริงการเริ่มต้นคอนเทนเนอร์นั้นไม่ใช่สิ่งที่ไคลเอนต์ผู้ใช้ปลายทางควรทำควรเริ่มต้นล่วงหน้าโดยผู้ให้บริการ
4. ในไคลเอนต์ผู้ใช้ปลายทางเรายังคงใช้ container.getBean ("รายงาน") เพื่อรับส่วนประกอบบริการที่ได้รับการสร้างอินสแตนซ์ในคอนเทนเนอร์คอนสตรัคเตอร์ล่วงหน้า ในแอปพลิเคชันเฉพาะส่วนประกอบบริการที่มีอยู่มักจะถูกปรับใช้กับเซิร์ฟเวอร์โดยใช้ XML และไฟล์การกำหนดค่าอื่น ๆ จากนั้นไฟล์การกำหนดค่าจะถูกอ่านโดยคอนเทนเนอร์และรวมกับเทคโนโลยีการสะท้อนเพื่อสร้างและฉีดส่วนประกอบบริการเฉพาะ
วิเคราะห์:
ก่อนหน้านี้ Reportservice ร้องขอส่วนประกอบบริการจากคอนเทนเนอร์อย่างแข็งขัน แต่ตอนนี้มันรอการฉีดคอนเทนเนอร์ (ฉีดนั่นคือ push) ส่วนประกอบบริการ การควบคุมจะถูกถ่ายโอนอย่างชัดเจนจากโมดูลพื้นฐาน (Reportservice เป็นส่วนประกอบ demander) ไปยังโมดูลระดับสูง (คอนเทนเนอร์เป็นผู้ให้บริการส่วนประกอบ) นั่นคือการควบคุมกลับด้าน