SOLID In practice: iPhone and iPad
บทความนี้ผมจะอิงบริบทของการเขียน Mobile iOS นิดนึง แต่คิดว่าเข้าใจไม่ยาก
ถ้าเราทำ Mobile แล้วเราทำทั้ง iPhone, iPad แล้วมี Requirement ว่า iPhone ก็ให้แสดงเหมือน iPad นั่นแหละ แต่ Margin ระหว่างแต่ละปุ่มแต่ละตัวหนังสือแคบลงหน่อยจะได้บีบลงหน้าจอได้
มัน Implement ได้สองแบบ
1. ใช้ xib ตัวเดิม แล้วลาก NSLayoutConstraint มาเป็น Outlet แล้วก็บอกว่า if (Device.iphone) thisSpecificMargin.constant = x
2. สร้าง xib ตัวใหม่เลยสำหรับ iphone
(XIB คือไฟล์ Layout สำหรับคนที่เขียนเว็บก็ให้นึกถึงว่าเป็น View template คล้ายๆ กับ Jade, Handlebars ไฟล์นึงนั่นแหละ)
ถามว่าทางไหนดีกว่ากัน ก็จะตอบว่า แล้วแต่ คือการแยก XIB ออกจากกันทำให้ถ้าอนาคตอีกฝั่งอยากแยก Layout ออกจากกัน iPhone, iPad ต่างกันมากขึ้นเรื่อยๆ ในระหว่างที่แอพเราพัฒนา เราจะรองรับการเปลี่ยนแปลงแบบนี้ได้ง่าย
ข้อเสียคือมันจะมี Code ที่ดู Text ซ้ำเยอะ ซึ่งหลายๆ คนมักจะหงุดหงิดที่เห็นโค้ดซ้ำกันแบบตัวอักษรต่อตัวอักษร
แต่ถ้าตรงข้าม ถ้าอนาคตในแอพพลิเคชั่นนี้ อนาคตเวลาเปลี่ยนมักจะเปลี่ยนพร้อมกันทั้ง iPhone และ iPad เช่น ถ้าเพิ่มปุ่มก็เพิ่มทั้งสองอัน ถ้าเปลี่ยนสีก็เปลี่ยนสีทั้งสองอันพร้อมๆ กัน
สรุปง่ายๆ คือ การเลือกทางเลือก 2 ทำให้การเปลี่ยน “พร้อมกัน” ยากขึ้น ในขณะที่เปลี่ยน “แยกกัน” ง่ายขึ้น ในขณะที่ทางเลือก 1 จะตรงข้ามเลย
ถ้าเราย้อนกลับมาดู SOLID Principle ในข้อ Single Responsibility เขากล่าวว่า แต่ละส่วนของ Application ควรจะ “Should have only one reason to change” หรือ แต่ละส่วนประกอบของ Application ควรจะมีแค่ “เหตุผลเดียว” ที่จะเปลี่ยนได้
คำถามคือ ในมุมมองของ User เขาเห็นว่า iPhone กับ iPad มันเป็น “สิ่งเดียวกัน” หรือเปล่า
ถ้าเขามองว่ามันเป็นสิ่งเดียวกันที่ควรเปลี่ยนพร้อมกันเป็นหลักอาจจะต่างกันบ้างแค่นิดหน่อย แต่การเปลี่ยนส่วนมากควรเปลี่ยนพร้อมกัน การเปลี่ยนโค้ดใน xib ก็มีเหตุผลเดียวคือ “User อยากเปลี่ยน Layout”
ในกรณีนี้เราก็ใช้ XIB เดียวได้โดยไม่ Violate SRP
แต่ตรงข้าม ถ้าเขามองแยกจากกันแล้ว เขารู้สึกว่า iPhone กับ iPad มันเป็นคนละแอพพลิเคชั่นกัน คนละ Layout กัน
กรณีนี้ ถ้าเราฝืนจับรวมกันโดยเลือกทางเลือกที่ 1 ที่มี xib ตัวเดียว จะกลายเป็น xib ตัวนั้น มีสองเหตุผลที่จะเปลี่ยนได้คือ “User อยากเปลี่ยน iPhone Layout” และ “User อยากเปลี่ยน iPad Layout”
นั่นก็คือเรา Violate Single-responsibility principle ไปแล้ว
Moral ที่ทำให้อยากเขียนเรื่องนี้คือ
- Developer มักไม่ได้มอง SOLID ในแง่ของที่ว่าจริงๆ แล้วหลักการ SOLID เนี่ยมัน Context-dependent กับตัวธรรมชาติของลูกค้าและแอพพลิเคชั่น เรามักคิดว่ามันเป็นปัญหาเชิง Technical ล้วนๆ ผมยกตัวอย่างให้เห็นแล้วว่าเคสนี้ เราจะ Violate SRP มั้ย ขึ้นอยู่กับว่า “ลูกค้า ผู้ใช้ Designer เห็นแอพเราเป็นอย่างไร” ทำเหมือนกัน อาจจะ Violate หรือไม่ Violate ก็ได้ ขึ้นกับ Context หรือบริบทของแอพ ไม่เกี่ยวกับ Technical เลย
- อยากให้เห็นว่า การใช้ SOLID ในเชิง Practice มันทำอย่างไรบ้าง อย่างถ้าผมต้องไปเล่าให้ใครฟังว่า SOLID มีประโยชน์ยังไงนอกตำรา ผมก็บอกว่า “ผมใช้ SOLID ผมเห็นว่าลูกค้ามอง iPhone และ iPad ไม่เหมือนกัน ผมเลยตัดสินใจ Copy-Paste XIB เป็นสองส่วน ถ้าผมไม่ใช้ SOLID แล้วผมคิดง่ายๆ ลูกค้าเขาบอกขอเปลี่ยน Layout ผมก็แค่ใช้ If โง่ๆ ตัวนึงจบเพราะมันง่ายและเร็วกว่าสร้าง XIB ใหม่”
- Developer หลายๆ คนชอบเห็นภาพของ SOLID หรือ OOP มันเป็นหลักการใหญ่ๆ แบบใช้มาเถียงกันในห้องประชุมตอนวางแผน กางตำรามาคุยกันว่าดีไซน์ในกระดาษรายงาน 10–20 หน้า มัน Violate อะไรมั้ย Audit ดีไซน์ประชุม 5–6 คนเถียงกันไปกันมา มีพิธีกรรมมากมายแลดูซับซ้อนยิ่งใหญ่เข้าถึงยาก ผมพบว่าภาพนั้นไม่ตรงความจริง ผมพบส่วนตัวว่าจริงๆ ผมใช้มันตลอดเวลาแต่แบบ Subtle เนียนๆ คล้ายๆ แบบนี้มากกว่า อย่างที่ผมเขียนบล็อกวันนี้ผมก็แค่เขียนโค้ด นึกขึ้นมาได้ อ้อ เรา Copy XIB ดีกว่าเพราะเดาเอาว่าฝั่ง User มองเห็นเป็นคนละแอพคนละ Layout นี่แหละคือ SOLID ในชีวิตจริง เป็นหลักการที่มีพลังสูงและช่วยผมมาก แต่ไม่ได้มีพิธีการการตระเตรียมอะไรยิ่งใหญ่มากมาย (อย่างบล็อกนี้ ใช้แค่ 4 วินาทีตอนที่ Copy-paste โค้ด) มันถูกใช้ตลอดเวลาทีละนิดละหน่อยมากกว่า
ส่งท้าย
แม้แต่ตัวคุณก็อาจจะเคยใช้ SOLID ก็ได้นะครับ แต่แค่ไม่เคยเข้ามาคิดว่า “เห้ย ช็อตนี้ 5 วินาทีที่ผ่านมา วิธีที่เราคิดเนี่ย นี่เรากำลังคิดแบบ SOLID นี่หว่า”
การที่เราลองทวนแล้วลอง Map กลับไปดูว่าตอนนี้เราคิดแบบ SOLID หรือไม่ มันจะมีพลังมากเมื่อเราต้องการ Scale นิสัยที่การเขียนโค้ดที่ดีของเรา
ถ้าเรารู้ตัวว่า จังหวะนี้ นิสัยของเรามัน Violate SOLID หรือเข้าทาง SOLID กันแน่ เราก็บอกได้ว่าเราควรจะพัฒนาอย่างไร นิสัยไหนควรทำมากขึ้น นิสัยไหนควรทำน้อยลงครับ