ฉันจะให้วัตถุโต้ตอบและสื่อสารกันโดยไม่บังคับลำดับชั้นได้อย่างไร


9

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

พบกับBoxPongเกมง่ายๆที่ฉันสร้างความคุ้นเคยกับการพัฒนาเกมเชิงวัตถุ ลากกล่องเพื่อควบคุมลูกบอลและรวบรวมสิ่งที่เป็นสีเหลือง
การทำให้ BoxPong ช่วยฉันกำหนดคำถามพื้นฐาน: ฉันจะมีวัตถุที่มีปฏิสัมพันธ์ซึ่งกันและกันโดยไม่ต้อง "เป็น" กันได้อย่างไร กล่าวอีกนัยหนึ่งมีวิธีที่วัตถุจะไม่เป็นลำดับชั้น แต่แทนที่จะอยู่ร่วมกันหรือไม่ (ฉันจะเข้าไปดูรายละเอียดเพิ่มเติมด้านล่าง)

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

โดยเฉพาะอย่างยิ่งในเกมง่ายๆเช่น BoxPong เป็นที่ชัดเจนว่ามีหรือควรมีวัตถุจำนวนหนึ่งอยู่ร่วมกันในระดับเดียวกัน มีกล่องมีลูกบอลมีของสะสม ทั้งหมดที่ฉันสามารถแสดงในภาษาเชิงวัตถุแม้ว่า - หรือเพื่อให้ดูเหมือน - มีความสัมพันธ์HAS-A ที่เข้มงวด ทำผ่านตัวแปรสมาชิก ฉันไม่สามารถเริ่มballและปล่อยให้มันทำสิ่งนั้นฉันต้องการให้มันเป็นของวัตถุอื่นอย่างถาวร ฉันได้ตั้งค่าเพื่อให้วัตถุหลักของเกมมีกล่องและกล่องนั้นมีลูกบอลและมีตัวนับคะแนน แต่ละวัตถุยังมีupdate()วิธีการซึ่งจะคำนวณตำแหน่งทิศทาง ฯลฯ และฉันไปลักษณะที่คล้ายกันที่นั่นผมเรียกวิธีการอัพเดตเกมวัตถุหลักซึ่งเรียกวิธีการปรับปรุงของเด็กทุกคนของตนและพวกเขาในทางกลับกันเรียกวิธีการปรับปรุงทั้งหมดของพวกเขาเด็ก นี่เป็นวิธีเดียวที่ฉันสามารถเห็นการสร้างเกมเชิงวัตถุ แต่ฉันรู้สึกว่ามันไม่ใช่วิธีที่เหมาะ ท้ายที่สุดฉันจะไม่คิดว่าลูกบอลเป็นของกล่อง แต่อยู่ในระดับเดียวกันและมีปฏิสัมพันธ์กับมัน ฉันคิดว่าสามารถทำได้โดยการเปลี่ยนวัตถุเกมทั้งหมดเป็นตัวแปรสมาชิกของวัตถุเกมหลัก แต่ฉันไม่เห็นว่าการแก้ปัญหาอะไร ฉันหมายถึง ... ทิ้งความยุ่งเหยิงที่เห็นได้ชัดจะมีวิธีใดสำหรับลูกบอลและกล่องที่จะรู้จักซึ่งกันและกันนั่นคือการโต้ตอบ?

นอกจากนี้ยังมีปัญหาของวัตถุที่ต้องการส่งผ่านข้อมูลระหว่างกัน ฉันมีประสบการณ์ค่อนข้างน้อยในการเขียนโค้ดสำหรับ SNES ซึ่งคุณสามารถเข้าถึง RAM ทั้งหมดได้ตลอดเวลา สมมติว่าคุณกำลังสร้างศัตรูที่กำหนดเองสำหรับSuper Mario Worldและคุณต้องการให้ลบเหรียญทั้งหมดของ Mario จากนั้นเพียงเก็บศูนย์ไว้ที่ $ 0DBF โดยไม่มีปัญหา ไม่มีข้อ จำกัด ว่าศัตรูไม่สามารถเข้าถึงสถานะของผู้เล่นได้ ฉันคิดว่าฉันถูกนิสัยเสียด้วยอิสรภาพนี้เพราะด้วย C ++ และสิ่งที่ฉันมักพบว่าตัวเองสงสัยว่าจะทำให้สิ่งอื่น ๆ สามารถเข้าถึงคุณค่าได้อย่างไร (หรือทั่วโลก)
ใช้ตัวอย่างของ BoxPong จะเกิดอะไรขึ้นถ้าฉันต้องการให้ลูกบอลกระเด็นขอบของหน้าจอ widthและheightเป็นคุณสมบัติของGameชั้นเรียนballเพื่อให้สามารถเข้าถึงได้ ฉันสามารถส่งผ่านค่าเหล่านี้ได้ (ไม่ว่าจะผ่านทางคอนสตรัคเตอร์หรือวิธีการที่พวกเขาต้องการ) แต่นั่นก็แค่กรีดร้องการปฏิบัติที่ไม่ดีกับฉัน

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

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

ขอบคุณล่วงหน้าสำหรับคำตอบใด ๆ - และอีกครั้งหากมีส่วนที่ไม่สมเหตุสมผลกับคุณโปรดแจ้งให้เราทราบ


2
อุตสาหกรรมเกมส่วนใหญ่ได้เคลื่อนย้ายไปสู่สถาปัตยกรรมเอนทิตี - คอมโพเนนต์ - ระบบและการเปลี่ยนแปลง มันเป็นความคิดที่แตกต่างจากวิธีการแบบดั้งเดิมของ OO แต่มันใช้งานได้ดีและเข้าท่าเมื่อแนวคิดรวบยอดเข้ามาความสามัคคีใช้มัน ที่จริงแล้ว Unity ใช้แค่ส่วนของ Entity-Component แต่ขึ้นอยู่กับ ECS
Dunk

ปัญหาของการอนุญาตให้ผู้เรียนร่วมมือกันโดยปราศจากความรู้ซึ่งกันและกันได้รับการแก้ไขโดยรูปแบบการออกแบบผู้ไกล่เกลี่ย คุณเคยดูไหม
Fuhrmanator

คำตอบ:


13

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

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

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


2

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

No!

ฉันคิดว่าประเด็นหลักที่คุณมีคือคุณกำลังใช้ "การเขียนโปรแกรมเชิงวัตถุ" เพียงเล็กน้อยเกินไปอย่างแท้จริง ใน OOP วัตถุไม่ได้เป็นตัวแทนของ "สิ่ง" แต่เป็น "ความคิด" ซึ่งหมายความว่า "บอล", "เกม", "ฟิสิกส์", "คณิตศาสตร์", "คณิตศาสตร์", "วันที่" ฯลฯ ทั้งหมดเป็นวัตถุที่ถูกต้อง นอกจากนี้ยังไม่มีข้อกำหนดสำหรับวัตถุที่จะ "รู้" เกี่ยวกับอะไร ตัวอย่างเช่นDate.Now().getTommorrow()จะถามคอมพิวเตอร์ว่าวันนี้เป็นวันใดให้ใช้กฎวันที่ arcane เพื่อหาวันที่ในวันพรุ่งนี้และส่งคืนไปยังผู้โทร Dateวัตถุไม่ทราบว่าเกี่ยวกับอะไรอื่นก็เพียงต้องการที่จะขอข้อมูลตามที่ต้องการจากระบบ นอกจากนี้Math.SquareRoot(number)ไม่จำเป็นต้องรู้อะไรนอกจากตรรกะของวิธีการคำนวณสแควร์รูท

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

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

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

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

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


1

พบกับ BoxPong เกมง่ายๆที่ฉันสร้างความคุ้นเคยกับการพัฒนาเกมเชิงวัตถุ

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

ฉันมีประสบการณ์ค่อนข้างน้อยในการเขียนโค้ดสำหรับ SNES ซึ่งคุณสามารถเข้าถึง RAM ทั้งหมดได้ตลอดเวลา สมมติว่าคุณกำลังสร้างศัตรูที่กำหนดเองสำหรับ Super Mario World และคุณต้องการให้ลบเหรียญทั้งหมดของ Mario จากนั้นเพียงเก็บศูนย์ไว้ที่ $ 0DBF โดยไม่มีปัญหา

คุณดูเหมือนจะหายไปจุดของการเขียนโปรแกรมเชิงวัตถุ

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

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

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

class Mario
{
    public:
        int coins;
}

คุณจะเขียน

class Mario
{
    public:
        void LoseCoins();

    private:
        int coins;
}

ตอนนี้ถ้าคุณต้องการเปลี่ยนวิธีที่มาริโอเก็บเหรียญของเขาจาก int เป็น long หรือ double หรือเก็บไว้ในเครือข่ายหรือเก็บไว้ในฐานข้อมูลหรือเริ่มต้นกระบวนการที่ยาวนานอื่น ๆ คุณทำการเปลี่ยนแปลงในที่เดียว: คลาสมาริโอและรหัสอื่น ๆ ของคุณทำงานได้อย่างต่อเนื่อง

ดังนั้นเมื่อคุณถาม

ฉันจะมีวัตถุที่มีปฏิสัมพันธ์ซึ่งกันและกันโดยไม่ต้อง "เป็น" ของกันและกันได้อย่างไร

คุณกำลังถามจริงๆ:

ฉันจะมีรหัสที่ขึ้นอยู่กับแต่ละอื่น ๆ โดยตรงโดยไม่ต้องมี abstractions ใด ๆ ได้อย่างไร

ซึ่งไม่ใช่การเขียนโปรแกรมเชิงวัตถุ

ฉันขอแนะนำให้คุณเริ่มต้นด้วยการอ่านทุกอย่างที่นี่: http://objectmentor.com/omSolutions/oops_what.htmlแล้วค้นหา youtube ทุกอย่างโดย Robert Martin และดูทั้งหมด

คำตอบของฉันมาจากเขาและบางคำก็มาจากเขาโดยตรง


ขอบคุณสำหรับคำตอบ (และหน้าที่คุณลิงก์; ดูน่าสนใจ) จริง ๆ แล้วฉันรู้เกี่ยวกับสิ่งที่เป็นนามธรรมและสามารถนำมาใช้ใหม่ได้ แต่ฉันคิดว่าฉันไม่ได้ตอบคำถามนี้อย่างดี อย่างไรก็ตามจากรหัสตัวอย่างที่คุณให้ฉันสามารถแสดงจุดของฉันได้ดีขึ้นในขณะนี้! คุณกำลังบอกว่าวัตถุของศัตรูไม่ควรทำmario.coins = 0;แต่mario.loseCoins();สิ่งใดที่ดีและจริง - แต่ประเด็นของฉันคือศัตรูสามารถเข้าถึงmarioวัตถุได้อย่างไร marioการเป็นตัวแปรของสมาชิกenemyดูเหมือนจะไม่ถูกต้องสำหรับฉัน
vvye

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

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

@Tezra แต่แล้ววัตถุลำดับที่สูงกว่านี้ไม่สามารถใช้ซ้ำได้เลยเหรอ? รู้สึกเหมือนว่าวัตถุเหล่านี้ทำหน้าที่เป็นฟังก์ชันพวกมันมีอยู่เพื่อเป็นกระบวนการที่พวกมันแสดงออกเท่านั้น
Steve Chamaillard

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

0

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

มันตระหนักถึง "ต้นแบบของเกมโปรดปล่อยให้สิ่งนี้เกิดขึ้น" ความคิดแบบนั้น

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

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

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