อินเตอร์เฟสแยกหลักการพูดว่า:
ไม่ควรบังคับให้ลูกค้าพึ่งพาวิธีการที่ไม่ได้ใช้ ISP แบ่งอินเตอร์เฟสที่มีขนาดใหญ่เป็นขนาดเล็กและเจาะจงมากขึ้นเพื่อให้ลูกค้าจะต้องรู้เกี่ยวกับวิธีการที่พวกเขาสนใจ
มีคำถามที่ยังไม่ได้ตอบสองสามข้อที่นี่ หนึ่งคือ:
เล็กแค่ไหน?
คุณพูด:
ขณะนี้ฉันจัดการเรื่องนี้โดยการแบ่งเนมสเปซของโมดูลขึ้นอยู่กับความต้องการของลูกค้า
ฉันเรียกคู่มือการพิมพ์เป็ดนี้ คุณสร้างอินเทอร์เฟซที่เปิดเผยสิ่งที่ลูกค้าต้องการเท่านั้น หลักการแยกส่วนต่อประสานนั้นไม่ใช่เพียงแค่การพิมพ์ด้วยมือเท่านั้น
แต่ผู้ให้บริการอินเทอร์เน็ตไม่ใช่เพียงแค่การเรียกใช้อินเทอร์เฟซบทบาท "ที่ต่อเนื่องกัน" เท่านั้นที่สามารถนำมาใช้ซ้ำได้ ไม่มีการออกแบบส่วนต่อประสานบทบาท "ที่ต่อเนื่องกัน" ได้อย่างสมบูรณ์แบบสามารถป้องกันการเพิ่มลูกค้าใหม่ด้วยความต้องการบทบาทของตัวเอง
ISPเป็นวิธีแยกลูกค้าออกจากผลกระทบของการเปลี่ยนแปลงการให้บริการ มันมีจุดประสงค์เพื่อให้บิลด์ทำงานเร็วขึ้นเมื่อคุณทำการเปลี่ยนแปลง แน่นอนว่ามันมีประโยชน์อื่น ๆ เช่นไม่ทำลายลูกค้า แต่นั่นคือประเด็นหลัก ถ้าฉันเปลี่ยนcount()
ลายเซ็นของฟังก์ชั่นบริการก็ดีถ้าลูกค้าที่ไม่ใช้count()
ไม่จำเป็นต้องแก้ไขและคอมไพล์ใหม่
นี่คือเหตุผลที่ฉันสนใจเกี่ยวกับหลักการแยกส่วนต่อประสาน มันไม่ใช่สิ่งที่ฉันเชื่อมั่นว่าสำคัญ มันแก้ปัญหาจริง
ดังนั้นวิธีที่ควรนำไปใช้ควรแก้ปัญหาให้คุณ ไม่มีวิธีการท่องจำสมองที่จะใช้ ISP ที่ไม่สามารถเอาชนะได้ด้วยตัวอย่างที่ถูกต้องของการเปลี่ยนแปลงที่จำเป็น คุณควรจะดูว่าระบบมีการเปลี่ยนแปลงและเลือกตัวเลือกที่จะทำให้สิ่งที่เงียบลง ลองสำรวจตัวเลือกต่างๆ
ก่อนอื่นถามตัวเอง: การเปลี่ยนแปลงส่วนต่อประสานบริการนั้นยากไหม ถ้าไม่ออกไปข้างนอกและเล่นจนกว่าคุณจะสงบลง นี่ไม่ใช่การออกกำลังกายทางปัญญา โปรดให้แน่ใจว่าการรักษาไม่เลวร้ายยิ่งกว่าโรค
ถ้าไคลเอนต์จำนวนมากใช้ชุดย่อยของฟังก์ชันที่ระบุสำหรับอินเทอร์เฟซที่นำกลับมาใช้ "coherent" ส่วนย่อยน่าจะมุ่งเน้นไปที่แนวคิดหนึ่งที่เราสามารถคิดได้ว่าเป็นบทบาทที่บริการให้แก่ลูกค้า มันดีเมื่อใช้งานได้ มันไม่ได้ผลเสมอไป
หากลูกค้าจำนวนมากใช้ฟังก์ชั่นย่อยที่แตกต่างกันเป็นไปได้ว่าลูกค้ากำลังใช้บริการผ่านหลายบทบาท ไม่เป็นไร แต่มันทำให้บทบาทยากต่อการมองเห็น ค้นหาพวกเขาและพยายามหยอกล้อพวกเขาออกจากกัน ที่อาจทำให้เรากลับมาในกรณีที่ 1 ลูกค้าเพียงแค่ใช้บริการผ่านอินเตอร์เฟซมากกว่าหนึ่ง โปรดอย่าเริ่มส่งสัญญาณบริการ หากมีสิ่งใดที่จะหมายถึงการส่งผ่านบริการไปยังลูกค้ามากกว่าหนึ่งครั้ง ใช้งานได้ แต่มันทำให้ฉันถามว่าบริการไม่ใช่ลูกโคลนขนาดใหญ่ที่ต้องถูกทำลาย
หากลูกค้าจำนวนมากใช้ชุดย่อยที่แตกต่างกัน แต่คุณไม่เห็นบทบาทแม้จะอนุญาตให้ลูกค้าใช้มากกว่าหนึ่งชุดคุณก็ไม่มีอะไรจะดีไปกว่าการพิมพ์เป็ดเพื่อออกแบบอินเตอร์เฟสของคุณ วิธีการออกแบบอินเทอร์เฟซนี้ช่วยให้มั่นใจว่าลูกค้าไม่ได้สัมผัสแม้แต่ฟังก์ชั่นเดียวที่ไม่ได้ใช้ แต่ก็เกือบจะรับประกันได้ว่าการเพิ่มไคลเอนต์ใหม่จะเกี่ยวข้องกับการเพิ่มอินเทอร์เฟซใหม่เสมอ เกี่ยวกับมันอินเตอร์เฟสที่รวมส่วนต่อประสานบทบาทจะ เราแลกเปลี่ยนความเจ็บปวดเพียงอย่างเดียวกับอีกสิ่งหนึ่ง
หากลูกค้าจำนวนมากใช้เซ็ตย่อยที่แตกต่างกันทับซ้อนไคลเอนต์ใหม่คาดว่าจะเพิ่มซึ่งจะต้องมีเซ็ตย่อยที่ไม่สามารถคาดเดาได้และคุณไม่ต้องการแยกย่อยบริการแล้วพิจารณาโซลูชันที่ใช้งานได้มากกว่า เนื่องจากตัวเลือกสองตัวแรกไม่ได้ผลและคุณอยู่ในสถานที่ที่ไม่ดีซึ่งไม่มีสิ่งใดตามรูปแบบและมีการเปลี่ยนแปลงเพิ่มเติมเกิดขึ้นจากนั้นให้พิจารณาแต่ละฟังก์ชั่นเป็นอินเทอร์เฟซของตัวเอง การสิ้นสุดที่นี่ไม่ได้หมายความว่า ISP ล้มเหลว หากสิ่งใดล้มเหลวมันเป็นกระบวนทัศน์ที่มุ่งเน้นวัตถุ วิธีการเชื่อมต่อวิธีเดียวตาม ISP ในสุดขีด มันเป็นแป้นพิมพ์ที่ค่อนข้างยุติธรรม แต่คุณอาจพบว่ามันทำให้อินเทอร์เฟซสามารถใช้ซ้ำได้ ตรวจสอบอีกครั้งว่าไม่มี
ดังนั้นมันกลับกลายเป็นว่าพวกเขามีขนาดเล็กมากอย่างแน่นอน
ฉันใช้คำถามนี้เป็นความท้าทายในการใช้ ISP ในกรณีที่ร้ายแรงที่สุด แต่จำไว้ว่าควรหลีกเลี่ยงสุดขั้ว ในการออกแบบที่คำนึงถึงหลักการอื่น ๆ ของSOLIDปัญหาเหล่านี้มักจะไม่เกิดขึ้นหรือมีความสำคัญ
อีกคำถามที่ไม่ได้รับคำตอบคือ:
ใครเป็นเจ้าของอินเตอร์เฟสเหล่านี้
ครั้งแล้วครั้งเล่าที่ฉันเห็นอินเทอร์เฟซที่ออกแบบด้วยสิ่งที่ฉันเรียกว่า "ห้องสมุด" ความคิด เราทุกคนมีความผิดในการเข้ารหัสลิง - ดู - ลิง - ทำที่คุณเพิ่งทำอะไรเพราะนั่นคือวิธีที่คุณเห็นมันทำ เรามีความผิดในสิ่งเดียวกันกับส่วนต่อประสาน
เมื่อฉันดูอินเทอร์เฟซที่ออกแบบมาสำหรับชั้นเรียนในห้องสมุดที่ฉันเคยคิดว่า: โอ้พวกเหล่านี้เป็นข้อดี นี่จะเป็นวิธีที่ถูกต้องในการทำอินเทอร์เฟซ สิ่งที่ฉันไม่เข้าใจคือขอบเขตห้องสมุดมีความต้องการและปัญหาของตัวเอง สิ่งหนึ่งที่ห้องสมุดไม่ได้คำนึงถึงการออกแบบของลูกค้าอย่างสมบูรณ์ ไม่ใช่ทุกขอบเขตเท่ากัน และบางครั้งแม้แต่ขอบเขตเดียวกันก็มีวิธีการข้ามที่แตกต่างกัน
นี่คือสองวิธีง่ายๆในการดูการออกแบบอินเตอร์เฟส:
อินเตอร์เฟสที่เป็นเจ้าของบริการ บางคนออกแบบทุกอินเทอร์เฟซเพื่อแสดงทุกอย่างที่บริการสามารถทำได้ คุณยังสามารถค้นหาตัวเลือก refactoring ใน IDE ที่จะเขียนอินเทอร์เฟซสำหรับคุณโดยใช้คลาสที่คุณป้อน
ส่วนต่อประสานกับลูกค้า ISP ดูเหมือนจะโต้แย้งว่าสิ่งนี้ถูกต้องและเจ้าของบริการนั้นผิด คุณควรแยกทุกอินเทอร์เฟซตามความต้องการของลูกค้า เนื่องจากไคลเอ็นต์เป็นเจ้าของอินเตอร์เฟสจึงควรกำหนด
ดังนั้นใครถูก
พิจารณาปลั๊กอิน:
ใครเป็นเจ้าของอินเตอร์เฟสที่นี่ ลูกค้าหรือไม่ บริการหรือไม่
ปรากฎทั้งคู่
สีที่นี่คือเลเยอร์ เลเยอร์สีแดง (ขวา) ไม่ควรรู้อะไรเกี่ยวกับเลเยอร์สีเขียว (ซ้าย) สามารถเปลี่ยนหรือแทนที่เลเยอร์สีเขียวได้โดยไม่ต้องสัมผัสเลเยอร์สีแดง ด้วยวิธีนี้ชั้นสีเขียวใด ๆ สามารถเสียบเข้ากับชั้นสีแดง
ฉันชอบรู้ว่าสิ่งที่ควรรู้เกี่ยวกับอะไรและสิ่งที่ไม่ควรรู้ สำหรับฉัน "รู้อะไรเกี่ยวกับอะไร" เป็นคำถามทางสถาปัตยกรรมที่สำคัญที่สุดคำถามเดียว
มาทำความเข้าใจคำศัพท์กันดีกว่า:
[Client] --> [Interface] <|-- [Service]
----- Flow ----- of ----- control ---->
ลูกค้าคือสิ่งที่ใช้
บริการเป็นสิ่งที่ใช้
Interactor
เกิดขึ้นเป็นทั้ง
ISP แจ้งว่าเลิกใช้อินเทอร์เฟซสำหรับลูกค้า ไม่เป็นไรช่วยให้สมัครได้ที่นี่:
Presenter
(บริการ) ไม่ควรกำหนดให้Output Port <I>
ส่วนต่อประสาน อินเทอร์เฟซควรถูก จำกัด ให้แคบลงกับสิ่งที่ต้องการInteractor
(ที่นี่ทำหน้าที่เป็นลูกค้า) นั่นหมายถึงอินเทอร์เฟซที่รู้เกี่ยวกับInteractor
และการติดตาม ISP จะต้องเปลี่ยนแปลงด้วย และนี่เป็นเรื่องปกติ
Interactor
(ที่นี่ทำหน้าที่เป็นบริการ) ไม่ควรกำหนดให้Input Port <I>
อินเทอร์เฟซ อินเทอร์เฟซควรถูก จำกัด ให้แคบลงตามความต้องการController
(ไคลเอนต์) นั่นหมายถึงอินเทอร์เฟซที่รู้เกี่ยวกับController
และการติดตาม ISP จะต้องเปลี่ยนแปลงด้วย และนี่ก็ไม่เป็นไร
อันที่สองไม่ดีเพราะชั้นสีแดงไม่ควรรู้เกี่ยวกับชั้นสีเขียว ISP ผิดหรือเปล่า? ก็ค่อนข้างดี ไม่มีหลักการใดที่สมบูรณ์ นี่เป็นกรณีที่คนโง่ที่ชอบอินเทอร์เฟซเพื่อแสดงทุกอย่างที่บริการสามารถทำได้อย่างถูกต้อง
อย่างน้อยพวกเขาพูดถูกถ้าInteractor
ไม่ทำสิ่งใดนอกจากความต้องการใช้งานนี้ หากInteractor
สิ่งที่ทำเพื่อกรณีการใช้งานอื่น ๆ ไม่มีเหตุผลนี้Input Port <I>
จะต้องรู้เกี่ยวกับพวกเขา ไม่แน่ใจว่าทำไมInteractor
ไม่สามารถมุ่งเน้นไปที่กรณีการใช้งานเพียงกรณีเดียวนี่เป็นปัญหาที่ไม่เกิดขึ้น
แต่input port <I>
อินเทอร์เฟซไม่สามารถผูกมัดตัวเองกับController
ลูกค้าและมีสิ่งนี้เป็นปลั๊กอินที่แท้จริง นี่คือขอบเขต 'ไลบรารี' ร้านเขียนโปรแกรมที่แตกต่างไปจากเดิมอย่างสิ้นเชิงสามารถเขียนเลเยอร์สีเขียวได้หลายปีหลังจากที่เลเยอร์สีแดงถูกเผยแพร่
หากคุณกำลังข้ามขอบเขต 'ไลบรารี่' และคุณรู้สึกว่าจำเป็นต้องใช้ ISP แม้ว่าคุณจะไม่ได้เป็นเจ้าของอินเทอร์เฟซในอีกด้านหนึ่งคุณจะต้องหาวิธี จำกัด อินเทอร์เฟซให้แคบลงโดยไม่ต้องเปลี่ยน
วิธีหนึ่งในการดึงออกมานั้นคืออะแดปเตอร์ วางไว้ระหว่างไคลเอนต์ที่ชอบControler
และInput Port <I>
อินเทอร์เฟซ อะแดปเตอร์ยอมรับInteractor
ว่าเป็นInput Port <I>
และมอบสิทธิ์ให้กับมัน อย่างไรก็ตามมันเปิดเผยเฉพาะสิ่งที่ลูกค้าต้องการController
ความต้องการผ่านอินเตอร์เฟซบทบาทหรืออินเตอร์เฟซที่เป็นเจ้าของโดยเลเยอร์สีเขียว อะแดปเตอร์ไม่ทำตาม ISP มันเอง แต่อนุญาตให้คลาสที่ซับซ้อนกว่าต้องการController
เพลิดเพลินกับ ISP สิ่งนี้มีประโยชน์หากมีอะแดปเตอร์น้อยกว่าไคลเอนต์เช่นController
นั้นใช้พวกเขาและเมื่อคุณอยู่ในสถานการณ์ที่ผิดปกติที่คุณข้ามเขตแดนของไลบรารีและแม้ว่าจะมีการเผยแพร่ไลบรารีจะไม่หยุดเปลี่ยน มองไปที่คุณ Firefox ตอนนี้การเปลี่ยนแปลงเหล่านั้นจะทำลายอะแดปเตอร์ของคุณเท่านั้น
ดังนั้นสิ่งนี้หมายความว่าอย่างไร มันแปลว่าคุณไม่ได้ให้ข้อมูลเพียงพอที่จะบอกฉันว่าคุณควรทำอะไร ฉันไม่รู้ว่าถ้าไม่ติดตาม ISP จะทำให้คุณเกิดปัญหา ฉันไม่รู้ว่าการติดตามมันจะไม่ทำให้คุณมีปัญหามากขึ้นหรือไม่
ฉันรู้ว่าคุณกำลังมองหาหลักการที่เรียบง่าย ISP พยายามเป็นเช่นนั้น แต่มันก็ไม่ได้พูดมาก ฉันเชื่อในมัน ใช่โปรดอย่าบังคับให้ลูกค้าต้องพึ่งพาวิธีการที่พวกเขาไม่ได้ใช้โดยไม่มีเหตุผลที่ดี!
หากคุณมีเหตุผลที่ดีดังกล่าวเป็นสิ่งที่การออกแบบของคุณจะยอมรับปลั๊กอินแล้วจะตระหนักถึงปัญหาที่เกิดขึ้นไม่ได้ติดตามสาเหตุ ISP (มันยากที่จะเปลี่ยนแปลงโดยไม่ทำลายลูกค้า) และวิธีการที่จะลดพวกเขา (เก็บInteractor
หรืออย่างน้อยInput Port <I>
มุ่งเน้นไปที่หนึ่งที่มีเสถียรภาพ กรณีการใช้งาน)