ชี้แจงหลักการเปิด / ปิด


25

ตามที่ฉันได้อธิบายหลักการเปิด / ปิดระบุว่าเมื่อเขียนรหัสไม่ควรแก้ไข (นอกเหนือจากการแก้ไขข้อผิดพลาด) แต่หากกฎธุรกิจของฉันเปลี่ยนฉันไม่ควรแก้ไขรหัสที่ใช้การเปลี่ยนแปลงเหล่านั้นหรือ ฉันสงสัยว่าฉันไม่เข้าใจบางสิ่งเกี่ยวกับหลักการเพราะมันไม่สมเหตุสมผลสำหรับฉัน

คำตอบ:


22

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

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

รหัสเก่าที่ใช้ใบแจ้งหนี้เก่าไม่ได้ขาดหรือได้รับผลกระทบใด ๆ รหัสใหม่สามารถใช้ HTMLInvoice (ถ้าคุณใช้Liskov Substitutability , L of solid คุณสามารถมอบ HTMLInvoice instance ให้กับโค้ดที่มีอยู่ซึ่งคาดว่าจะอินสแตนซ์ Invoice) ทุกคนใช้ชีวิตอย่างมีความสุขตลอดไป

ใบแจ้งหนี้ถูกปิดเพื่อแก้ไขเปิดเพื่อขยาย และคุณต้องเขียนใบแจ้งหนี้อย่างถูกต้องล่วงหน้าเพื่อให้สิ่งนี้ทำงาน btw


1
หากการเปลี่ยนแปลงกฎธุรกิจไม่มีข้อสันนิษฐานว่าจะทำงานได้อย่างสมบูรณ์แบบโดยไม่มีข้อบกพร่องดังนั้นหลักการแบบเปิด / ปิดจึงไม่มีผล
JeffO

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

1
@Jeff OI แยกแยะความแตกต่างระหว่างการแก้ไขข้อบกพร่อง (ที่รหัสไม่เป็นไปตามข้อกำหนดเดิมและไม่มีใครต้องการมันเป็น) และการเปลี่ยนแปลงข้อกำหนด ถ้าฉันต้องการ PDF และโค้ดสร้าง PDF ก็ไม่มีข้อผิดพลาดถึงแม้ว่าตอนนี้ฉันต้องการ HTML (และโดยปกติแล้วคนต้องการ HTML เช่นกันไม่ใช่แทนที่จะเป็น)
Kate Gregory

2
@ Winston - นี่คือสิ่งที่ฉันหมายถึงเมื่อฉันบอกว่าคุณต้องเขียนใบแจ้งหนี้อย่างถูกต้อง เป็นการดีที่มีใบแจ้งหนี้ที่เป็นนามธรรมอยู่แล้วและคุณได้รับ PDFInvoice โดยคาดหวังสิ่งนี้ ถ้าไม่คุณต้องฝ่าฝืนกฎหนึ่งครั้งเพื่อตั้งตัวเองให้ไม่ทำลายมันในอนาคต ทั้งสองวิธีการทำนายการเปลี่ยนแปลงในอนาคตเป็นส่วนใหญ่ของทั้งหมดนี้ - และนั่นคือส่วน "จับและตัดช้าง" ของสูตร
Kate Gregory

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

13

คุณได้อ่านบทความThe Open หลักการที่ปิดโดยเพื่อน ๆ ของลุงบ๊อบที่ ObjectMentor หรือไม่? ฉันคิดว่านี่เป็นหนึ่งในคำอธิบายที่ดีกว่า

มีฮิวริสติกจำนวนมากที่เกี่ยวข้องกับการออกแบบเชิงวัตถุ ตัวอย่างเช่น "ตัวแปรสมาชิกทั้งหมดควรเป็นส่วนตัว" หรือ "ควรหลีกเลี่ยงตัวแปรส่วนกลาง" หรือ "การใช้การระบุประเภทเวลาทำงาน (RTTI) เป็นอันตราย" แหล่งที่มาของการวิเคราะห์พฤติกรรมเหล่านี้คืออะไร? อะไรทำให้พวกเขาเป็นจริง พวกเขาเป็นจริงเสมอ คอลัมน์นี้จะตรวจสอบหลักการออกแบบที่รองรับฮิวริสติกเหล่านี้ - หลักการแบบเปิด

ดังที่ Ivar Jacobson กล่าวว่า“ ทุกระบบเปลี่ยนแปลงในช่วงชีวิตของพวกเขา สิ่งนี้จะต้องเกิดขึ้นในใจเมื่อการพัฒนาระบบคาดว่าจะยาวนานกว่ารุ่นแรก” เราจะสร้างการออกแบบที่มีความเสถียรเมื่อเผชิญกับการเปลี่ยนแปลงได้อย่างไรและจะยาวนานกว่ารุ่นแรกอย่างไร เบอร์ทรานด์เมเยอร์ให้คำแนะนำกับเราเมื่อปี 1988 เมื่อเขาประกาศใช้หลักการแบบเปิดที่มีชื่อเสียงในขณะนี้ เพื่อถอดความเขา:

ซอฟแวร์ ENTITIES (คลาส, โมดูล, ฟังก์ชั่น, ฯลฯ ) ควรเปิดสำหรับการขยาย, แต่ปิดสำหรับการดัดแปลง

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

ลักษณะ

โมดูลที่สอดคล้องกับหลักการเปิดมีสองคุณสมบัติหลัก

  1. พวกเขาคือ“ เปิดเพื่อการขยาย”
    ซึ่งหมายความว่าพฤติกรรมของโมดูลสามารถขยายได้ เราสามารถทำให้โมดูลทำงานในรูปแบบใหม่และแตกต่างกันไปตามข้อกำหนดของการเปลี่ยนแปลงแอปพลิเคชันหรือเพื่อตอบสนองความต้องการของแอปพลิเคชันใหม่
  2. พวกเขาคือ“ ปิดเพื่อแก้ไข”
    ซอร์สโค้ดของโมดูลดังกล่าวไม่ถูกทำลาย ไม่มีใครได้รับอนุญาตให้ทำการเปลี่ยนแปลงซอร์สโค้ดมัน

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

สิ่งที่เป็นนามธรรมคือกุญแจสำคัญ ...


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

@Chris, cool - ฉันยังแนะนำหนังสือ "Clean code" โดย Uncle Bob ถ้าคุณชอบสิ่งนี้
Martijn Verburg

@Michael - เห็นด้วยอย่างสมบูรณ์มันเกือบเหมือนการ refactor รหัสเพื่อให้เหมาะในการทดสอบ
Martijn Verburg

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

1
ว้าว. รหัสขัดขืนไม่ได้? ฉันไม่เคยเป็นแฟนตัวยงของลุงบ๊อบ อาจารย์ใหญ่คนนี้เป็นคนหยาบคายและไม่จริงจังและมีความเชื่อมโยงกับความเป็นจริง
user949300

12

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

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


2
ใช่เพราะในทางปฏิบัติมันเป็นไปไม่ได้ที่จะสร้างคลาสที่สามารถขยายให้เหมาะกับอนาคตที่เป็นไปได้ทั้งหมดเว้นแต่ว่าคุณจะได้รับการป้องกันวิธีการทั้งหมด (ซึ่งดูดและละเมิดหลักการ YAGNI ซึ่งมีความสำคัญมากกว่า O / C Dito)
Martin Wickman

"ตาม OCP เรายังควรสร้างคลาสย่อยใหม่แม้ว่าจะสามารถเพิ่มฟิลด์ใหม่ในการใช้งานที่มีอยู่โดยการเปลี่ยนโค้ดไม่กี่บรรทัด": จริงเหรอ? ทำไมไม่เพิ่มเขตข้อมูลใหม่หรือวิธีการใหม่ จุดสำคัญคือคุณกำลังเพิ่ม (ขยาย) เท่านั้นและไม่เปลี่ยนแปลงสิ่งที่มีอยู่แล้ว
Giorgio

ฉันคิดว่าหลักการเหมาะสมเมื่อต้องจัดการกับไลบรารี / กรอบงานมาตรฐาน คุณไม่ต้องการที่จะเปิดและแก้ไขรหัสที่ได้รับการยอมรับ ไม่เช่นนั้นจะเกี่ยวกับการปรับโครงสร้างและทดสอบคงที่ทดสอบและทดสอบ
mastaBlasta

@Giorgio แน่นอนว่าการเพิ่มเขตข้อมูลหรือวิธีการใหม่เป็นสิ่งที่ฉันอยากจะแนะนำในกรณีส่วนใหญ่ แต่นั่นไม่ใช่ส่วนขยายมันคือ "การดัดแปลง"; จุดทั้งหมดของ OCP คือรหัสควร "ปิดเพื่อแก้ไข" (กล่าวคือไม่มีการเปลี่ยนแปลงไฟล์ต้นฉบับที่มีอยู่ก่อน) ในขณะที่ "เปิดเพื่อการขยาย" และการขยายใน OCP นั้นทำได้โดยการสืบทอดการใช้งาน
Rogério

@ Rogério: ทำไมคุณถึงกำหนดขอบเขตระหว่างส่วนขยายและการแก้ไขในระดับชั้น มีเหตุผลพิเศษสำหรับเรื่องนี้หรือไม่? ฉันอยากจะตั้งไว้ที่ระดับวิธี: การเปลี่ยนวิธีการเปลี่ยนพฤติกรรมของแอปพลิเคชันของคุณเพิ่มวิธี (สาธารณะ) ขยายส่วนต่อประสานของมัน
Giorgio

6

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

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

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

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

บอกว่าถ้าไม่มีการทดสอบใด ๆ แล้วหลักการนี้เป็นเสียง

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