ออกแบบสองครั้ง
การออกแบบซอฟต์แวร์เป็นเรื่องที่ยาก ดังนั้นจึงเป็นไปได้น้อยที่ความคิดแรกของคุณเกี่ยวกับวิธีการสร้างโครงสร้าง Module หรือ System จะสร้างออกแบบที่ดีที่สุด คุณจะได้ผลลัพธ์ที่ดีกว่ามากหากคุณพิจารณาตัวเลือกหลายตัวสำหรับการตัดสินใจออกแบบที่สำคัญแต่ละอย่าง: ออกแบบสองครั้ง
สมมติว่าคุณกำลังออกแบบ Class ที่จะจัดการข้อความของไฟล์สำหรับ GUI Text Editor ขั้นตอนแรกคือการกำหนด Interface ที่ Class จะนำเสนอให้กับส่วนอื่นของ Editor แทนที่จะเลือกความคิดแรกที่เข้ามาในใจ ให้พิจารณาความเป็นไปได้หลายประการ ตัวเลือกหนึ่งคือ Line-Oriented Interface ที่มี Operation เพื่อแทรก แก้ไข และลบบรรทัดข้อความทั้งหมด ตัวเลือกอื่นคือ Interface ที่อิงตามการแทรกและการลบอักขระส่วนบุคคล ตัวเลือกที่สามคือ String-Oriented Interface ซึ่งทำงานบนช่วงอักขระตามอำเภอใจที่อาจข้ามขอบเขตบรรทัด คุณไม่จำเป็นต้องระบุทุกคุณลักษณะของแต่ละทางเลือก ในจุดนี้ก็เพียงพอที่จะร่างวิธี Method ที่สำคัญที่สุดไม่กี่วิธี
พยายามเลือกแนวทางที่แตกต่างกันอย่างสิ้นเชิงจากกัน คุณจะได้เรียนรู้มากขึ้นในลักษณะนี้ แม้ว่าคุณมั่นใจว่ามีเพียงวิธีเดียวที่สมเหตุสมผล ให้พิจารณา Design ที่สองอยู่ดี ไม่ว่าคุณจะคิดว่ามันจะแย่แค่ไหน เป็นเรื่องสนใจที่จะคิดเกี่ยวกับจุดอ่อนของ Design นั้นและเปรียบเทียบกับคุณสมบัติของ Design อื่น
หลังจากที่คุณได้ร่าง Design สำหรับทางเลือก ให้สร้างรายการข้อดีและข้อเสียของแต่ละอย่าง การพิจารณาที่สำคัญที่สุดสำหรับ Interface คือความง่ายในการใช้งานสำหรับซอฟต์แวร์ระดับสูงกว่า ในตัวอย่างข้างต้น Interface ที่เป็น Line-Oriented และ Character-Oriented ต่างก็ต้องมีงานพิเศษในซอฟต์แวร์ที่ใช้ Text Class Line-Oriented Interface จะต้องให้ซอฟต์แวร์ระดับสูงกว่าแยกและรวมบรรทัดระหว่างการทำงาน Partial-Line และ Multi-Line เช่นการตัดและวาง Selection Character-Oriented Interface จะต้องใช้ Loop เพื่อใช้งาน Operation ที่แก้ไขมากกว่าอักขระเดียว นอกจากนี้ยังควรพิจารณาปัจจัยอื่นๆ:
- ทางเลือกหนึ่งมี Interface ที่ง่ายกว่าอีกทางเลือกหนึ่งหรือไม่? ในตัวอย่าง Text Interface ทั้งหมดนั้นค่อนข้างง่าย
- Interface หนึ่งมีความทั่วไปมากกว่าอีก Interface หนึ่งหรือไม่?
- Interface หนึ่งช่วยให้สามารถใช้งาน Implementation ได้อย่างมีประสิทธิภาพมากกว่าอีก Interface หนึ่งหรือไม่? ในตัวอย่าง Text Character-Oriented Approach อาจจะช้ากว่า Approach อื่นๆ อย่างมาก เพราะว่ามันต้องมีการเรียกแยกต่างหากเข้าไปใน Text Module สำหรับแต่ละอักขระ
เมื่อคุณได้เปรียบเทียบ Alternative Design แล้ว คุณจะอยู่ในตำแหน่งที่ดีกว่าในการระบุ Design ที่ดีที่สุด Best Choice อาจเป็นหนึ่งในทางเลือก หรือคุณอาจค้นพบว่าคุณสามารถรวมคุณสมบัติของทางเลือกหลายตัวเป็น Design ใหม่ที่ดีกว่าทางเลือกเดิมใดๆ
บางครั้งไม่มีทางเลือกใดที่ดึงดูดโดยเฉพาะ เมื่อเรื่องนี้เกิดขึ้น ให้ดูว่าคุณสามารถคิดค้น Scheme เพิ่มเติมได้หรือไม่ ใช้ปัญหาที่คุณพบกับทางเลือกเดิมเพื่อขับเคลื่อน Design ใหม่ หากคุณกำลังออกแบบ Text Class และพิจารณาเฉพาะ Line-Oriented และ Character-Oriented Approach คุณอาจสังเกตว่าแต่ละทางเลือกนั้นมีปัญหา เพราะว่ามันต้องการให้ซอฟต์แวร์ระดับสูงกว่าทำการจัดการข้อความเพิ่มเติม นั่นคือสัญญาณเตือน: หากจะมี Text Class มันควรจัดการ Text Manipulation ทั้งหมด เพื่อขจัด Text Manipulation เพิ่มเติม Text Interface ต้องตรงกับ Operation ที่เกิดขึ้นในซอฟต์แวร์ระดับสูงกว่ามากขึ้น Operation เหล่านี้ไม่ได้สอดคล้องกับอักขระเดียวหรือบรรทัดเดียวเสมอไป ลักษณะของการให้เหตุผลนี้ควรนำคุณไปยัง Range-Oriented API สำหรับ Text ซึ่งขจัดปัญหากับ Design ก่อนหน้า
หลักการ Design-It-Twice สามารถนำไปประยุกต์ใช้ในหลายระดับของ System สำหรับ Module คุณสามารถใช้แนวทางนี้ก่อนเลือก Interface ตามที่อธิบายข้างต้น จากนั้นคุณสามารถนำไปประยุกต์ใช้อีกครั้งเมื่อคุณออกแบบ Implementation: สำหรับ Text Class คุณอาจพิจารณา Implementation เช่น Linked List ของ Line Fixed-Size Blocks ของอักขระ หรือ "Gap Buffer" Goal จะแตกต่างกันสำหรับ Implementation มากกว่า Interface: สำหรับ Implementation สิ่งที่สำคัญที่สุดคือ Simplicity และ Performance นอกจากนี้ยังมีประโยชน์ในการสำรวจ Design หลายแบบในระดับที่สูงขึ้นในระบบ เช่นเมื่อเลือก Feature สำหรับ User Interface หรือเมื่อแยก System ออกเป็น Major Module ในแต่ละกรณี ทำให้ง่ายในการระบุแนวทางที่ดีที่สุดหากคุณสามารถเปรียบเทียบทางเลือกอีกสองสามตัว
การออกแบบสองครั้งไม่ต้องใช้เวลาพิเศษมากนัก สำหรับ Module ที่เล็กกว่าเช่น Class คุณอาจไม่ต้องการมากกว่าหนึ่งหรือสองชั่วโมงเพื่อพิจารณาทางเลือก นี่เป็นเวลาน้อยเมื่อเทียบกับวันหรือสัปดาห์ที่คุณจะใช้ไป ในการใช้งาน Class การทดลอง Design เริ่มต้นนั้นจะส่งผลให้ Design ที่ดีกว่าอย่างมาก ซึ่งจะชดเชยเวลาที่ใช้ไปในการออกแบบสองครั้ง สำหรับ Module ที่ใหญ่กว่า คุณจะใช้เวลามากขึ้นในการสำรวจ Design เริ่มต้น แต่ Implementation ยังจะใช้เวลานานขึ้นด้วย และประโยชน์ของ Design ที่ดีกว่าก็จะสูงขึ้นด้วยเช่นกัน
ผมสังเกตเห็นว่าหลักการ Design-It-Twice นั้นบางครั้งยากสำหรับคนฉลาดที่แท้จริง เมื่อพวกเขาเติบโตขึ้น คนฉลาดค้นพบว่าความคิดแรกที่รวดเร็วของพวกเขาเกี่ยวกับปัญหาใดๆ ก็เพียงพอสำหรับเกรดที่ดี ไม่จำเป็นต้องพิจารณาความเป็นไปได้ที่สองหรือสาม นี่ทำให้ง่ายต่อการพัฒนานิสัยการทำงานที่ไม่ดี อย่างไรก็ตาม เมื่อคนเหล่านี้โตขึ้น พวกเขาก็ได้รับการเลื่อนตำแหน่งเข้าไปในสภาพแวดล้อมที่มีปัญหายากขึ้น ในที่สุด ทุกคนถึงจุดที่ความคิดแรกของคุณก็ไม่เพียงพอแล้ว หากคุณต้องการได้ผลลัพธ์ที่ยิ่งใหญ่จริงๆ คุณจะต้องพิจารณาความเป็นไปได้ที่สอง หรือบางทีความเป็นไปได้ที่สาม ไม่ว่าคุณจะฉลาดแค่ไหน การออกแบบ Large Software System จึงอยู่ในหมวดหมู่นี้: ไม่มีใครฉลาดพอที่จะทำให้ถูกต้องเมื่อพยายามครั้งแรก
น่าเสียดายที่ ผมมักเห็นคนฉลาดที่ยืนยันในการใช้งาน Design ที่เข้ามาในใจครั้งแรก และนี่ทำให้พวกเขาทำงานต่ำกว่าศักยภาพที่แท้จริงของพวกเขา (นอกจากนี้ยังทำให้พวกเขาเป็นเรื่องที่น่ารำคาญในการทำงานด้วย) บางที พวกเขาอาจเชื่ออย่างไม่รู้สึกตัวว่า "คนฉลาดทำให้ถูกต้องครั้งแรก" ดังนั้นหากพวกเขาพยายามออกแบบหลายครั้ง มันจะหมายความว่าพวกเขาไม่ฉลาดหลังจากนั้น นี่ไม่ใช่ความจริง ไม่ใช่ว่าคุณไม่ฉลาด เป็นเพราะว่าปัญหานั้นยากมากจริง! นอกจากนี้ นั่นเป็นเรื่องที่ดี: มันสนุกกว่ามากในการทำงานบนปัญหาที่ยากซึ่งคุณต้องคิดอย่างระมัดระวัง มากกว่าปัญหาที่ง่ายซึ่งคุณไม่ต้องคิดเลย
แนวทาง Design-It-Twice ไม่เพียงแต่ปรับปรุง Design ของคุณเท่านั้น แต่ยังปรับปรุง Design Skill ของคุณด้วย กระบวนการคิดค้นและเปรียบเทียบ Approach หลายแบบจะสอนให้คุณรู้เกี่ยวกับปัจจัยที่ทำให้ Design ดีหรือแย่ลง เมื่อเวลาผ่านไป นี่จะทำให้ง่ายขึ้นสำหรับคุณในการตัดทิ้ง Design ที่ไม่ดีและระบุ Design ที่ยิ่งใหญ่จริงๆ