ในที่สุดสิ่งใดที่กลายพันธุ์ไม่เหมาะสมกับรัฐ
ใช่ แต่ถ้ามันอยู่เบื้องหลังฟังก์ชั่นสมาชิกของคลาสเล็ก ๆ ที่เป็นเอนทิตี้เพียงอย่างเดียวในระบบทั้งหมดที่สามารถจัดการสถานะส่วนตัวของมันได้สถานะนั้นจะมีขอบเขตแคบมาก
คุณควรจัดการกับสภาวะที่น้อยที่สุดเท่าที่จะเป็นไปได้
จากจุดยืนของตัวแปร: มีโค้ดไม่กี่บรรทัดที่สามารถเข้าถึงได้ จำกัด ขอบเขตของตัวแปรให้น้อยที่สุด
จากจุดยืนของรหัส: ตัวแปรน้อยควรสามารถเข้าถึงได้จากบรรทัดของรหัสที่เป็นไปได้ จำกัด จำนวนของตัวแปรที่บรรทัดของรหัสสามารถอาจเข้าถึง (มันไม่ได้สำคัญมากไม่ว่าจะไม่เข้าถึงได้ทุกที่เรื่องคือว่ามันสามารถทำได้ )
ตัวแปรโกลบอลนั้นแย่มากเพราะมันมีขอบเขตสูงสุด แม้ว่าพวกเขาจะเข้าถึงได้จากโค้ด 2 บรรทัดใน codebase จากบรรทัด POV ของโค้ด แต่ตัวแปรทั่วโลกก็สามารถเข้าถึงได้เสมอ จากมุมมองของตัวแปรตัวแปรทั่วโลกที่มีการเชื่อมโยงภายนอกสามารถเข้าถึงโค้ดทุกบรรทัดในโค้ดเบสทั้งหมดได้ (หรือโค้ดทุกบรรทัดที่มีส่วนหัวอยู่แล้ว) ทั้งๆที่มีการเข้าถึงรหัส 2 บรรทัดเท่านั้นหากตัวแปรทั่วโลกสามารถมองเห็นได้ด้วยรหัส 400,000 บรรทัดรายการผู้ต้องสงสัยทันทีของคุณเมื่อคุณพบว่ามีการตั้งค่าสถานะที่ไม่ถูกต้องจะมี 400,000 รายการ (อาจลดลงอย่างรวดเร็ว 2 รายการที่มีเครื่องมือ แต่อย่างไรก็ตามรายการทันทีจะมีผู้ต้องสงสัย 400,000 รายและนั่นไม่ใช่จุดเริ่มต้นที่ให้กำลังใจ)
โอกาสที่เช่นเดียวกันแม้ว่าตัวแปรทั่วโลกจะเริ่มต้นเพียงการแก้ไขโดยโค้ด 2 บรรทัดใน codebase ทั้งหมด แต่แนวโน้มที่โชคร้ายของโค้ดเบสที่จะวิวัฒนาการไปข้างหลังจะมีแนวโน้มเพิ่มขึ้นอย่างมากเพราะมันสามารถเพิ่มได้มาก นักพัฒนาคลั่งไปตามกำหนดเวลาดูตัวแปรทั่วโลกและรู้ว่าพวกเขาสามารถใช้ทางลัดผ่านมัน
ในภาษาที่ไม่บริสุทธิ์เช่น C ++ การจัดการของรัฐไม่ใช่สิ่งที่คุณกำลังทำจริง ๆ หรือ
ส่วนใหญ่ใช่ถ้าคุณใช้ C ++ ในแบบที่แปลกใหม่มากซึ่งคุณจัดการกับโครงสร้างข้อมูลที่ไม่เปลี่ยนรูปแบบและการเขียนโปรแกรมที่ใช้งานได้จริง - มันมักจะเป็นแหล่งที่มาของข้อบกพร่องส่วนใหญ่เมื่อการจัดการของรัฐซับซ้อนและซับซ้อน มักจะเป็นฟังก์ชั่นการมองเห็น / การเปิดรับแสงของสถานะนั้น
และวิธีอื่น ๆ ในการจัดการกับสภาพน้อยที่สุดเท่าที่จะทำได้นอกเหนือจากการ จำกัด อายุการใช้งานของตัวแปร?
สิ่งเหล่านี้อยู่ในขอบเขตการ จำกัด ขอบเขตของตัวแปร แต่มีหลายวิธีในการทำเช่นนี้:
- หลีกเลี่ยงตัวแปรดิบทั่วโลกเช่นกาฬโรค แม้แต่ฟังก์ชั่น setter / getter ระดับโลกที่โง่เง่าก็ จำกัด การมองเห็นของตัวแปรนั้นอย่างมากและอย่างน้อยก็อนุญาตให้มีวิธีการรักษาค่าคงที่ (เช่น: ถ้าตัวแปรทั่วโลกไม่ควรได้รับอนุญาตให้เป็นค่าลบ แน่นอนว่าแม้แต่การออกแบบตัวเซ็ตเตอร์ / ทะเยอทะยานเหนือสิ่งอื่นใดที่จะเป็นตัวแปรระดับโลกก็คือการออกแบบที่ไม่ดีนักประเด็นของฉันคือว่ามันยังดีกว่า
- ทำให้คลาสของคุณเล็กลงถ้าเป็นไปได้ คลาสที่มีฟังก์ชั่นสมาชิกหลายร้อยตัวตัวแปรสมาชิก 20 ตัวและโค้ด 30,000 บรรทัดที่ใช้มันจะมีตัวแปรส่วนตัว "ทั่วโลก" เนื่องจากตัวแปรเหล่านั้นจะสามารถเข้าถึงได้จากฟังก์ชั่นสมาชิกซึ่งประกอบด้วยโค้ดขนาด 30k บรรทัด คุณอาจจะบอกว่า "ความซับซ้อนของรัฐ"
30,000*20=600,000
ในกรณีที่ว่าในขณะที่ลดตัวแปรท้องถิ่นในการทำงานของสมาชิกแต่ละคนเป็น หากมีตัวแปรส่วนกลาง 10 ตัวที่สามารถเข้าถึงได้จากนั้นความซับซ้อนของรัฐอาจเป็นเช่น30,000*(20+10)=900,000
นั้น "ความซับซ้อนของรัฐ" ที่มีสุขภาพดี (ตัวชี้วัดส่วนบุคคลของฉันที่ประดิษฐ์ขึ้นมา) ควรมีค่าเป็นพันหรือต่ำกว่าสำหรับชั้นเรียนไม่นับหมื่นและแน่นอนไม่นับแสน สำหรับฟังก์ชั่นฟรีให้พูดหลายร้อยหรือต่ำกว่าก่อนที่เราจะเริ่มปวดหัวอย่างรุนแรงในการบำรุงรักษา
- ในหลอดเลือดดำเดียวกันกับข้างต้นอย่าใช้บางสิ่งบางอย่างในฐานะสมาชิกฟังก์ชันหรือฟังก์ชั่นเพื่อนที่อาจไม่ใช่สมาชิกหรือไม่เป็นเพื่อนโดยใช้เพียงส่วนติดต่อสาธารณะของชั้นเรียนเท่านั้น ฟังก์ชั่นดังกล่าวไม่สามารถเข้าถึงตัวแปรส่วนตัวของชั้นเรียนและลดโอกาสในการเกิดข้อผิดพลาดโดยการลดขอบเขตของตัวแปรส่วนตัวเหล่านั้น
- หลีกเลี่ยงการประกาศตัวแปรนานก่อนที่พวกเขาจะต้องการใช้งานจริงในฟังก์ชั่น (เช่นหลีกเลี่ยงการสืบทอดสไตล์ C ซึ่งประกาศตัวแปรทั้งหมดที่ด้านบนของฟังก์ชั่นแม้ว่าพวกเขาจะต้องการเพียงหลายบรรทัดด้านล่าง) ถ้าคุณใช้สไตล์นี้อย่างน้อยพยายามอย่างน้อยสำหรับฟังก์ชั่นที่สั้นลง
นอกเหนือจากตัวแปร: ผลข้างเคียง
แนวทางเหล่านี้มากมายที่ฉันระบุไว้ข้างต้นคือการแก้ปัญหาการเข้าถึงโดยตรงไปยังสถานะดิบและไม่แน่นอน แต่ใน codebase ที่ซับซ้อนเพียงพอเพียงการ จำกัด ขอบเขตของตัวแปรดิบจะไม่เพียงพอที่จะให้เหตุผลเกี่ยวกับความถูกต้องได้อย่างง่ายดาย
คุณอาจพูดได้ว่าเป็นโครงสร้างข้อมูลกลางที่อยู่เบื้องหลังส่วนติดต่อที่เป็นของแข็งทั้งหมดที่เป็นนามธรรมมีความสามารถอย่างเต็มที่ในการบำรุงรักษาค่าคงที่ได้อย่างสมบูรณ์และยังคงจบลงด้วยความเศร้าโศกมากมาย ตัวอย่างของรัฐส่วนกลางที่ไม่จำเป็นต้องสามารถเข้าถึงได้ทั่วโลก แต่สามารถเข้าถึงได้อย่างกว้างขวางเท่านั้นคือกราฟฉากกลางของเอ็นจิ้นเกมหรือโครงสร้างข้อมูลเลเยอร์กลางของ Photoshop
ในกรณีเช่นนี้แนวคิดของ "สถานะ" นั้นเกินกว่าตัวแปรดิบและเพียงแค่โครงสร้างข้อมูลและสิ่งต่าง ๆ ที่จัดเรียง มันก็ช่วยลดขอบเขตของมันได้เช่นกัน (ลดจำนวนบรรทัดที่สามารถเรียกใช้ฟังก์ชันที่ทำให้กลายพันธุ์ทางอ้อม)
โปรดสังเกตว่าฉันตั้งใจทำเครื่องหมายแม้กระทั่งอินเทอร์เฟซเป็นสีแดงที่นี่ตั้งแต่ระดับสถาปัตยกรรมแบบซูมออกที่กว้างการเข้าถึงส่วนต่อประสานนั้นยังคงอยู่ในสภาพกลายพันธุ์แม้ว่าจะเป็นทางอ้อม ชั้นสามารถรักษาค่าคงที่เป็นผลมาจากอินเทอร์เฟซ แต่ที่ไปเท่านั้นในแง่ของความสามารถของเราที่จะให้เหตุผลเกี่ยวกับความถูกต้อง
ในกรณีนี้โครงสร้างข้อมูลกลางอยู่เบื้องหลังอินเทอร์เฟซแบบนามธรรมซึ่งอาจไม่สามารถเข้าถึงได้ทั่วโลก มันอาจถูกส่งเข้ามาแล้วก็กลายพันธุ์ทางอ้อม (ผ่านฟังก์ชั่นสมาชิก) จากส่วนของฟังก์ชั่นใน codebase ที่ซับซ้อนของคุณ
ในกรณีเช่นนี้แม้ว่าโครงสร้างข้อมูลจะรักษาค่าคงที่ไว้ได้อย่างสมบูรณ์แบบสิ่งต่าง ๆ สามารถเกิดขึ้นได้ในระดับที่กว้างขึ้น (เช่นเครื่องเล่นเสียงอาจรักษาค่าคงที่ทุกประเภทไว้เช่นระดับเสียงไม่เคยอยู่นอกช่วง 0% ถึง 100% แต่นั่นไม่ได้ป้องกันจากผู้ใช้กดปุ่มเล่นและมีคลิปเสียงแบบสุ่มนอกเหนือจากที่เขาเพิ่งโหลดเมื่อเร็ว ๆ นี้เริ่มเล่นเป็นเหตุการณ์ที่เกิดขึ้นซึ่งทำให้รายการที่จะเล่นในทางที่ถูกต้อง แต่ ยังไม่พึงประสงค์พฤติกรรมผิดพลาดจากมุมมองของผู้ใช้ในวงกว้าง)
วิธีการป้องกันตัวเองในสถานการณ์ที่ซับซ้อนเหล่านี้คือ "คอขวด" สถานที่ใน codebase ที่สามารถเรียกใช้ฟังก์ชั่นซึ่งทำให้เกิดผลข้างเคียงจากภายนอกแม้ในมุมมองที่กว้างกว่าของระบบที่เกินกว่าสภาพดิบ
คุณจะเห็นว่าไม่มี "สถานะ" (แสดงเป็นสีแดงและนี่ไม่ได้หมายความว่า "ตัวแปรดิบ" มันหมายถึง "วัตถุ" และอาจอยู่เบื้องหลังอินเทอร์เฟซแบบนามธรรม) ถูกเข้าถึงได้หลายแห่ง . ฟังก์ชั่นแต่ละตัวมีการเข้าถึงสถานะท้องถิ่นซึ่งสามารถเข้าถึงได้โดยตัวอัพเดตกลางและรัฐกลางสามารถเข้าถึงตัวอัปเดตกลางเท่านั้น (ทำให้ไม่อยู่กึ่งกลางอีกต่อไป
นี่เป็นเพียงฐานรหัสที่ซับซ้อนจริงๆเช่นเกมที่มีโค้ดยาวถึง 10 ล้านบรรทัด แต่มันสามารถช่วยได้อย่างมากในการให้เหตุผลเกี่ยวกับความถูกต้องของซอฟต์แวร์ของคุณและพบว่าการเปลี่ยนแปลงของคุณให้ผลลัพธ์ที่สามารถคาดการณ์ได้ ของสถานที่ที่สามารถกลายพันธุ์สถานะสำคัญที่สถาปัตยกรรมทั้งหมดหมุนไปรอบ ๆ เพื่อให้ทำงานได้อย่างถูกต้อง
นอกเหนือจากตัวแปรดิบคือผลข้างเคียงภายนอกและผลข้างเคียงจากภายนอกเป็นแหล่งที่มาของข้อผิดพลาดแม้ว่าจะถูก จำกัด อยู่ในฟังก์ชันสมาชิกจำนวนหนึ่ง หากฟังก์ชั่น boatload สามารถเรียกใช้ฟังก์ชันสมาชิกจำนวนมากโดยตรงได้แสดงว่ามีฟังก์ชั่นจำนวนมากในระบบที่สามารถทำให้เกิดผลข้างเคียงทางอ้อมภายนอกและเพิ่มความซับซ้อน หากมีเพียงที่เดียวใน codebase ที่เข้าถึงฟังก์ชันสมาชิกเหล่านั้นและเส้นทางการดำเนินการหนึ่งนั้นไม่ถูกทริกเกอร์โดยเหตุการณ์ที่เกิดขึ้นเป็นระยะ ๆ ทั่วสถานที่ แต่ถูกดำเนินการแทนในรูปแบบที่คาดการณ์ได้และควบคุมได้แทนมันจะลดความซับซ้อน
ความซับซ้อนของรัฐ
แม้แต่ความซับซ้อนของรัฐก็เป็นปัจจัยสำคัญที่ต้องคำนึงถึง โครงสร้างที่เรียบง่ายซึ่งสามารถเข้าถึงได้อย่างกว้างขวางหลังอินเทอร์เฟซแบบนามธรรมนั้นไม่ยากที่จะทำ
โครงสร้างข้อมูลกราฟที่ซับซ้อนซึ่งแสดงถึงการเป็นตัวแทนตรรกะหลักของสถาปัตยกรรมที่ซับซ้อนนั้นเป็นเรื่องง่ายที่จะเลอะและในลักษณะที่ไม่ได้ละเมิดค่าคงที่ของกราฟ กราฟมีความซับซ้อนมากกว่าโครงสร้างที่เรียบง่ายหลายเท่าและยิ่งมีความสำคัญยิ่งขึ้นในกรณีเช่นนี้เพื่อลดความซับซ้อนของการรับรู้โค้ดเบสเพื่อลดจำนวนสถานที่ที่เข้าถึงโครงสร้างกราฟดังกล่าวให้น้อยที่สุด และในกรณีที่ "กลยุทธ์แห่งการอัพเดทกลาง" แบบนั้นพลิกกลับไปสู่กระบวนทัศน์แบบดึงเพื่อหลีกเลี่ยงการเกิดเป็นระยะ ๆ ผลักไปที่โครงสร้างข้อมูลกราฟโดยตรงจากทั่วทุกที่สามารถจ่ายได้จริง