เหตุใดจึงเกิดการวน (จริง) ในตัวสร้างที่ไม่ดีจริง ๆ


47

แม้ว่าคำถามทั่วไปขอบเขตของฉันจะค่อนข้าง C # เพราะฉันรู้ว่าภาษาอย่าง C ++ มีความหมายที่แตกต่างกันเกี่ยวกับการเรียกใช้คอนสตรัคเตอร์การจัดการหน่วยความจำพฤติกรรมที่ไม่ได้กำหนด ฯลฯ

บางคนถามคำถามที่น่าสนใจซึ่งไม่ได้รับคำตอบสำหรับฉันง่ายๆ

ทำไม (หรือเลยหรือ?) ถือได้ว่าเป็นการออกแบบที่ไม่ดีเพื่อให้ตัวสร้างคลาสเริ่มการวนซ้ำไม่สิ้นสุด (เช่นลูปเกม)

มีแนวคิดบางอย่างที่แตกหักโดยสิ่งนี้:

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

แล้วมีปัญหาทางเทคนิค:

  • คอนสตรัคเตอร์ไม่เคยเสร็จสิ้นดังนั้นจะเกิดอะไรขึ้นกับ GC ที่นี่? วัตถุนี้มีอยู่แล้วใน Gen 0 หรือไม่
  • การได้มาจากคลาสนั้นเป็นไปไม่ได้หรืออย่างน้อยก็ซับซ้อนมากเนื่องจากข้อเท็จจริงที่ว่าคอนสตรัคเตอร์ไม่ส่งคืน

เห็นได้ชัดว่ามีบางสิ่งที่ไม่ดีหรือคดเคี้ยวมากกว่านี้หรือไม่?


61
ทำไมถึงดี หากคุณเพียงแค่ย้ายลูปหลักไปยังเมธอด (การรีแฟคเตอร์ง่าย ๆ ) จากนั้นผู้ใช้สามารถเขียนโค้ดที่ไม่แปลกใจเช่นนี้:var g = new Game {...}; g.MainLoop();
Brandin

61
คำถามของคุณระบุเหตุผล 6 ข้อที่จะไม่ใช้และฉันต้องการเห็นสิ่งที่คุณชื่นชอบ นอกจากนี้คุณยังสามารถขอเหตุผลที่มันเป็นความคิดที่ดีที่จะนำwhile(true)วงในตั้งค่าคุณสมบัติ: new Game().RunNow = true?
Groo

38
การเปรียบเทียบขั้นสูง: ทำไมเราถึงพูดว่าสิ่งที่ฮิตเลอร์ทำผิด ยกเว้นการเลือกปฏิบัติทางเชื้อชาติเริ่มต้นสงครามโลกครั้งที่สองฆ่าผู้คนนับล้านความรุนแรง [ฯลฯ ฯลฯ ด้วยเหตุผลอื่นมากกว่า 50 ข้อ] เขาไม่ได้ทำอะไรผิด แน่นอนว่าถ้าคุณลบรายการเหตุผลโดยพลการว่าทำไมบางสิ่งผิดปกติคุณอาจสรุปได้ว่าสิ่งนั้นดีสำหรับทุกสิ่งอย่างแท้จริง
Bakuriu

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

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

คำตอบ:


250

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

เออร์โกวง จำกัด อนันต์ทำลายสัญญาพื้นฐานของคอนสตรัคเตอร์


11
บางทีพวกเขาตั้งใจจะวางในขณะที่ (จริง) กับตัวแบ่งกลาง? เสี่ยง แต่ถูกกฎหมาย
John Dvorak

2
ฉันเห็นด้วยว่าสิ่งนี้ถูกต้อง - อย่างน้อย จากมุมมองทางเคมี ถือเหมือนกันสำหรับทุกฟังก์ชั่นที่ส่งกลับบางสิ่ง
Doc Brown

61
ลองนึกภาพ Sod ที่ไม่ดีที่สร้างคลาสย่อยของคลาสดังกล่าว ...
Newtopian

5
@JanDvorak: นั่นเป็นคำถามที่แตกต่าง OP ระบุอย่างชัดเจนว่า "ไม่สิ้นสุด" และกล่าวถึงเหตุการณ์วนรอบ
Jörg W Mittag

4
@ JörgWMittagใช่แล้วอย่าใส่มันไว้ในคอนสตรัคเตอร์
John Dvorak

45

เห็นได้ชัดว่ามีบางสิ่งที่ไม่ดีหรือคดเคี้ยวมากกว่านี้หรือไม่?

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

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

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

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

หากต้องการจบด้วยการเปรียบเทียบ (ภาษาการเขียนโปรแกรมอื่นนี่คือปัญหาในการคำนวณ2+2):

$sum_a = `bash -c "echo $((2+2)) 2>/dev/null"`;   # calculate
chomp $sum_a;                         # remove trailing \n
$sum_a = $sum_a + 0;                  # force it to be a number in case some non-digit characters managed to sneak in

$sum_b = 2+2;

เกิดอะไรขึ้นกับแนวทางแรก? มันจะคืนค่า 4 หลังจากระยะเวลาอันสั้นพอสมควร ถูกต้อง. การคัดค้าน (พร้อมด้วยเหตุผลทั่วไปที่นักพัฒนาอาจให้การหักล้าง) คือ:

  • ยากที่จะอ่าน (แต่เฮ้โปรแกรมเมอร์ที่ดีสามารถอ่านมันได้อย่างง่ายดายและเราทำมันแบบนี้เสมอถ้าคุณต้องการฉันสามารถ refactor เป็นวิธี!)
  • ช้าลง (แต่นี่ไม่ได้อยู่ในสถานที่สำคัญในการจับเวลาดังนั้นเราจึงสามารถเพิกเฉยได้และนอกจากนี้ยังเป็นการดีที่สุดที่จะเพิ่มประสิทธิภาพเมื่อจำเป็นเท่านั้น!)
  • ใช้ทรัพยากรมากขึ้น (กระบวนการใหม่, RAM เพิ่มเติมและอื่น ๆ - แต่ไม่เซิร์ฟเวอร์ของเราเร็วกว่าพอ)
  • แนะนำการพึ่งพาbash(แต่จะไม่ทำงานบน Windows, Mac หรือ Android เลย)
  • และเหตุผลอื่น ๆ ที่กล่าวมาข้างต้น

5
"GC อาจอยู่ในสถานะการล็อกพิเศษบางอย่างในขณะที่เรียกใช้ตัวสร้าง" - เพราะเหตุใด ตัวจัดสรรจัดสรรก่อนจากนั้นจึงเรียกใช้ตัวสร้างเป็นวิธีปกติ ไม่มีอะไรพิเศษเกี่ยวกับมันใช่ไหม? และตัวจัดสรรต้องอนุญาตการจัดสรรใหม่ (และสิ่งเหล่านี้อาจทำให้เกิดสถานการณ์ OOM) ภายในตัวสร้างต่อไป
John Dvorak

1
@JanDvorak เพียงแค่ต้องการทำให้จุดที่อาจมีพฤติกรรมพิเศษ "ที่ไหนสักแห่ง" ในขณะที่อยู่ในคอนสตรัคเตอร์ แต่คุณถูกต้องตัวอย่างที่ฉันเลือกคือไม่สมจริง ลบออก
AnoE

ฉันชอบคำอธิบายของคุณและชอบที่จะทำเครื่องหมายคำตอบทั้งคู่ว่าเป็นคำตอบ (อย่างน้อยต้องมี upvote) การเปรียบเทียบเป็นสิ่งที่ดีและเส้นทางความคิดและคำอธิบายของต้นทุนรองของคุณก็เช่นกัน แต่ฉันถือว่าพวกเขาเป็นข้อกำหนดเสริมสำหรับรหัส (เช่นเดียวกับข้อโต้แย้งของฉัน) สถานะปัจจุบันของศิลปะคือการทดสอบโค้ดดังนั้นความสามารถในการทดสอบ ฯลฯ จึงเป็นข้อกำหนดตามความเป็นจริง แต่คำสั่งของ@JörgWMittag fundamental contract of a constructor: to construct somethingเป็นจุดที่
ซามูเอล

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

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

8

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

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

ฉันไม่รู้ว่าซีแมนทิกส์เกี่ยวกับสถานการณ์แบบนี้มีการนิยามไว้อย่างดีในภาษา C # หรือไม่ แต่ฉันจะเถียงว่าไม่สำคัญเพราะไม่ใช่สิ่งที่นักพัฒนาส่วนใหญ่ต้องการลองเจาะลึกลงไป


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

@mroman: มันอาจโอเคที่จะรั่วไหลthisใน Constructor - แต่ถ้าคุณอยู่ใน Constructor ของคลาสที่ได้รับมามากที่สุด ถ้าคุณมีสองชั้นAและBมีB : Aถ้าคอนสตรัคของAจะรั่วthisสมาชิกของBจะยังคงเตรียม (และวิธีการโทรเสมือนหรือปลดเปลื้องจะBสามารถสนองความเสียหายขึ้นอยู่กับที่)
hoffmale

1
ฉันเดาใน C ++ ที่อาจบ้าใช่ ในภาษาอื่น ๆ สมาชิกจะได้รับค่าเริ่มต้นก่อนที่พวกเขาจะได้รับการกำหนดค่าที่แท้จริงของพวกเขาดังนั้นในขณะที่มันโง่ที่จะเขียนรหัสดังกล่าวพฤติกรรมที่ยังคงกำหนดไว้เป็นอย่างดี ถ้าคุณเรียกใช้วิธีการที่ใช้สมาชิกเหล่านี้คุณอาจพบกับ NullPointerExceptions หรือการคำนวณที่ผิด (เพราะตัวเลขเริ่มต้นที่ 0) หรือสิ่งต่าง ๆ เช่นนั้น แต่มันกำหนดทางเทคนิคว่าจะเกิดอะไรขึ้น
mroman

@mroman คุณสามารถค้นหาการอ้างอิงที่ชัดเจนสำหรับ CLR ที่จะแสดงให้เห็นว่าเป็นกรณีหรือไม่
JimmyJames

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

1

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

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

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

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

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

ใน C # อย่าใช้ตรรกะแบบขยายหรือไม่มีที่สิ้นสุดภายใน Constructor เนื่องจาก ... มีทางเลือกที่ดีกว่า


1
เขาดูเหมือนจะไม่เสนออะไรที่สำคัญกว่าจุดทำและอธิบายในก่อน 9 คำตอบ
ริ้น

1
เฮ้ฉันต้องยอมแพ้ :) ฉันคิดว่ามันเป็นเรื่องสำคัญที่จะต้องเน้นว่าในขณะที่ไม่มีกฎเกณฑ์ในการทำเช่นนี้คุณไม่ควรทำตามความจริงที่ว่ามีวิธีที่ง่ายกว่า คำตอบอื่น ๆ ส่วนใหญ่มุ่งเน้นไปที่ด้านเทคนิคว่าทำไมมันไม่ดี แต่มันก็ยากที่จะขายให้กับนักพัฒนาคนใหม่ที่พูดว่า "แต่มันก็คอมไพล์ดังนั้นฉันจะทิ้งมันไว้อย่างนั้นได้ไหม"
Chris Schaller

0

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

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

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

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

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


หากการสร้างวัตถุของคุณต้องการลูปเกมให้วางอย่างน้อยในวิธีการจากโรงงานแทน มากกว่าGameTestResults testResults = runGameTests(tests, configuration); GameTestResults testResults = new GameTestResults(tests, configuration);
253751

@immibis ใช่นั่นเป็นเรื่องจริงยกเว้นกรณีที่ไม่มีเหตุผล หากคุณกำลังทำงานกับระบบที่มีการสะท้อนแสงซึ่งทำให้วัตถุของคุณเป็นแบบอินสแตนซ์คุณอาจไม่มีตัวเลือกในการสร้างวิธีการจากโรงงาน
Cort Ammon

0

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

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

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

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


0

วัตถุของคุณทำให้ผมนึกถึงของการเริ่มต้นงาน วัตถุมีงานคอนสตรัค แต่มันก็ยังมีพวงของวิธีการโรงงานคงเหมือน: Task.Run, และTask.StartTask.Factory.StartNew

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

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

คุณสามารถจัดให้มีคอนสตรัคเตอร์ที่อนุญาตการควบคุมแบบละเอียด (ตามที่ @Brandin แนะนำบางอย่างเช่นvar g = new Game {...}; g.MainLoop();ที่รองรับผู้ใช้ที่ไม่ต้องการเริ่มเกมทันทีและอาจต้องการผ่านมันก่อนและคุณสามารถเขียนสิ่งที่ต้องการvar runningGame = Game.StartNew();เริ่มต้น มันง่ายทันที


นั่นหมายความว่าJörg รู้สึกประหลาดใจอย่างมากกับพื้นฐานซึ่งต้องบอกว่าเซอร์ไพรซ์เช่นนั้นเป็นความเจ็บปวดในพื้นฐาน
Steve Jessop

0

เหตุใดจึงเกิดการวน (จริง) ในตัวสร้างที่ไม่ดีจริง ๆ

นั่นไม่เลวเลย

ทำไม (หรือเลยหรือ?) ถือได้ว่าเป็นการออกแบบที่ไม่ดีเพื่อให้ตัวสร้างคลาสเริ่มการวนซ้ำไม่สิ้นสุด (เช่นลูปเกม)

ทำไมคุณต้องการทำเช่นนี้? อย่างจริงจัง. ชั้นนั้นมีฟังก์ชั่นเดียว: ตัวสร้าง หากสิ่งที่คุณต้องการคือฟังก์ชั่นเดียวนั่นคือสาเหตุที่เรามีฟังก์ชั่นไม่ใช่ตัวสร้าง

คุณพูดถึงเหตุผลบางอย่างที่ชัดเจนกับตัวเอง แต่คุณได้ทิ้งสิ่งที่ใหญ่ที่สุดไว้:

มีตัวเลือกที่ดีกว่าและง่ายกว่าเพื่อให้ได้สิ่งเดียวกัน

หากมี 2 ทางเลือกและทางเลือกที่ดีกว่าก็ไม่สำคัญว่าจะดีกว่าขนาดไหนคุณก็เลือกทางเลือกที่ดีกว่า อนึ่งนั่นคือเหตุผลที่เราไม่เคยใช้ GoTo เลย


-1

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

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


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