วิธีสร้างสถาปัตยกรรมปลั๊กอินแบบยืดหยุ่น


154

ชุดรูปแบบซ้ำในงานพัฒนาของฉันคือการใช้หรือการสร้างสถาปัตยกรรมปลั๊กอินภายในองค์กร ฉันเคยเห็นมันเข้าหาหลายวิธี - ไฟล์การกำหนดค่า (XML, .conf และอื่น ๆ ) เฟรมเวิร์กการสืบทอดข้อมูลฐานข้อมูลไลบรารีและอื่น ๆ จากประสบการณ์ของฉัน:

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

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

ตัวอย่างเล็ก ๆ น้อย ๆ ที่ฉันพบด้วยตัวเอง:

ตัวอย่างเหล่านี้ดูเหมือนจะเล่นกับจุดแข็งด้านภาษาต่างๆ สถาปัตยกรรมปลั๊กอินที่ดีจำเป็นต้องเชื่อมโยงกับภาษาหรือไม่ เป็นการดีที่สุดที่จะใช้เครื่องมือในการสร้างสถาปัตยกรรมปลั๊กอินหรือใช้กับรุ่นต่อไปนี้ของตัวเอง?


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

@mdma - นั่นเป็นคำถามที่ดี ฉันว่ามีเป้าหมายร่วมกันในระบบที่ขยายได้ บางทีเป้าหมายเป็นสิ่งที่พบได้ทั่วไปและวิธีแก้ปัญหาที่ดีที่สุดอาจแตกต่างกันไปตามความต้องการของระบบที่ขยายได้ภาษาที่ใช้ในการเขียนภาษาและอื่น ๆ อย่างไรก็ตามฉันเห็นรูปแบบเช่น IOC ถูกนำไปใช้กับหลาย ๆ ภาษาและฉันถูกขอให้ทำปลั๊กอินที่คล้ายกัน ฉันคิดว่าเป็นการดีที่จะได้แนวคิดทั่วไปเกี่ยวกับวิธีปฏิบัติที่ดีที่สุดสำหรับปลั๊กอินประเภทต่างๆ
justkt

คำตอบ:


89

นี่ไม่ใช่คำตอบที่มากพอ ๆ กับคำพูด / ตัวอย่างที่อาจเป็นประโยชน์

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

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

  • หนึ่งที่ดีก็คือpy.test มันเป็นไปตามปรัชญา "API ที่ดีที่สุดคือไม่มี API" และอาศัยเบ็ดเดียวที่ถูกเรียกในทุกระดับ คุณสามารถแทนที่ hooks เหล่านี้ในไฟล์ / ฟังก์ชั่นที่ตั้งชื่อตามแบบแผนและปรับเปลี่ยนพฤติกรรม คุณสามารถดูรายการปลั๊กอินในเว็บไซต์เพื่อดูว่าสามารถนำไปใช้ได้อย่างรวดเร็ว / ง่ายดาย

จุดทั่วไปไม่กี่

  • พยายามทำให้แกนที่ไม่สามารถขยายได้ / ไม่สามารถปรับเปลี่ยนได้ของผู้ใช้มีขนาดเล็กที่สุดเท่าที่จะทำได้ มอบหมายทุกสิ่งที่คุณทำได้ให้กับเลเยอร์ที่สูงขึ้นเพื่อเพิ่มความสามารถในการขยาย สิ่งที่น้อยลงในการแก้ไขในหลักแล้วในกรณีที่เลือกไม่ถูกต้อง
  • เกี่ยวข้องกับประเด็นข้างต้นคือคุณไม่ควรตัดสินใจมากเกินไปเกี่ยวกับทิศทางของโครงการเมื่อเริ่มแรก ใช้เซ็ตย่อยที่ต้องการน้อยที่สุดจากนั้นเริ่มเขียนปลั๊กอิน
  • หากคุณกำลังฝังภาษาสคริปต์ให้แน่ใจว่ามันเป็นหนึ่งเต็มรูปแบบในที่ที่คุณสามารถเขียนโปรแกรมทั่วไปและไม่ใช่ภาษาของเล่นเพียงสำหรับการใช้งานของคุณ
  • ลดสำเร็จรูปให้มากที่สุด ไม่ต้องกังวลกับคลาสย่อย API ที่ซับซ้อนการลงทะเบียนปลั๊กอินและสิ่งอื่น ๆ พยายามที่จะเก็บไว้เพื่อให้ง่ายว่ามันเป็นเรื่องง่ายและไม่เพียง แต่เป็นไปได้ที่จะขยาย สิ่งนี้จะทำให้ปลั๊กอิน API ของคุณมีการใช้งานมากขึ้นและจะส่งเสริมให้ผู้ใช้เขียนปลั๊กอิน ไม่ใช่แค่นักพัฒนาปลั๊กอิน py.test ทำสิ่งนี้ได้ดี Eclipse เท่าที่ผมรู้ไม่ได้

1
ฉันจะขยายจุดภาษาสคริปต์ที่เป็นมูลค่าการพิจารณาการฝังเป็นภาษาที่มีอยู่เช่นงูหลามหรือ Perl
ฤดี

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

24

จากประสบการณ์ของฉันฉันพบว่ามีปลั๊กอินสองประเภทจริงๆ

  1. อย่างใดอย่างหนึ่งดังต่อไปนี้Eclipse modelซึ่งมีขึ้นเพื่อให้มีอิสระและเป็นปลายเปิด
  2. อื่น ๆ มักจะต้องใช้ปลั๊กอินในการติดตามnarrow APIเพราะปลั๊กอินจะเติมฟังก์ชั่นที่เฉพาะเจาะจง

เพื่อให้รัฐนี้ในทางที่แตกต่างกันหนึ่งช่วยให้ปลั๊กอินเพื่อเข้าถึงแอพลิเคชันของคุณในขณะที่คนอื่น ๆจะช่วยให้แอพลิเคชันของคุณเพื่อเข้าถึงปลั๊กอิน

ความแตกต่างนั้นบอบบางและบางครั้งก็ไม่มีการบิดเบือน ... คุณต้องการทั้งสองอย่างสำหรับการสมัคร

ฉันไม่มีประสบการณ์มากมายเกี่ยวกับ Eclipse / การเปิดแอปของคุณไปยังโมเดลปลั๊กอิน (บทความในโพสต์ของ Kalkie นั้นยอดเยี่ยม) ฉันได้อ่านนิดหน่อยเกี่ยวกับวิธีที่ eclipse ทำสิ่งต่างๆ แต่ไม่มีอะไรมากไปกว่านั้น

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

งานส่วนใหญ่ที่ฉันทำไปนั้นใช้สถาปัตยกรรมปลั๊กอินเพื่ออนุญาตให้แอปของฉันเข้าถึงปลั๊กอินสิ่งต่าง ๆ เช่นข้อมูลเวลา / การแสดงผล / แผนที่เป็นต้น

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

  • ตอนนี้ฉันมักจะได้DI frameworkทำงานส่วนใหญ่แล้ว

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


@ จัสตินใช่ DI = การฉีดพึ่งพา
มา

14

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

http://www.eclipse.org/articles/Article-Plug-in-architecture/plugin_architecture.html


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

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

6
วิธีการเดียวกัน (ทุกอย่างเป็นปลั๊กอิน) ใช้ในเฟรมเวิร์ก Symfony เวอร์ชันใหม่ พวกมันถูกเรียกว่าบันเดิล แต่หลักการก็เหมือนกัน มันเป็นสถาปัตยกรรมที่ค่อนข้างง่ายบางทีคุณควรดู: symfony-reloaded.org/quick-tour-part-4
Marijn Huizendveld

@Marjin Huizendveld - คุณควรเพิ่มคำตอบนี้เป็นคำตอบแยกต่างหากเพื่อให้ฉันสามารถโหวตคำตอบได้ ดูเหมือนกรอบที่น่าสนใจ!
justkt

11

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

IPluginอินเตอร์เฟซที่ใช้ในการระบุชั้นเรียนที่ใช้ปลั๊กอิน มีการเพิ่มวิธีการในอินเทอร์เฟซเพื่ออนุญาตให้แอปพลิเคชันสื่อสารกับปลั๊กอิน ตัวอย่างเช่นInitวิธีการที่แอปพลิเคชันจะใช้เพื่อแนะนำให้ปลั๊กอินเริ่มต้น

ในการค้นหาปลั๊กอินแอปพลิเคชันจะสแกนโฟลเดอร์ปลั๊กอินสำหรับแอสเซมบลี. Net แต่ละชุดจะถูกโหลด IPluginสะท้อนจะใช้ในการสแกนสำหรับการเรียนที่ใช้ อินสแตนซ์ของแต่ละคลาสปลั๊กอินถูกสร้างขึ้น

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

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

IApplicationมีวิธีการที่อนุญาตให้ปลั๊กอินสื่อสารกับแอปพลิเคชัน ตัวอย่างเช่นถ้าคุณกำลังเขียนเท็กซ์เอดิเตอร์อินเตอร์เฟสนี้จะมีOpenDocumentsคุณสมบัติที่อนุญาตให้ปลั๊กอินระบุจำนวนคอลเล็กชันของเอกสารที่เปิดอยู่ในปัจจุบัน

ระบบปลั๊กอินนี้สามารถขยายไปยังภาษาสคริปต์เช่น Lua โดยการสร้างคลาสปลั๊กอินที่ได้รับเช่นการLuaPluginส่งต่อIPluginฟังก์ชั่นและส่วนต่อประสานแอปพลิเคชันไปยังสคริปต์ Lua

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


7

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

หากคุณสนใจอ่านเพิ่มเติม ...


ขอบคุณดูน่าสนใจ มันง่ายแค่ไหนที่จะใช้หลักการที่คล้ายกันกับภาษาอื่น ๆ ?
justkt

จริง ๆ แล้ว MEF สำหรับ. NET Framework เท่านั้นฉันไม่รู้อะไรเกี่ยวกับเฟรมเวิร์กอื่น ๆ
BALKANGraph

7

ฉันเคยทำงานในโครงการที่ต้องยืดหยุ่นในวิธีที่ลูกค้าแต่ละรายสามารถติดตั้งระบบซึ่งการออกแบบที่ดีเพียงอย่างเดียวที่เราพบคือการส่งคอมไพเลอร์ C # ให้กับลูกค้า!

หากข้อมูลจำเพาะเต็มไปด้วยคำเช่น:

  • คล่องตัว
  • เสียบเข้าไป
  • ที่ปรับแต่งได้

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

การสนับสนุนของลูกค้า (หรือคนสนับสนุนบรรทัด) การเขียนโปรแกรมเสริมนั้นยากกว่าสถาปัตยกรรมมาก


5

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

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

เพื่อให้ใช้งานได้ระบบสคริปต์จะต้องมี API ที่มีประสิทธิภาพพอสมควรโดยมี hooks สำหรับข้อมูลแอปพลิเคชันตรรกะและ GUI รวมถึงฟังก์ชันพื้นฐานของการนำเข้าและเรียกใช้โค้ดจากไลบรารี นอกจากนี้สคริปต์จะต้องปลอดภัยในแง่ที่ว่าแอปพลิเคชันสามารถกู้คืนได้อย่างงดงามจากสคริปต์ที่เขียนไม่ดี การใช้ระบบสคริปต์เป็นเลเยอร์ทางอ้อมหมายความว่าแอปพลิเคชันสามารถแยกตัวออกได้ง่ายขึ้นในกรณีของ Something Bad ™

ปลั๊กอินของบรรจุภัณฑ์นั้นขึ้นอยู่กับความชอบส่วนบุคคลเป็นส่วนใหญ่ แต่คุณไม่เคยผิดพลาดกับไฟล์เก็บถาวรที่ถูกบีบอัดด้วยส่วนต่อประสานที่ใช้งานง่ายพูดPluginName.extในไดเรกทอรีราก


3

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

ฉันพบว่าอาจารย์ใหญ่ IOC (Inversion of Control) (read springframework) ทำงานได้ดีสำหรับการจัดทำฐานที่ยืดหยุ่นซึ่งคุณสามารถเพิ่มความเชี่ยวชาญเพื่อทำให้การพัฒนาปลั๊กอินง่ายขึ้น

  • คุณสามารถสแกนคอนเทนเนอร์สำหรับกลไก "อินเตอร์เฟสเป็นโฆษณาประเภทปลั๊กอิน"
  • คุณสามารถใช้คอนเทนเนอร์เพื่อฉีดการขึ้นต่อกันทั่วไปที่ปลั๊กอินอาจต้องการ (เช่น ResourceLoaderAware หรือ MessageSourceAware)

1

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

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