ฉันกำลังอ่านและได้ยินว่าผู้คน (บนเว็บไซต์นี้) ชื่นชมกระบวนทัศน์การเขียนโปรแกรมการทำงานเป็นประจำโดยเน้นว่าการมีทุกสิ่งที่ไม่เปลี่ยนรูปนั้นดีเพียงใด ผู้คนเสนอวิธีการนี้แม้ในภาษา OO ที่จำเป็นแบบดั้งเดิมเช่น C #, Java หรือ C ++ ไม่เพียง แต่ในภาษาที่ใช้งานได้จริงเท่านั้นเช่น Haskell ที่บังคับใช้โปรแกรมนี้
ฉันพบว่ามันยากที่จะเข้าใจเพราะฉันพบว่าความไม่แน่นอนและผลข้างเคียง ... สะดวก อย่างไรก็ตามเมื่อพิจารณาว่าผู้คนในขณะนี้ประณามผลข้างเคียงและพิจารณาว่าเป็นวิธีปฏิบัติที่ดีในการกำจัดพวกเขาทุกที่ที่เป็นไปได้ฉันเชื่อว่าถ้าฉันต้องการเป็นโปรแกรมเมอร์ที่มีความสามารถฉันจะต้องเริ่มต้น ดังนั้นคำถามของฉัน
ที่เดียวเมื่อฉันพบปัญหากับกระบวนทัศน์การทำงานคือเมื่อวัตถุถูกอ้างอิงโดยธรรมชาติจากหลาย ๆ ที่ ผมขออธิบายสองตัวอย่าง
ตัวอย่างแรกจะเป็นของฉัน# เกม C ฉันพยายามที่จะทำในเวลาว่างของฉัน มันเป็นเกมบนเว็บแบบเปิดที่ผู้เล่นทั้งสองมีทีมจาก 4 มอนสเตอร์และสามารถส่งมอนสเตอร์จากทีมของพวกเขาไปยังสนามรบซึ่งมันจะเผชิญหน้ากับสัตว์ประหลาดที่ถูกส่งโดยผู้เล่นฝ่ายตรงข้าม ผู้เล่นสามารถจำสัตว์ประหลาดจากสนามรบและแทนที่พวกมันด้วยสัตว์ประหลาดอีกตัวจากทีมของพวกเขา (คล้ายกับโปเกมอน)
ในการตั้งค่านี้มอนสเตอร์ตัวเดียวสามารถอ้างอิงได้อย่างเป็นธรรมชาติจากสถานที่อย่างน้อย 2 แห่ง: ทีมของผู้เล่นและสนามรบซึ่งอ้างอิงมอนสเตอร์ที่ "คล่องแคล่ว" สองตัว
ทีนี้ลองพิจารณาสถานการณ์ตอนที่มอนสเตอร์ตัวใดตัวหนึ่งถูกโจมตีและเสียพลังชีวิต 20 แต้ม ภายในวงเล็บของกระบวนทัศน์ที่จำเป็นฉันปรับเปลี่ยนhealth
ฟิลด์ของมอนสเตอร์นี้เพื่อสะท้อนการเปลี่ยนแปลงนี้ - และนี่คือสิ่งที่ฉันกำลังทำอยู่ตอนนี้ อย่างไรก็ตามสิ่งนี้ทำให้Monster
คลาสไม่แน่นอนและฟังก์ชั่นที่เกี่ยวข้อง (เมธอด) ไม่บริสุทธิ์ซึ่งฉันคิดว่าเป็นวิธีปฏิบัติที่ไม่ดี ณ ตอนนี้
แม้ว่าฉันจะอนุญาตให้ตัวเองมีรหัสของเกมนี้ในสภาพที่น้อยกว่าอุดมคติเพื่อที่จะมีความหวังที่จะทำมันให้สำเร็จในบางจุดของอนาคตฉันอยากจะรู้และเข้าใจว่ามันควรจะเป็นอย่างไร เขียนอย่างถูกต้อง ดังนั้น: หากนี่คือข้อบกพร่องในการออกแบบจะแก้ไขได้อย่างไร
ในรูปแบบการทำงานตามที่ฉันเข้าใจฉันจะทำสำเนาของMonster
วัตถุนี้แทนทำให้เหมือนกันกับของเก่ายกเว้นสำหรับฟิลด์นี้ และวิธีการนี้suffer_hit
จะคืนค่าออบเจกต์ใหม่นี้แทนการแก้ไขอันเดิม จากนั้นฉันก็คัดลอกBattlefield
วัตถุเก็บฟิลด์ทั้งหมดไว้เหมือนกันยกเว้นสัตว์ประหลาดตัวนี้
สิ่งนี้มาพร้อมกับปัญหาอย่างน้อย 2 ประการ:
- ลำดับชั้นสามารถจะลึกกว่าตัวอย่างนี้ง่ายเพียง->
Battlefield
Monster
ฉันต้องทำการคัดลอกฟิลด์ทั้งหมดยกเว้นหนึ่งและส่งคืนวัตถุใหม่จนลำดับชั้นนี้ นี่จะเป็นรหัสสำเร็จรูปซึ่งฉันพบว่าน่ารำคาญโดยเฉพาะอย่างยิ่งเนื่องจากการเขียนโปรแกรมการทำงานควรจะลดสำเร็จรูป - อย่างไรก็ตามปัญหาที่รุนแรงยิ่งกว่านั้นคือสิ่งนี้จะนำไปสู่ข้อมูลที่ไม่ซิงค์กัน สัตว์ประหลาดที่อยู่ในสนามจะเห็นสุขภาพลดลง แม้กระนั้นสัตว์ประหลาดตัวนี้ซึ่งอ้างอิงจากผู้เล่นที่ควบคุมมัน
Team
จะไม่ ถ้าฉันใช้รูปแบบที่จำเป็นแทนการเปลี่ยนแปลงข้อมูลทุกอย่างจะสามารถมองเห็นได้ทันทีจากที่อื่น ๆ ของรหัสและในกรณีเช่นนี้ฉันคิดว่ามันสะดวกจริง ๆ - แต่วิธีที่ฉันได้สิ่งนี้เป็นสิ่งที่ผู้คนพูดอย่างแม่นยำผิดกับสไตล์ที่จำเป็น!- ตอนนี้มันเป็นไปได้ที่จะจัดการกับปัญหานี้โดยการเดินทางไป
Team
หลังจากการโจมตีแต่ละครั้ง นี่คืองานพิเศษ อย่างไรก็ตามถ้าหากสัตว์ประหลาดสามารถอ้างอิงได้ในภายหลังจากที่อื่น ๆ ? จะเป็นอย่างไรถ้าฉันมาพร้อมความสามารถที่ทำให้สัตว์ประหลาดมุ่งเน้นไปที่สัตว์ประหลาดตัวอื่นที่ไม่จำเป็นต้องอยู่บนสนาม (ฉันกำลังพิจารณาความสามารถดังกล่าว) ฉันจะจำได้หรือไม่ว่าจะต้องเดินทางไปยังสัตว์ประหลาดที่มุ่งเน้นทันทีหลังจากการโจมตีแต่ละครั้ง? ดูเหมือนว่าจะเป็นระเบิดเวลาที่จะระเบิดเมื่อรหัสซับซ้อนขึ้นดังนั้นฉันคิดว่ามันไม่มีทางออก
- ตอนนี้มันเป็นไปได้ที่จะจัดการกับปัญหานี้โดยการเดินทางไป
แนวคิดสำหรับการแก้ปัญหาที่ดีกว่ามาจากตัวอย่างที่สองของฉันเมื่อฉันประสบปัญหาเดียวกัน ในสถาบันการศึกษาเราได้รับคำสั่งให้เขียนล่ามภาษาที่เราออกแบบเองที่ Haskell (นี่คือวิธีที่ฉันถูกบังคับให้เริ่มเข้าใจว่า FP คืออะไร) ปัญหาปรากฏขึ้นเมื่อฉันใช้งานการปิด ขอบเขตเดียวกันนี้สามารถอ้างอิงได้จากหลาย ๆ ที่อีกครั้ง: ผ่านตัวแปรที่เก็บขอบเขตนี้และเป็นขอบเขตหลักของขอบเขตที่ซ้อนกัน! เห็นได้ชัดว่าหากมีการเปลี่ยนแปลงในขอบเขตนี้ผ่านการอ้างอิงใด ๆ ที่ชี้ไปที่การเปลี่ยนแปลงนี้จะต้องสามารถมองเห็นได้ผ่านการอ้างอิงอื่นทั้งหมด
วิธีแก้ปัญหาที่ฉันมาด้วยคือการกำหนด ID แต่ละขอบเขตและถือพจนานุกรมกลางของขอบเขตทั้งหมดในState
monad ตอนนี้ตัวแปรจะเก็บ ID ของขอบเขตที่ถูกผูกไว้เท่านั้นแทนที่จะเป็นขอบเขตเองและขอบเขตที่ซ้อนกันจะเก็บ ID ของขอบเขตหลักไว้ด้วย
ฉันคิดว่าวิธีเดียวกันนี้สามารถทำได้ในเกมต่อสู้สัตว์ประหลาดของฉัน ... ทุ่งและทีมไม่ได้อ้างอิงสัตว์ประหลาด พวกเขาเก็บรหัสประจำตัวของสัตว์ประหลาดที่บันทึกไว้ในพจนานุกรมมอนสเตอร์กลาง
อย่างไรก็ตามฉันสามารถเห็นปัญหาอีกครั้งด้วยวิธีการนี้ที่ป้องกันไม่ให้ฉันยอมรับมันโดยไม่ลังเลว่าจะเป็นวิธีแก้ปัญหา:
มันอีกครั้งเป็นแหล่งที่มาของรหัสสำเร็จรูป มันทำให้หนึ่ง-liners จำเป็น 3-liners: สิ่งที่ก่อนหน้านี้คือการปรับเปลี่ยนแบบหนึ่งบรรทัดในสถานที่เดียวตอนนี้ต้องใช้ (a) การดึงวัตถุจากพจนานุกรมกลาง (b) การเปลี่ยนแปลง (c) การบันทึกวัตถุใหม่ ไปที่พจนานุกรมกลาง นอกจากนี้การถือรหัสของวัตถุและพจนานุกรมกลางแทนที่จะมีการอ้างอิงเพิ่มความซับซ้อน เนื่องจากมีการโฆษณา FP เพื่อลดความซับซ้อนและรหัสสำเร็จรูปซึ่งคำแนะนำนี้ฉันทำผิด
ผมก็ยังจะเขียนเกี่ยวกับปัญหา asecond ที่ดูเหมือนว่าจะรุนแรงมากขึ้น: วิธีการนี้จะเปิดตัวหน่วยความจำรั่ว โดยปกติแล้ววัตถุที่ไม่สามารถเข้าถึงได้จะถูกเก็บรวบรวมขยะ อย่างไรก็ตามวัตถุที่อยู่ในพจนานุกรมกลางไม่สามารถรวบรวมขยะได้แม้ว่าจะไม่มีวัตถุที่สามารถเข้าถึงได้ที่อ้างถึง ID เฉพาะนี้ และในขณะที่การตั้งโปรแกรมอย่างระมัดระวังในทางทฤษฎีสามารถหลีกเลี่ยงการรั่วไหลของหน่วยความจำ (เราสามารถดูแลการลบแต่ละวัตถุออกจากพจนานุกรมกลางด้วยตนเองเมื่อมันไม่ต้องการอีกต่อไป) นี่เป็นข้อผิดพลาดและ FP โฆษณาเพื่อเพิ่มความถูกต้องของโปรแกรม ไม่ใช่วิธีที่ถูกต้อง
อย่างไรก็ตามฉันพบในเวลาที่ดูเหมือนว่าจะเป็นปัญหาที่แก้ไขแล้ว Java นำเสนอWeakHashMap
ที่สามารถใช้เพื่อแก้ไขปัญหานี้ C # จัดให้มีสิ่งอำนวยความสะดวกที่คล้ายกัน - ConditionalWeakTable
- แม้ว่าจะเป็นไปตามเอกสาร แต่ก็มีความหมายที่จะใช้โดยคอมไพเลอร์ และใน Haskell เรามีSystem.Mem.Weak
การจัดเก็บพจนานุกรมดังกล่าวเป็นโซลูชันการทำงานที่ถูกต้องสำหรับปัญหานี้หรือไม่หรือมีวิธีที่ง่ายกว่าที่ฉันมองไม่เห็นใช่หรือไม่ ฉันจินตนาการว่าจำนวนพจนานุกรมดังกล่าวสามารถเติบโตและไม่ดีได้อย่างง่ายดาย ดังนั้นหากพจนานุกรมเหล่านี้ควรจะไม่เปลี่ยนรูปแบบเช่นนี้อาจหมายถึงการส่งผ่านพารามิเตอร์จำนวนมากหรือในภาษาที่สนับสนุนการคำนวณแบบ monadic เนื่องจากพจนานุกรมจะจัดขึ้นใน monads (แต่อีกครั้งฉันอ่านว่าในการทำงานอย่างหมดจด ภาษาที่มีโค้ดน้อยที่สุดควรเป็น monadic ในขณะที่โซลูชันพจนานุกรมนี้จะวางโค้ดเกือบทั้งหมดไว้ในState
monad ซึ่งทำให้ฉันสงสัยอีกครั้งว่านี่เป็นวิธีที่ถูกต้องหรือไม่)
หลังจากพิจารณาแล้วฉันคิดว่าฉันจะเพิ่มคำถามอีกหนึ่งคำถาม: เราได้อะไรจากการสร้างพจนานุกรมดังกล่าว นั่นคือสิ่งที่ผิดปกติกับการเขียนโปรแกรมที่จำเป็นคือผู้เชี่ยวชาญหลายคนกล่าวว่าการเปลี่ยนแปลงในวัตถุบางอย่างแพร่กระจายไปยังรหัสอื่น ๆ ในการแก้ปัญหานี้วัตถุควรจะไม่เปลี่ยนรูป - ด้วยเหตุผลนี้หากฉันเข้าใจอย่างถูกต้องการเปลี่ยนแปลงที่เกิดขึ้นกับวัตถุเหล่านั้นไม่ควรปรากฏที่อื่น แต่ตอนนี้ฉันกังวลเกี่ยวกับส่วนอื่น ๆ ของรหัสที่ทำงานกับข้อมูลที่ล้าสมัยดังนั้นฉันจึงคิดค้นพจนานุกรมกลางเพื่อที่ ... การเปลี่ยนแปลงอีกครั้งในบางส่วนของรหัสเผยแพร่ไปยังส่วนอื่น ๆ ของรหัส! ดังนั้นเราไม่ได้กลับไปสู่รูปแบบที่จำเป็นพร้อมกับข้อเสียทั้งหมด แต่มีความซับซ้อนเพิ่มขึ้นหรือไม่
Team
) สามารถดึงผลลัพธ์ของการต่อสู้และทำให้สถานะของมอนสเตอร์โดย (หมายเลขการต่อสู้, รหัสเอนทิตีมอนสเตอร์) ทูเปิล