การมีเพศสัมพันธ์ต่ำและการทำงานร่วมกันแน่น


11

แน่นอนมันขึ้นอยู่กับสถานการณ์ แต่เมื่อวัตถุคันล่างหรือระบบสื่อสารกับระบบระดับที่สูงขึ้นควรเรียกกลับหรือเหตุการณ์ที่จะรักษาตัวชี้ไปยังวัตถุระดับที่สูงขึ้น?

ตัวอย่างเช่นเรามีชั้นเรียนที่มีตัวแปรสมาชิกworld vector<monster> monstersเมื่อmonsterชั้นเรียนสื่อสารกับworldฉันควรจะใช้ฟังก์ชั่นโทรกลับแล้วหรือฉันควรจะมีตัวชี้ไปยังworldชั้นเรียนภายในmonsterชั้นเรียนหรือไม่?


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

คำตอบ:


10

มีสามวิธีหลักที่ชั้นเรียนหนึ่งสามารถพูดคุยกับอีกชั้นหนึ่งได้โดยไม่ต้องอยู่คู่กันอย่างแน่นหนา:

  1. ผ่านฟังก์ชั่นการโทรกลับ
  2. ผ่านระบบเหตุการณ์
  3. ผ่านทางอินเตอร์เฟส

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

ใน C ++ ฉันไม่ค่อยใช้การเรียกกลับ:

  1. C ++ ไม่มีการสนับสนุนที่ดีสำหรับการเรียกกลับที่เก็บรักษาthisตัวชี้ของพวกเขาดังนั้นจึงยากที่จะใช้การเรียกกลับในโค้ดเชิงวัตถุ

  2. การติดต่อกลับนั้นเป็นส่วนติดต่อแบบวิธีเดียวที่ไม่สามารถขยายได้ เมื่อเวลาผ่านไปฉันพบว่าฉันมักจะต้องการวิธีมากกว่าหนึ่งวิธีในการกำหนดอินเทอร์เฟซและการติดต่อกลับครั้งเดียวไม่ค่อยเพียงพอ

ในกรณีนี้ฉันอาจใช้ส่วนต่อประสาน ในคำถามของคุณคุณไม่จริงสะกดออกสิ่งที่จริงความต้องการในการสื่อสารกับmonster worldฉันจะทำสิ่งที่ชอบ:

class IWorld {
public:
  virtual Monster* getNearbyMonster(const Position & position) = 0;
  virtual Item*    getItemAt(const Position & position) = 0;
};

class Monster {
public:
  void update(IWorld * world) {
    // Do stuff...
  }
}

class World : public IWorld {
public:
  virtual Monster* getNearbyMonster(const Position & position) {
    // ...
  }

  virtual Item*    getItemAt(const Position & position) {
    // ...
  }

  // Lots of other stuff that Monster should not have access to...
}

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


1
+1 Delegates (callbacks) มักจะมีจำนวนมากขึ้นเมื่อเวลาผ่านไป การให้อินเทอร์เฟซกับสัตว์ประหลาดเพื่อให้พวกเขาได้สิ่งต่าง ๆ เป็นวิธีที่ดีในความคิดของฉัน
Michael Coleman

12

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

ฉันจะไม่เรียกมันว่าเป็นการปฏิบัติที่ดีที่สุด แต่เป็นรูปแบบทั่วไปที่มีเอนทิตีที่บางสิ่งบางอย่างมีตัวชี้ไปยังผู้ปกครอง

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


+1 การให้สัตว์ประหลาดมีวิธี จำกัด ในการโทรหาผู้ปกครองเป็นวิธีที่ดีในความคิดของฉัน
Michael Coleman

7

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

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

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


3

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

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

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


2

คุณจะไม่ไปไกลโดยไม่มีเหตุการณ์ชัดเจน แต่ก่อนที่จะเริ่มเขียน (และออกแบบ) ระบบเหตุการณ์คุณควรถามคำถามจริง: ทำไมสัตว์ประหลาดถึงสื่อสารกับระดับโลก ควรจริงเหรอ?

มาดูสถานการณ์ "คลาสสิค" กันเถอะสัตว์ประหลาดโจมตีผู้เล่น

สัตว์ประหลาดโจมตี: โลกสามารถระบุสถานการณ์ที่ฮีโร่อยู่ถัดจากสัตว์ประหลาดและบอกให้สัตว์ประหลาดโจมตี ดังนั้นฟังก์ชั่นในสัตว์ประหลาดจะเป็น:

void Monster::attack(LivingCreature l)
{
  // Call to combat system
}

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

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

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


1

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

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

อีกวิธีหนึ่งคือการโทรออกไปยังวัตถุลูกร่วมกัน นั่นคือถ้า A เรียกใช้เมธอดบน B และ B จำเป็นต้องสื่อสารข้อมูลบางอย่างไปยัง A, B เรียกเมธอดบน C โดยที่ A และ B สามารถเรียกทั้งสองได้ว่า C แต่ C ไม่สามารถเรียก A หรือ B. Object A ได้ รับผิดชอบในการรับข้อมูลจาก C หลังจาก B คืนค่าการควบคุมไปที่ A โปรดทราบว่านี่ไม่ได้แตกต่างอย่างแท้จริงจากวิธีแรกที่ฉันเสนอ วัตถุ A สามารถดึงข้อมูลจากค่าที่ส่งคืนได้เท่านั้น ไม่มีการเรียกใช้เมธอดของวัตถุ A โดย B หรือ C การเปลี่ยนแปลงของกลอุบายนี้คือการส่ง C เป็นพารามิเตอร์ไปยังเมธอด แต่ข้อ จำกัด เกี่ยวกับความสัมพันธ์ของ C กับ A และ B ยังคงมีผลบังคับใช้

ตอนนี้คำถามสำคัญคือทำไมฉันยืนยันที่จะทำสิ่งนี้ มีสามเหตุผลหลัก:

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

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

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


0

ส่วนตัว? ฉันแค่ใช้ซิงเกิล

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

คุณจะมีสองโลกที่ทำงานพร้อมกันหรือไม่? อาจจะ! บางทีคุณจะ แต่ถ้าคุณไม่สามารถคิดถึงสถานการณ์นั้นในตอนนี้คุณอาจจะไม่

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

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

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


2
ฉันอาจจะวลีมันสั้นกระชับ: "อย่ากลัวที่จะปรับโครงสร้าง"
Tetrad

ซิงเกิลนั้นชั่วร้าย! Globals ดีกว่ามาก คุณธรรมเดี่ยวทั้งหมดที่คุณระบุไว้นั้นเหมือนกันสำหรับทั่วโลก ฉันใช้พอยน์เตอร์ส่วนกลาง (ฟังก์ชั่นสากลที่ส่งคืนการอ้างอิง) ไปยังระบบย่อยต่างๆของฉันและเริ่มต้น / ทำลายพวกมันในฟังก์ชั่นหลักของฉัน ตัวชี้ Globals หลีกเลี่ยงปัญหาการเริ่มต้น, การห้อยเดี่ยวในระหว่างการรื้อ, การสร้างซิงเกิลที่ไม่น่าสนใจ, ฯลฯ ฉันทำซ้ำซิงเกิลตันเป็นความชั่วร้าย
deft_code

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