มีการใช้หลักการผกผันการพึ่งพาอาศัยกันโดยใช้ การผกผันการพึ่งพา

บ้าน / ทะเลาะกัน

อันที่จริงหลักการทั้งหมด แข็งมีการเชื่อมต่อถึงกันอย่างแน่นหนา และเป้าหมายหลักของพวกเขาคือการช่วยสร้างซอฟต์แวร์คุณภาพสูงที่ปรับขนาดได้ แต่หลักการสุดท้าย แข็งโดดเด่นกว่าพวกเขาจริงๆ อันดับแรก มาดูการกำหนดหลักการนี้กัน ดังนั้น, หลักการผกผันการพึ่งพา (หลักการผกผันการพึ่งพา - DIP): “ขึ้นอยู่กับนามธรรม ไม่มีการพึ่งพาสิ่งใดเป็นพิเศษ. โรเบิร์ต มาร์ติน ผู้เชี่ยวชาญด้านการพัฒนาซอฟต์แวร์ชื่อดัง ยังเน้นย้ำถึงหลักการ จุ่มและนำเสนอง่ายๆ ว่าเป็นผลจากการทำตามหลักการอื่นๆ แข็ง— หลักการเปิด/ปิด และหลักการทดแทน Liskov จำได้ว่าข้อแรกบอกว่าไม่ควรแก้ไขคลาสเพื่อทำการเปลี่ยนแปลงใหม่ และข้อที่สองเกี่ยวข้องกับการสืบทอดและถือว่าการใช้ประเภทพื้นฐานบางประเภทที่ได้รับมาอย่างปลอดภัยโดยไม่ทำลายการทำงานที่ถูกต้องของโปรแกรม เดิมที Robert Martin ได้กำหนดหลักการนี้ไว้ดังนี้:

หนึ่ง). โมดูลระดับบนไม่ควรขึ้นอยู่กับโมดูลระดับล่าง โมดูลทั้งสองระดับต้องขึ้นอยู่กับนามธรรม

2). นามธรรมไม่ควรขึ้นอยู่กับรายละเอียด รายละเอียดควรขึ้นอยู่กับนามธรรม

นั่นคือจำเป็นต้องพัฒนาคลาสในแง่ของ abstractions ไม่ใช่การใช้งานเฉพาะ และถ้าคุณปฏิบัติตามหลักการ OCPและ LSPนั่นคือสิ่งที่เราจะบรรลุ ดังนั้น ให้กลับไปที่บทเรียนเล็กน้อย ที่นั่นเป็นตัวอย่างเราพิจารณาคลาส กวีซึ่งตอนแรกได้เดินสายไปยังชั้นเรียน กีตาร์, เป็นตัวแทนของเครื่องดนตรีเฉพาะ:

กวีระดับสาธารณะ ( กีตาร์กีตาร์ส่วนตัว กวีสาธารณะ (กีตาร์กีตาร์) ( this.guitar = กีตาร์; ) การเล่นโมฆะสาธารณะ () ( guitar.play (); ) )

กวีชั้นสาธารณะ(

กีตาร์กีตาร์ส่วนตัว

กวีสาธารณะ (กีตาร์ กีตาร์ )

นี้. กีต้าร์ = กีต้าร์ ;

การเล่นเป็นโมฆะสาธารณะ ()

กีตาร์. เล่น();

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

ไฟล์ Instrument.java:

อินเทอร์เฟซสาธารณะ ตราสาร (เล่นเป็นโมฆะ (); )

เครื่องมืออินเทอร์เฟซสาธารณะ (

voidplay();

ไฟล์ Guitar.java:

class Guitar ใช้เครื่องดนตรี ( @Override public void play() ( System.out.println("Play Guitar!"); ) )

คลาส Guitar ใช้เครื่องมือ (

@แทนที่

การเล่นเป็นโมฆะสาธารณะ ()

ระบบ. ออก . println("เล่นกีตาร์!");

ไฟล์ lute.java:

คลาสสาธารณะ Lute ใช้เครื่องมือ ( @Override public void play() ( System.out.println("Play Lute!"); ) )

คลาสสาธารณะ Lute ใช้เครื่องมือ (

@แทนที่

การเล่นเป็นโมฆะสาธารณะ ()

ระบบ. ออก . println("เล่นลูท!" );

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

คลาสสาธารณะ กวี ( เครื่องดนตรีส่วนตัว กวีสาธารณะ () ( ) การเล่นโมฆะสาธารณะ () ( instrument.play (); ) โมฆะสาธารณะ setInstrument (เครื่องดนตรี) ( this.instrument = เครื่องดนตรี; ) )

กวีชั้นสาธารณะ(

เครื่องดนตรีส่วนตัว ;

2 ตอบกลับ

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

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

จุดหนึ่งที่ควรทราบเมื่ออ่านบทความของลุงบ๊อบบน DIP คือ C++ ไม่มี (และในขณะที่เขียนไม่มี) มีส่วนต่อประสาน ดังนั้นการบรรลุสิ่งที่เป็นนามธรรมใน C ++ มักจะทำได้ผ่านคลาสฐานเสมือนที่เป็นนามธรรม/บริสุทธิ์ ในขณะที่ใน Java หรือ C# สิ่งที่เป็นนามธรรมเพื่อคลายการมีเพศสัมพันธ์มักจะยกเลิกการเชื่อมโยงโดยแยกส่วนต่อประสานออกจากการพึ่งพาและผูกโมดูลระดับสูงกว่ากับอินเทอร์เฟซ

แก้ไขเพียงเพื่อชี้แจง:

"ในบางที่ ฉันยังเห็นว่ามันถูกเรียกว่าการผกผันของการพึ่งพา"

ผกผัน:สลับการจัดการการพึ่งพาจากแอปพลิเคชันไปยังคอนเทนเนอร์ (เช่น Spring)

การฉีดพึ่งพา:

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

แล้วการผกผันของการควบคุม (IoC) ล่ะ?

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

การผกผันของการควบคุมเป็นแนวทางการออกแบบมีจุดประสงค์ดังต่อไปนี้:

  • มีการแยกการดำเนินการของงานเฉพาะจากการนำไปใช้งาน
  • แต่ละโมดูลสามารถมุ่งเน้นไปที่สิ่งที่มีไว้สำหรับ
  • โมดูลไม่ได้ตั้งสมมติฐานใดๆ เกี่ยวกับสิ่งที่ระบบอื่นๆ ทำ แต่อาศัยสัญญาของพวกเขา
  • การเปลี่ยนโมดูลไม่มีผลกับโมดูลอื่นๆ

ดูข้อมูลเพิ่มเติม

ปรับปรุงล่าสุด: 03/11/2016

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

โมดูลระดับบนสุดไม่ควรขึ้นอยู่กับโมดูลระดับล่าง ทั้งสองต้องขึ้นอยู่กับนามธรรม

นามธรรมไม่ควรขึ้นอยู่กับรายละเอียด รายละเอียดควรขึ้นอยู่กับนามธรรม

เพื่อให้เข้าใจหลักการ พิจารณาตัวอย่างต่อไปนี้:

หนังสือคลาส ( ข้อความสตริงสาธารณะ ( รับ; ชุด; ) เครื่องพิมพ์ ConsolePrinter สาธารณะ ( รับ; ชุด; ) โมฆะสาธารณะ พิมพ์() ( Printer.Print(ข้อความ); ) ) คลาส ConsolePrinter ( โมฆะสาธารณะ พิมพ์ (ข้อความสตริง) ( Console.WriteLine (ข้อความ); ) )

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

ตอนนี้ เรามาลองทำให้คลาสของเราสอดคล้องกับหลักการผกผันการพึ่งพาโดยแยก abstractions ออกจากการใช้งานระดับต่ำ:

อินเทอร์เฟซ IPrinter ( void Print(string text); ) class Book ( public string Text ( get; set; ) เครื่องพิมพ์ IPrinter สาธารณะ ( get; set; ) หนังสือสาธารณะ (เครื่องพิมพ์ IPrinter) ( this.Printer = เครื่องพิมพ์; ) public void Print( ) ( Printer.Print(Text); ) ) คลาส ConsolePrinter: IPrinter ( โมฆะสาธารณะ Print(ข้อความสตริง) ( Console.WriteLine("พิมพ์ไปยังคอนโซล"); ) คลาส HtmlPrinter: IPrinter ( โมฆะสาธารณะ พิมพ์ (ข้อความสตริง) ( Console.WriteLine("พิมพ์เป็น html"); ) )

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

หนังสือหนังสือ = หนังสือใหม่ (ใหม่ ConsolePrinter ()); book.พิมพ์(); book.Printer = HtmlPrinter ใหม่ (); book.พิมพ์();

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

คำอธิบายนี้ได้รับการพัฒนาร่วมกับ Vladimir Matveev เพื่อเตรียมชั้นเรียนกับนักเรียน Java

บทความอื่น ๆ จากชุดนี้:

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

ต่ำกว่าคลาส A ภายในเมธอดที่เรียกว่า someMethod() จะสร้างวัตถุของคลาส B อย่างชัดเจนและเข้าถึงเมธอดของ someMethodOfB()

คลาสสาธารณะ A ( เป็นโมฆะ someMethod() ( B b = new B(); b.someMethodOfB(); ) )

ในทำนองเดียวกัน ตัวอย่างเช่น คลาส B อ้างถึงฟิลด์สแตติกและวิธีการของคลาสระบบอย่างชัดเจน:

คลาสสาธารณะ B ( ถือเป็นโมฆะ someMethodOfB() ( System.out.println("สวัสดีชาวโลก"); ) )

ในทุกกรณีที่คลาสใด ๆ (ประเภท A) สร้างคลาสใด ๆ (ประเภท B) หรือเข้าถึงฟิลด์สแตติกหรือสมาชิกคลาสอย่างชัดเจน สิ่งนี้เรียกว่า ตรงติดยาเสพติด เหล่านั้น. สำคัญ: ถ้าคลาสภายในตัวเองทำงานภายในตัวเองกับคลาสอื่น นี่คือการพึ่งพา หากเขาสร้างคลาสนี้ในตัวเองด้วย ตรงติดยาเสพติด

เกิดอะไรขึ้นกับการพึ่งพาโดยตรง? การพึ่งพาโดยตรงนั้นไม่ดีเพราะคลาสที่สร้างคลาสอื่นภายในตัวเองอย่างอิสระนั้นผูกติดอยู่กับคลาสนี้อย่างแน่นหนา เหล่านั้น. หากมีการเขียนชัดเจนว่า B = new B(); จากนั้นคลาส A จะทำงานกับคลาส B เสมอและไม่มีคลาสอื่น หรือถ้ามันบอกว่า System.out.println("..."); จากนั้นคลาสจะส่งออกไปยัง System.out เสมอและไม่มีที่อื่น

สำหรับชั้นเรียนขนาดเล็ก การพึ่งพาอาศัยกันนั้นไม่น่ากลัว รหัสดังกล่าวอาจใช้งานได้ดี แต่ในบางกรณี เพื่อให้คลาส A ของคุณทำงานได้ในระดับสากลในสภาพแวดล้อมของคลาสที่แตกต่างกัน คลาส A อาจต้องมีการใช้งานคลาสอื่น - การพึ่งพา เหล่านั้น. คุณจะต้องใช้ตัวอย่างเช่นไม่ใช่คลาส B แต่เป็นคลาสอื่นที่มีอินเทอร์เฟซเดียวกันหรือไม่ System.out แต่ตัวอย่างเช่นเอาต์พุตไปยังตัวบันทึก (เช่น log4j)

การพึ่งพาโดยตรงสามารถแสดงแบบกราฟิกได้ดังนี้:

เหล่านั้น. เมื่อคุณสร้างคลาส A ในรหัสของคุณ: A a = new A(); อันที่จริงไม่มีการสร้างคลาส A ขึ้น แต่เป็นลำดับชั้นทั้งหมดของคลาสที่ขึ้นต่อกัน ตัวอย่างที่อยู่ในภาพด้านบน ลำดับชั้นนี้ "เข้มงวด": โดยไม่ต้องเปลี่ยนซอร์สโค้ดของแต่ละคลาส จะไม่สามารถแทนที่คลาสในลำดับชั้นได้ ดังนั้นคลาส A ในการใช้งานดังกล่าวจึงไม่สามารถปรับตัวให้เข้ากับสภาพแวดล้อมที่เปลี่ยนแปลงได้ เป็นไปได้มากว่าจะไม่สามารถใช้มันในรหัสใด ๆ ได้ ยกเว้นรหัสเฉพาะที่คุณเขียน

หากต้องการแยกคลาส A ออกจากการพึ่งพาเฉพาะ ให้ใช้ การฉีดพึ่งพา. การฉีดพึ่งพาคืออะไร? แทนที่จะสร้างคลาสที่ต้องการอย่างชัดเจนในโค้ด การพึ่งพาจะถูกส่งไปยังคลาส A ผ่านตัวสร้าง:

คลาสสาธารณะ A ( ส่วนตัวสุดท้าย B b; สาธารณะ A(B b) ( this.b = b; ) โมฆะสาธารณะ someMethod() ( b.someMethodOfB(); ) )

ที่. คลาส A ตอนนี้ได้รับการพึ่งพาผ่านตัวสร้าง ตอนนี้ ในการสร้างคลาส A คุณต้องสร้างคลาสที่ขึ้นต่อกันก่อน ในกรณีนี้คือ B:

B b = ใหม่ B(); A a = ใหม่ A(b); a.someMethod();

หากทำซ้ำขั้นตอนเดียวกันสำหรับชั้นเรียนทั้งหมดเช่น ส่งผ่านอินสแตนซ์ของคลาส D ไปยังคอนสตรัคเตอร์ของคลาส B ไปยังคอนสตรัคเตอร์ของคลาส D - การพึ่งพา E และ F ฯลฯ จากนั้นคุณจะได้รับโค้ดซึ่งการพึ่งพาทั้งหมดจะถูกสร้างขึ้นในลำดับที่กลับกัน:

G g = ใหม่ G(); H ชั่วโมง = ใหม่ H(); F f = ใหม่(g,h); E e = ใหม่ E(); D d = ใหม่ D(e,f); B b = ใหม่ B(d); A a = ใหม่ A(b); a.someMethod();

กราฟสามารถแสดงได้ดังนี้:

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

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

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

© 2022 skudelnica.ru -- ความรัก การทรยศ จิตวิทยา การหย่าร้าง ความรู้สึก การทะเลาะวิวาท