รูปแบบความรับผิดชอบเดี่ยวควรมีรูปแบบเฉพาะสำหรับชั้นเรียนอย่างไร


14

ตัวอย่างเช่นสมมติว่าคุณมีโปรแกรมเกมคอนโซลซึ่งมีวิธีอินพุต / เอาต์พุตทุกชนิดเข้าและออกจากคอนโซล มันจะเป็นสมาร์ทเพื่อให้พวกเขาทั้งหมดในครั้งเดียวinputOutputชั้นเรียนหรือทำลายพวกเขาลงไปเรียนที่เฉพาะเจาะจงมากขึ้นเช่นstartMenuIO, inGameIO, playerIO, gameBoardIOฯลฯ ดังกล่าวว่าแต่ละชั้นจะมีประมาณ 1-5 วิธี?

และเมื่อทราบเหมือนกันถ้ามันจะดีกว่าที่จะทำลายพวกเขาลงมันจะเป็นสมาร์ทที่จะวางไว้ในIOnamespace จึงทำให้เรียกพวกเขาเล็ก ๆ น้อย ๆ มากขึ้นอย่างละเอียดเช่น: IO.inGameฯลฯ ?



ที่เกี่ยวข้อง: ฉันไม่เคยเห็นเหตุผลที่ดีในการรวมอินพุตและเอาต์พุต และเมื่อฉันจงใจแยกพวกเขาออกรหัสของฉันจะออกมาสะอาดกว่ามาก
Mooing Duck

1
คุณตั้งชื่อคลาสอะไรใน lowerCamelCase ในภาษาใด
253751

คำตอบ:


7

อัปเดต (ปะยาง)

เนื่องจากฉันได้เขียนคำตอบที่ค่อนข้างละเอียดนี่คือสิ่งที่ทำให้ทุกอย่างเดือดลงไป:

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

ฉันคิดว่าคุณกำลังดูสิ่งนี้ในทางที่ผิด คุณกำลังแยก IO ออกมาในฟังก์ชั่นของส่วนประกอบของแอปพลิเคชั่นในขณะที่ - สำหรับฉันแล้วมันสมเหตุสมผลกว่าที่จะมีคลาส IO แยกกันตามแหล่งที่มาและ"type"ของ IO

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

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

จากนั้นฉันจะฉีดวัตถุเหล่านี้ลงในวัตถุตัวจัดการที่จะแมป IO ดิบบนสิ่งที่ตรรกะการใช้งานของฉันสามารถทำงานกับ (เช่น: ผู้ใช้กด"w"ตัวจัดการแผนที่ที่ลงบนMOVE_FORWARD)

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

[ IO.Keyboard.InGame ] // generic, if SoC and SRP are strongly adhered to, changing this component should be fairly easy to do
   ||
   ==> [ Controls.Keyboard.InGameMapper ]

[ Game.Engine ] <- Controls.Keyboard.InGameMapper
                <- IO.Screen
                <- ... all sorts of stuff here
    InGameMapper.move() //returns MOVE_FORWARD or something
      ||
      ==> 1. Game.updateStuff();//do all the things you need to do to move the character in the given direction
          2. Game.Screen.SetState(GameState); //translate the game state (inverse handler)
          3. IO.Screen.draw();//generate actual output

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

ทุกคลาสมีหน้าที่เดียว: การจัดการคีย์บอร์ดอินพุตทำได้โดยคลาสที่ไม่รู้ / ดูแล / ต้องรู้ว่าการประมวลผลอินพุตนั้นหมายถึงอะไร สิ่งที่มันรู้ก็คือวิธีรับอินพุต (บัฟเฟอร์, ไม่บัฟเฟอร์, ... )

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

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

จากนั้นวัตถุเหล่านี้จะถ่ายทอดสถานะของพวกเขากลับมาและข้อมูลนี้จะถูกส่งผ่านไปGame.Screenซึ่งเป็นตัวจัดการ IO ที่ตรงกันข้าม มันแมปการเป็นตัวแทนภายในกับสิ่งที่IO.Screenส่วนประกอบสามารถใช้ในการสร้างผลลัพธ์ที่แท้จริง


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

@kuhaku: เป็นเนมสเปซ ส่วนสำคัญของสิ่งที่ฉันกำลังพูดคือถ้าคุณเลือกที่จะสร้างคลาสย่อยตามส่วนของแอพพลิเคชั่นที่คุณเข้าร่วม คุณจะจบลงด้วยการเรียนที่มีความรับผิดชอบ IO ในการทำงานของแอพลิเคชัน สำหรับฉันแล้วดูเหมือนว่าจะเป็นการละเมิด SRP สำหรับชื่อและคลาสเนมสเปซ: ฉันมักจะชอบเนมสเปซ 95% ของเวลา
Elias Van Ootegem

ฉันได้อัปเดตคำตอบของฉันแล้วเพื่อสรุปคำตอบของฉัน
Elias Van Ootegem

ใช่แล้วนั่นเป็นปัญหาอีกอย่างที่ฉันมี (เชื่อมต่อ IO กับตรรกะ) ดังนั้นคุณแนะนำให้แยกอินพุตออกจากเอาต์พุตจริงหรือ
shinzou

@kuhaku: นั่นขึ้นอยู่กับสิ่งที่คุณทำและความซับซ้อนของอินพุต / เอาต์พุตคืออะไร หากตัวจัดการ (แปลสถานะเกมกับอินพุต / เอาต์พุตดิบ) แตกต่างกันมากเกินไปคุณอาจต้องการแยกคลาสอินพุทและเอาท์พุทถ้าไม่ใช่คลาส IO เดียวก็ใช้ได้
Elias Van Ootegem

23

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

ยานพาหนะบนท้องถนนโดยทั่วไปจะมีสี่ล้อขับเคลื่อนโดยเครื่องยนต์สันดาปภายใน

จากนั้นคุณจะกำหนดสิ่งต่าง ๆ เช่น "ยานพาหนะ" "ถนน" "ล้อ" ฯลฯ แยกจากกัน คุณจะไม่พยายามพูด:

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

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


เช่นเดียวกับการกำหนดรถยนต์ด้วยคำเพียงไม่กี่คำแทนที่จะเป็นหลายคำจากนั้นการกำหนดชั้นเรียนขนาดเล็กโดยเฉพาะ
shinzou

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

6
ประโยคที่สองของคุณดูเหมือนว่า "ประณามคุณครูกล่าวว่าสิ่งนี้จะต้องมี 4 หน้า ... 'สร้างพลังจูงใจ' มันคือ !!"
corsiKa

2

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

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


0

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

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

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