ทำไมคลาสไม่ควรถูกออกแบบให้“ เปิด”?


44

เมื่ออ่านคำถาม Stack Overflow และรหัสอื่น ๆ ข้อสรุปทั่วไปเกี่ยวกับวิธีการออกแบบคลาสจะปิดลง ซึ่งหมายความว่าโดยเริ่มต้นใน Java และ C # ทุกอย่างเป็นส่วนตัวฟิลด์เป็นที่สิ้นสุด, วิธีการบางอย่างเป็นที่สิ้นสุดและบางครั้งการเรียนเป็นที่สิ้นสุดแม้กระทั่ง

แนวคิดเบื้องหลังนี้คือการซ่อนรายละเอียดการใช้งานซึ่งเป็นเหตุผลที่ดีมาก อย่างไรก็ตามด้วยการมีอยู่protectedในภาษา OOP ส่วนใหญ่และความแตกต่างนี้ไม่ทำงาน

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

  • อย่าขยายชั้นเรียนเพียงแก้ไขปัญหาที่นำไปสู่รหัสที่ซับซ้อนมากขึ้น
  • คัดลอกและวางทั้งคลาสการฆ่ารหัสนำมาใช้ใหม่
  • แยกโครงการ

นั่นไม่ใช่ตัวเลือกที่ดี เหตุใดจึงไม่protectedใช้ในโครงการที่เขียนเป็นภาษาที่รองรับ เหตุใดบางโครงการจึงห้ามไม่ให้สืบทอดจากคลาสของพวกเขาอย่างชัดเจน


1
ฉันเห็นด้วยฉันเคยมีปัญหากับองค์ประกอบของ Java Swing นี้มันแย่จริงๆ
Jonas


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

2
"ภาษา OOP ส่วนใหญ่?" ฉันรู้มากขึ้นว่าไม่สามารถปิดชั้นเรียนได้ คุณออกจากตัวเลือกที่สี่: เปลี่ยนภาษา
วินไคลน์

@ Jonas หนึ่งในปัญหาที่สำคัญของ Swing ก็คือมันทำให้เกิดการใช้งานมากเกินไป นั่นเป็นการฆ่าการพัฒนาจริงๆ
Tom Hawtin - tackline

คำตอบ:


55

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

ดังนั้นจึงไม่มากนักที่ชั้นเรียนไม่ควรเปิด แต่บางครั้งก็ไม่คุ้มกับความพยายามที่จะเปิด

แน่นอนว่าอาจเป็นไปได้ว่าผู้เขียนห้องสมุดขี้เกียจ ขึ้นอยู่กับว่าคุณกำลังพูดถึงห้องสมุดอะไร :-)


13
ใช่นี่คือเหตุผลที่คลาสถูกปิดผนึกโดยค่าเริ่มต้น เนื่องจากคุณไม่สามารถคาดการณ์ได้หลายวิธีที่ลูกค้าของคุณอาจขยายชั้นเรียนได้จึงปลอดภัยกว่าการผูกมัดกับการสนับสนุนวิธีที่อาจขยายจำนวนชั้นเรียนที่ไร้ขีด จำกัด
Robert Harvey


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

5
@TheLQ: นั่นเป็นสมมติฐานที่ค่อนข้างใหญ่
Robert Harvey

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

24

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

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

ในห้องสมุดที่ได้รับการออกแบบมาอย่างดีชั้นเรียนที่คุ้มค่าต่อการสืบทอดมีการป้องกันและสมาชิกสาธารณะในสถานที่ที่เหมาะสมและซ่อนทุกอย่างอื่น


นั่นทำให้รู้สึกบางอย่าง ยังคงมีปัญหาอยู่แม้ว่าเมื่อคุณต้องการสิ่งที่คล้ายกันมาก แต่มีการเปลี่ยนแปลง 1 หรือ 2 สิ่งในการนำไปใช้ (การทำงานภายในของชั้นเรียน) ฉันยังดิ้นรนที่จะเข้าใจว่าหากคุณเอาชนะชั้นเรียนคุณไม่ได้ขึ้นอยู่กับการดำเนินการของชั้นเรียนหรือไม่?
TheLQ

5
+1 เพื่อประโยชน์ของข้อต่อหลวม @TheLQ เป็นอินเทอร์เฟซที่คุณควรพึ่งพาอย่างมากไม่ใช่การใช้งาน
Karl Bielefeldt

19

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

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


4
นี่คือคำตอบที่แท้จริง คำตอบอื่น ๆ สัมผัสกับสิ่งสำคัญ แต่สิ่งที่เกี่ยวข้องจริง ๆ คือ: ทุกสิ่งที่ไม่ได้finalและ / หรือprivateถูกล็อคอยู่ในสถานที่ และไม่สามารถเปลี่ยนแปลงได้
Konrad Rudolph

1
@ Konrad: จนกว่านักพัฒนาจะตัดสินใจปรับปรุงทุกอย่างและไม่เข้ากันได้แบบย้อนหลัง
JAB

นั่นเป็นเรื่องจริง แต่มันก็คุ้มค่าที่จะทราบว่ามันเป็นข้อกำหนดที่อ่อนแอกว่าสำหรับpublicสมาชิก protectedสมาชิกจัดทำสัญญาระหว่างชั้นเรียนที่เปิดเผยพวกเขาและชั้นเรียนที่ได้รับทันที ในทางตรงกันข้ามpublicสมาชิกจะทำสัญญากับผู้บริโภคทุกคนในนามของชั้นเรียนที่มีอยู่ในปัจจุบันและอนาคตที่เป็นไปได้ทั้งหมด
supercat

14

ใน OO มีสองวิธีในการเพิ่มฟังก์ชันการทำงานให้กับรหัสที่มีอยู่

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

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

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


5
จุดที่ดีกับองค์ประกอบเทียบกับการสืบทอด
Zsolt Török

+1 แต่ฉันไม่เคยชอบตัวอย่าง "is a shape" สำหรับการสืบทอด รูปสี่เหลี่ยมผืนผ้าและวงกลมอาจเป็นสองสิ่งที่แตกต่างกันมาก ฉันชอบที่จะใช้มากขึ้นจตุรัสเป็นสี่เหลี่ยมที่ถูก จำกัด สามารถใช้รูปสี่เหลี่ยมผืนผ้าได้เช่นรูปร่าง
tylermac

@tylac: แน่นอน กล่องสี่เหลี่ยม / สี่เหลี่ยมผืนผ้าเป็นจริงหนึ่งในตัวอย่างของ LSP อาจเป็นไปได้ว่าฉันควรเริ่มคิดถึงตัวอย่างที่มีประสิทธิภาพมากขึ้น
เคาะ

3
สแควร์ / สี่เหลี่ยมผืนผ้าเป็นเพียงตัวอย่างตัวอย่างถ้าคุณอนุญาตให้มีการปรับเปลี่ยน
starblue

11

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

อย่างไรก็ตามฉันจะไม่พิจารณาเพียงแค่ปิดการออกแบบรหัสที่เหมาะสมทั้งหมด "O" ใน SOLID สำหรับ "Open-Closed Principle" โดยระบุว่าคลาสควร "ปิด" เพื่อปรับเปลี่ยน แต่ "open" เป็นส่วนขยาย แนวคิดคือคุณมีรหัสในวัตถุ มันใช้งานได้ดี การเพิ่มฟังก์ชั่นไม่ควรต้องเปิดรหัสนั้นและทำการเปลี่ยนแปลงการผ่าตัดซึ่งอาจทำให้พฤติกรรมการทำงานก่อนหน้านี้พัง แต่ควรจะใช้การสืบทอดหรือการฉีดพึ่งพาเพื่อ "ขยาย" ชั้นเรียนเพื่อทำสิ่งเพิ่มเติม

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

แต่คุณสามารถทำหนึ่งในสองสิ่งต่อไปนี้: คุณสามารถสืบทอดมาจาก ConsoleWriter เพื่อสร้าง ConsoleAndFileWriter และขยายเมธอด Write () เพื่อเรียกใช้งานฐานก่อนแล้วจึงเขียนไปยังไฟล์ หรือคุณสามารถแยกส่วนต่อประสาน IWriter จาก ConsoleWriter และปรับใช้ส่วนต่อประสานนั้นใหม่เพื่อสร้างสองคลาสใหม่ FileWriter และ "MultiWriter" ซึ่งสามารถรับ IWriters อื่น ๆ และจะ "ออกอากาศ" การโทรใด ๆ ที่ทำกับวิธีการของมันให้กับนักเขียนที่ได้รับทั้งหมด สิ่งที่คุณเลือกขึ้นอยู่กับสิ่งที่คุณต้องการ มาง่ายคือดีง่ายกว่า แต่ถ้าคุณมีความสุขุมเล็กน้อยในที่สุดจำเป็นต้องส่งข้อความไปยังไคลเอนต์เครือข่ายสามไฟล์สองชื่อไปป์และคอนโซลไปข้างหน้าและใช้ปัญหาในการแยกอินเทอร์เฟซและสร้าง "Y-อะแดปเตอร์"; มัน'

ตอนนี้ถ้าฟังก์ชัน Write () ไม่เคยถูกประกาศเสมือน (หรือถูกปิดผนึก) ตอนนี้คุณกำลังมีปัญหาหากคุณไม่ได้ควบคุมซอร์สโค้ด บางครั้งแม้ว่าคุณจะทำ โดยทั่วไปนี่เป็นตำแหน่งที่ไม่ดีที่จะอยู่ในและมันทำให้ผู้ใช้ API ปิด - แหล่งที่มาหรือ จำกัด ที่จะไม่สิ้นสุดเมื่อมันเกิดขึ้น แต่มีเหตุผลที่ถูกต้องว่าทำไมปิดผนึก Write () หรือ ConsoleWriter ทั้งคลาสนั้น อาจมีข้อมูลที่ละเอียดอ่อนในเขตข้อมูลที่มีการป้องกัน (แทนที่ในชั้นฐานเพื่อให้ข้อมูลดังกล่าว) อาจมีการตรวจสอบไม่เพียงพอ เมื่อคุณเขียน api คุณจะต้องสมมติว่าโปรแกรมเมอร์ผู้ใช้งานโค้ดของคุณนั้นไม่ฉลาดหรือมีเมตตามากกว่า "ผู้ใช้ปลายทาง" โดยเฉลี่ยของระบบสุดท้าย แบบที่คุณไม่เคยผิดหวัง


จุดดี. การรับมรดกอาจดีหรือไม่ดีดังนั้นจึงเป็นการผิดที่จะกล่าวว่าทุกชั้นควรได้รับการผนึก มันขึ้นอยู่กับปัญหาที่เกิดขึ้นจริง ๆ
Knulp

2

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

โดยทั่วไปหากคุณกำลังจะเขียนเป็นภาษา oo โครงการของคุณจำเป็นต้องทำตามกระบวนทัศน์ oo ซึ่งรวมถึงส่วนขยาย แน่นอนถ้ามีคนเขียนห้องสมุดในกระบวนทัศน์ที่แตกต่างกันบางทีคุณไม่ควรขยายมันต่อไป


7
BTW, OO และขั้นตอนการดำเนินงานส่วนใหญ่ไม่ได้เน้นเฉพาะที่กันและกัน คุณไม่สามารถมี OO ได้หากไม่มีขั้นตอน นอกจากนี้การจัดองค์ประกอบเป็นแนวคิด OO มากพอ ๆ
Michael K

@Michael ไม่แน่นอน ฉันกล่าวว่าคุณอาจต้องการระบบขั้นตอนซึ่งก็คือระบบที่ไม่มีแนวคิด OO ฉันเคยเห็นผู้คนใช้ Java เพื่อเขียนโค้ดโพรซีเดอร์อย่างหมดจดและมันไม่ทำงานถ้าคุณลองและโต้ตอบกับมันในแบบ OO
Spencer Rathbun

1
นั่นอาจแก้ไขได้ถ้า Java มีฟังก์ชันชั้นหนึ่ง Meh
Michael K

เป็นการยากที่จะสอนภาษา Java หรือ DOTNet ให้กับนักเรียนใหม่ ฉันมักจะสอนขั้นตอนกับปาสกาลหรือ "ธรรมดา C" และต่อมาเปลี่ยนเป็น OO ด้วย Object Pascal หรือ "C ++" และปล่อย Java ไว้ภายหลัง การสอนการเขียนโปรแกรมด้วยโปรแกรมโพรซีเดอร์ดูเหมือนว่าวัตถุ (ซิงเกิลตัน) ทำงานได้ดี
umlcat

@Michael K: ใน OOP ฟังก์ชั่นชั้นหนึ่งเป็นเพียงวัตถุที่มีวิธีการเดียว
Giorgio

2

เพราะพวกเขาไม่รู้อะไรเลย

ผู้เขียนดั้งเดิมอาจจะยึดมั่นกับความเข้าใจผิดในหลักการของ SOLID ซึ่งเกิดขึ้นในโลก C + + ที่สับสนและซับซ้อน

ฉันหวังว่าคุณจะสังเกตเห็นว่าทับทิมทับทิมและงูหลามในโลกไม่มีปัญหาที่คำตอบที่นี่อ้างว่าเป็นเหตุผลในการปิดผนึก โปรดทราบว่ามันตั้งฉากกับการพิมพ์แบบไดนามิก ตัวดัดแปลงการเข้าถึงนั้นใช้งานได้ง่ายในเกือบทุกภาษา (ทั้งหมด?) ฟิลด์ c ++ สามารถ mucked ด้วยโดยการแคสต์ชนิดอื่น (C ++ อ่อนแอกว่า) Java และ C # สามารถใช้การสะท้อนกลับได้ ตัวดัดแปลงการเข้าถึงทำให้สิ่งต่าง ๆ ยากพอที่จะป้องกันคุณไม่ให้ทำเว้นแต่คุณต้องการจริงๆ

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

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

  1. ความเย่อหยิ่ง - พวกเขาเชื่อว่าพวกเขาเปิดกว้างสำหรับการขยายและปิดเพื่อดัดแปลง
  2. ความพึงพอใจ - พวกเขารู้ว่าอาจมีกรณีการใช้งานอื่น แต่ตัดสินใจที่จะไม่เขียนสำหรับกรณีการใช้งานเหล่านั้น

ฉันคิดว่าในกรอบขององค์กรทุ่ง # 2 น่าจะเป็นกรณี เฟรมเวิร์ก C ++, Java และ. NET เหล่านั้นจะต้อง "เสร็จสิ้น" และพวกเขาจะต้องปฏิบัติตามแนวทางที่แน่นอน แนวทางเหล่านั้นมักจะหมายถึงประเภทที่ปิดผนึกเว้นแต่ว่าประเภทนั้นได้รับการออกแบบอย่างชัดเจนเป็นส่วนหนึ่งของลำดับชั้นของประเภทและสมาชิกส่วนตัวสำหรับสิ่งต่าง ๆ ซึ่งอาจเป็นประโยชน์สำหรับผู้อื่นใช้ .. แต่เนื่องจากพวกเขาไม่เกี่ยวข้องโดยตรงกับชั้นเรียน . การแตกไฟล์ประเภทใหม่นั้นแพงเกินกว่าจะรองรับเอกสาร ฯลฯ ...

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

ฉันชอบวิธีการจัดการชื่องูใหญ่ของงูใหญ่ คุณสามารถ (แทนได้ง่ายกว่าการสะท้อน) ได้อย่างง่ายดายแทนที่ Privates หากคุณต้องการ มีการเขียนที่ยอดเยี่ยมอยู่ที่นี่: http://bytebaker.com/2009/03/31/python-properties-vs-java-access-modifiers/

ตัวดัดแปลงส่วนตัวของ Ruby นั้นได้รับการปกป้องมากกว่าใน C # และไม่มีความเป็นส่วนตัวในฐานะตัวดัดแปลง C # การป้องกันแตกต่างกันเล็กน้อย มีคำอธิบายที่ดีที่นี่: http://www.ruby-lang.org/en/documentation/ruby-from-other-languages/

โปรดจำไว้ว่าภาษาแบบคงที่ของคุณไม่จำเป็นต้องสอดคล้องกับรูปแบบที่ล้าสมัยของการเขียนโค้ดในภาษานั้นในอดีต


5
"การห่อหุ้มไม่เคยประสบความสำเร็จอย่างแน่นอน" ฉันคิดว่าทุกคนจะเห็นด้วยว่าการขาดการห่อหุ้มนั้นทำให้เกิดปัญหามากมาย
Joh

5
-1 คนโง่เขลาในที่ที่ทูตสวรรค์กลัวที่จะเหยียบ การฉลองความสามารถในการเปลี่ยนตัวแปรส่วนตัวแสดงให้เห็นถึงข้อบกพร่องที่ร้ายแรงในสไตล์การเขียนโค้ดของคุณ
riwalk

2
นอกเหนือจากการเป็นที่ถกเถียงกันและอาจผิดโพสต์นี้ก็ค่อนข้างหยิ่งและดูถูก ทำได้ดีมาก
Konrad Rudolph

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

1

แก้ไข: ฉันเชื่อว่าคลาสควรได้รับการออกแบบให้เปิด ด้วยข้อ จำกัด บางอย่าง แต่ไม่ควรปิดเพื่อรับมรดก

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

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

บางครั้งฉันต้องจัดการกับคลาสที่ "ปิดผนึก" บางโอกาสและสิ้นสุดการสร้าง "wrapper" คลาสใหม่ต่อคลาสที่กำหนดซึ่งมีสมาชิกที่คล้ายกัน

class MyBaseWrapper {
  protected FinalClass _FinalObject;

  public virtual DoSomething()
  {
    // these method cannot be overriden,
    // its from a "final" class
    // the wrapper method can  be open and virtual:
    FinalObject.DoSomething();
  }
} // class MyBaseWrapper


class MyBaseWrapper: MyBaseWrapper {

  public override DoSomething()
  {
    // the wrapper method allows "overriding",
    // a method from a "final" class:
    DoSomethingNewBefore();
    FinalObject.DoSomething();
    DoSomethingNewAfter();
  }
} // class MyBaseWrapper

ขอบเขต clasifiers อนุญาตให้ "ประทับตรา" คุณสมบัติบางอย่างโดยไม่ จำกัด การสืบทอด

เมื่อฉันเปลี่ยนจาก Structured Progr ถึง OOP ฉันเริ่มใช้สมาชิกคลาสส่วนตัวแต่หลังจากหลายโครงการฉันสิ้นสุดการใช้การป้องกันและในที่สุดก็ส่งเสริมคุณสมบัติหรือวิธีการเหล่านั้นสู่สาธารณะถ้าจำเป็น

ป.ล. ฉันไม่ชอบคำว่า "สุดท้าย" เป็นคำหลักใน C # ฉันควรใช้ "ที่ปิดสนิท" เช่น Java หรือ "หมัน" หลังจากใช้คำเปรียบเทียบการสืบทอด นอกจากนี้ "final" เป็นคำที่ใช้ในหลายบริบท


-1: คุณระบุว่าคุณชอบการออกแบบแบบเปิด แต่นี่ไม่ได้ตอบคำถามซึ่งเป็นสาเหตุที่ไม่ควรออกแบบคลาสให้เปิด
Joh

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