อะไรคือข้อได้เปรียบของ OOP ที่ใช้ต้นแบบมากกว่า OOP แบบอิงคลาส?


47

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

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

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

ขอขอบคุณ.


คำตอบ:


46

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

ซอร์สโค้ดที่นี่ถ้าคุณสนใจ ( Tyrant - Java Roguelike )

นี่คือประโยชน์หลัก:

  • มันไม่สำคัญที่จะสร้าง "คลาส" ใหม่ - เพียงแค่คัดลอกต้นแบบและเปลี่ยนคุณสมบัติและ voila ... คลาสใหม่ ฉันใช้สิ่งนี้เพื่อกำหนด potion ชนิดใหม่ตัวอย่างเช่นในแต่ละบรรทัดของ Java 3-6 บรรทัด ดีกว่าไฟล์คลาสใหม่และโหลดสำเร็จรูปมากมาย!
  • เป็นไปได้ที่จะสร้างและรักษา "คลาส" จำนวนมากด้วยโค้ดที่ค่อนข้างน้อยตัวอย่างเช่นทรราชมีบางอย่างเช่นต้นแบบ 3,000 แบบที่แตกต่างกันมีเพียง 42,000 บรรทัดของรหัสทั้งหมด มันค่อนข้างน่าทึ่งสำหรับ Java!
  • การรับมรดกหลายรายการเป็นเรื่องง่าย - คุณเพียงแค่คัดลอกชุดย่อยของคุณสมบัติจากต้นแบบหนึ่งและวางเหนือคุณสมบัติในต้นแบบอื่น ตัวอย่างเช่นในเกม RPG คุณอาจต้องการ "อาวุธเหล็ก" เพื่อให้มีคุณสมบัติบางอย่างของ "วัตถุเหล็ก" และคุณสมบัติบางอย่างของ "อาวุธ" และคุณสมบัติบางอย่างของ "สัตว์ประหลาดที่ไม่ฉลาด" ง่ายกับต้นแบบลองทำสิ่งนั้นกับทายาทมรดก ......
  • คุณสามารถทำสิ่งที่ฉลาดด้วยตัวดัดแปลงคุณสมบัติ - โดยการใส่ตรรกะที่ฉลาดในวิธี "คุณสมบัติทั่วไป" คุณสามารถใช้ตัวดัดแปลงต่าง ๆ ตัวอย่างเช่นมันง่ายในการกำหนดแหวนเวทมนต์ที่เพิ่มความแรง +2 ให้กับผู้ที่สวมมัน ตรรกะสำหรับสิ่งนี้อยู่ในวัตถุวงแหวนไม่ใช่ในวิธี "read strength" ดังนั้นคุณหลีกเลี่ยงการทำการทดสอบตามเงื่อนไขจำนวนมากที่อื่นในฐานรหัสของคุณ (เช่น "ตัวละครสวมแหวนเพิ่มความแข็งแรงหรือไม่?")
  • อินสแตนซ์สามารถกลายเป็นแม่แบบสำหรับอินสแตนซ์อื่น ๆ ได้เช่นถ้าคุณต้องการ "ลอกแบบ" วัตถุที่เป็นเรื่องง่ายเพียงแค่ใช้วัตถุที่มีอยู่เป็นต้นแบบสำหรับวัตถุใหม่ ไม่จำเป็นต้องเขียนตรรกะการโคลนที่ซับซ้อนจำนวนมากสำหรับคลาสที่แตกต่างกัน
  • มันค่อนข้างง่ายที่จะเปลี่ยนพฤติกรรมที่รันไทม์ - เช่นคุณสามารถเปลี่ยนคุณสมบัติและ "morph" วัตถุได้โดยพลการในรันไทม์ ช่วยให้เอฟเฟ็กต์ในเกมเจ๋ง ๆ และถ้าคุณจับคู่สิ่งนี้กับ "ภาษาสคริปต์" แล้วทุกอย่างก็สวยมากตอนรันไทม์
  • มันเหมาะกับการเขียนโปรแกรมแบบ "ใช้งานได้" - คุณมักจะพบว่าตัวเองเขียนฟังก์ชั่นมากมายที่วิเคราะห์การกระทำของวัตถุอย่างเหมาะสมแทนที่จะใช้ตรรกะที่ฝังอยู่ในวิธีการที่แนบมากับคลาสที่เฉพาะเจาะจง ฉันชอบสไตล์ FP นี้โดยส่วนตัว

นี่คือข้อเสียเปรียบหลัก:

  • คุณสูญเสียความมั่นใจในการพิมพ์คงที่ - เนื่องจากคุณกำลังสร้างระบบวัตถุแบบไดนามิกอย่างมีประสิทธิภาพ นี่หมายความว่าคุณต้องเขียนการทดสอบเพิ่มเติมเพื่อให้แน่ใจว่าพฤติกรรมนั้นถูกต้องและวัตถุนั้นมี "ชนิด" ที่ถูกต้อง
  • มีค่าใช้จ่ายประสิทธิภาพบางส่วนเนื่องจากการอ่านคุณสมบัติของวัตถุโดยทั่วไปจะถูกบังคับให้ผ่านการค้นหาแผนที่หนึ่งรายการขึ้นไปคุณต้องจ่ายค่าใช้จ่ายเล็กน้อยในแง่ของประสิทธิภาพ ในกรณีของฉันมันไม่ใช่ปัญหา แต่อาจเป็นปัญหาในบางกรณี (เช่น FPS 3D ที่มีวัตถุจำนวนมากถูกสอบถามในทุกเฟรม)
  • Refactorings ไม่ทำงานเหมือนกัน - ในระบบที่ใช้ต้นแบบคุณจะต้อง "สร้าง" การสืบทอดมรดกของคุณด้วยรหัส เครื่องมือ IDEs / refactoring ไม่สามารถช่วยคุณได้จริงๆเพราะพวกเขาไม่สามารถหาแนวทางของคุณได้ ฉันไม่เคยพบปัญหานี้ แต่อาจหลุดพ้นจากมือหากคุณไม่ระวัง คุณอาจต้องการทดสอบเพื่อตรวจสอบว่าลำดับชั้นการสืบทอดของคุณกำลังสร้างอย่างถูกต้อง!
  • มันค่อนข้างต่างดาวคนที่คุ้นเคยกับสไตล์ OOP แบบดั้งเดิมอาจสับสนได้ง่าย "คุณหมายความว่ามีเพียงคลาสเดียวที่เรียกว่า" สิ่ง "หรือไม่?" - "ฉันจะขยายชั้นเรียนสุดท้ายนี้ได้อย่างไร!?!" - "คุณละเมิดหลักการ OOP !!!" - "ผิดที่มีฟังก์ชั่นคงที่ทั้งหมดที่ทำหน้าที่กับวัตถุชนิดใด!?!?"

ในที่สุดก็มีบันทึกการใช้งาน:

  • ฉันใช้ Java HashMap สำหรับคุณสมบัติและตัวชี้ "พาเรนต์" สำหรับต้นแบบ สิ่งนี้ทำงานได้ดี แต่มีข้อเสียต่อไปนี้: a) คุณสมบัติการอ่านบางครั้งต้องติดตามกลับผ่านสายโซ่ยาวประสิทธิภาพการทำงานข) ถ้าคุณกลายพันธุ์ต้นแบบผู้ปกครองการเปลี่ยนแปลงจะส่งผลกระทบต่อเด็กทุกคนที่ไม่ได้ทำลายคุณสมบัติการเปลี่ยนแปลง สิ่งนี้อาจทำให้เกิดข้อบกพร่องเล็กน้อยหากคุณไม่ระวัง!
  • ถ้าผมทำเช่นนี้อีกครั้งผมจะใช้แผนที่ไม่เปลี่ยนรูปถาวรสำหรับคุณสมบัติ (ชนิดเช่นแผนที่ถาวร Clojure ของ ) หรือตัวเอง Java ถาวรกัญชาดำเนินงานแผนที่ของฉัน จากนั้นคุณจะได้รับประโยชน์จากการคัดลอก / เปลี่ยนแปลงราคาถูกควบคู่ไปกับพฤติกรรมที่ไม่เปลี่ยนรูปแบบและคุณไม่จำเป็นต้องเชื่อมโยงวัตถุกับผู้ปกครองอย่างถาวร
  • คุณสามารถสนุกสนานถ้าคุณฝังฟังก์ชัน / วิธีลงในคุณสมบัติของวัตถุ แฮ็คที่ฉันใช้ใน Java สำหรับสิ่งนี้ (ประเภทย่อยที่ไม่ระบุชื่อของคลาส "สคริปต์") ไม่ได้สวยงามมาก - ถ้าทำเช่นนี้อีกครั้งฉันอาจใช้ภาษาที่ฝังตัวได้ง่ายสำหรับสคริปต์ (Clojure หรือ Groovy)


(+1) การวิเคราะห์ที่ดี Altought ซึ่งมีพื้นฐานมากกว่าในโมเดล Java เช่น Delphi, C #, VB.Net มีคุณสมบัติที่ชัดเจน
umlcat

3
@umlcat - ฉันรู้สึกว่าโมเดล Java นั้นค่อนข้างเหมือนกับรุ่น Delphi / C # (นอกเหนือจากน้ำตาล syntactic ที่ดีสำหรับการเข้าถึงคุณสมบัติ) - คุณยังคงต้องประกาศคุณสมบัติที่คุณต้องการในนิยามคลาสของคุณแบบคงที่ จุดประสงค์ของแบบจำลองต้นแบบคือคำจำกัดความนี้ไม่คงที่และคุณไม่จำเป็นต้องทำการประกาศล่วงหน้า ....
mikera

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

เกี่ยวกับการสืบทอดหลาย ๆ ครั้งนั้นง่ายกว่าคุณเปรียบเทียบกับ Java ที่ไม่รองรับการสืบทอดหลายแบบ แต่จะเปรียบเทียบได้ง่ายกว่ากับภาษาที่สนับสนุนเช่น C ++ หรือไม่
Piovezan

2

ข้อได้เปรียบหลักของ OOP แบบตัวอย่างคือมันสามารถขยายอ็อบเจกต์ & "คลาส" ที่รันไทม์ได้

ใน OOP แบบคลาสมีคุณสมบัติที่ดีหลายอย่าง แต่ขึ้นอยู่กับภาษาการเขียนโปรแกรม

Object Pascal (Delphi), VB.Net & C # มีวิธีการโดยตรงในการใช้คุณสมบัติ (เพื่อไม่ให้สับสนกับฟิลด์) และวิธีการเข้าถึงคุณสมบัติของคุณสมบัติในขณะที่ Java & C ++, คุณสมบัติเข้าถึงได้โดยวิธีการ และ PHP มีทั้งสองอย่างผสมกันเรียกว่า "วิธีการทางเวทมนตร์"

มีคลาสการพิมพ์แบบไดนามิกบางส่วนแม้ว่าภาษา OO คลาสหลักจะมีการพิมพ์แบบคงที่ ฉันคิดว่าการพิมพ์แบบสแตติกกับ Class OO นั้นมีประโยชน์มากเพราะช่วยให้คุณสมบัติที่เรียกว่า Object Introspection ช่วยให้สามารถสร้าง IDE เหล่านั้นและพัฒนาเว็บเพจสายตาและรวดเร็ว


0

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


1
คุณอาจต้องการที่จะอ่านMonoliths "Unstrung" , std::stringแล้วมีจำนวนสมาชิกมากเกินไปที่ควรจะเป็นอัลกอริทึมอิสระหรือที่ไม่ใช่เพื่อนไม่ใช่สมาชิกไม่น้อยกว่า และอย่างไรก็ตามสมาชิกสามารถเพิ่มฟังก์ชั่นใหม่โดยไม่ต้องเปลี่ยนเลย์เอาต์ในหน่วยความจำใด ๆ หากคุณสามารถแก้ไขคลาสเดิมได้
Deduplicator
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.