Intro
ใน MVVM วิธีปฏิบัติตามปกติคือการให้ Views ค้นหา ViewModels ของตนโดยการแก้ไขจากคอนเทนเนอร์Dependency injection (DI) สิ่งนี้จะเกิดขึ้นโดยอัตโนมัติเมื่อคอนเทนเนอร์ถูกขอให้ระบุ (แก้ไข) อินสแตนซ์ของคลาส View คอนเทนเนอร์ฉีด ViewModel ลงใน View โดยเรียกตัวสร้างของ View ซึ่งยอมรับพารามิเตอร์ ViewModel โครงร่างนี้เรียกว่าการกลับตัวควบคุม (IoC)
ประโยชน์ของ DI
ประโยชน์หลักที่นี่คือสามารถกำหนดค่าคอนเทนเนอร์ในขณะทำงานพร้อมคำแนะนำเกี่ยวกับวิธีแก้ไขประเภทที่เราขอจากคอนเทนเนอร์ สิ่งนี้ช่วยให้สามารถทดสอบได้มากขึ้นโดยสั่งให้แก้ไขประเภท (มุมมองและ ViewModels) ที่เราใช้เมื่อแอปพลิเคชันของเราทำงานจริง แต่ให้คำสั่งต่างกันเมื่อเรียกใช้การทดสอบหน่วยสำหรับแอปพลิเคชัน ในกรณีหลังแอปพลิเคชันจะไม่มี UI ด้วยซ้ำ (ยังไม่ทำงานเพียงแค่การทดสอบเท่านั้น) ดังนั้นคอนเทนเนอร์จะแก้ไขปัญหาการล้อเลียนแทนประเภท "ปกติ" ที่ใช้เมื่อแอปพลิเคชันทำงาน
ปัญหาที่เกิดจาก DI
จนถึงตอนนี้เราได้เห็นแล้วว่าแนวทาง DI ช่วยให้สามารถทดสอบแอปพลิเคชันได้ง่ายโดยการเพิ่มเลเยอร์นามธรรมเหนือการสร้างส่วนประกอบของแอปพลิเคชัน มีปัญหาอย่างหนึ่งในแนวทางนี้: ไม่สามารถใช้งานได้ดีกับนักออกแบบภาพเช่น Microsoft Expression Blend
ปัญหาคือทั้งในการเรียกใช้แอปพลิเคชันปกติและการทดสอบหน่วยต้องมีคนตั้งค่าคอนเทนเนอร์พร้อมคำแนะนำเกี่ยวกับประเภทที่ต้องแก้ไข นอกจากนี้ใครบางคนต้องขอให้คอนเทนเนอร์แก้ไขมุมมองเพื่อให้สามารถแทรก ViewModels ลงในได้
อย่างไรก็ตามในเวลาออกแบบไม่มีรหัสของเราทำงานอยู่ ผู้ออกแบบพยายามใช้การสะท้อนเพื่อสร้างอินสแตนซ์ของมุมมองของเราซึ่งหมายความว่า:
- หากตัวสร้าง View ต้องการอินสแตนซ์ ViewModel ผู้ออกแบบจะไม่สามารถสร้างอินสแตนซ์ View ได้เลย - มันจะเกิดข้อผิดพลาดในลักษณะควบคุมบางอย่าง
- หากมุมมองมีตัวสร้างที่ไม่มีพารามิเตอร์มุมมองจะถูกสร้างอินสแตนซ์ แต่
DataContext
จะเป็นnull
เช่นนั้นเราจะได้มุมมอง "ว่าง" ในตัวออกแบบซึ่งไม่มีประโยชน์มากนัก
เข้าสู่ ViewModelLocator
ViewModelLocator เป็นนามธรรมเพิ่มเติมที่ใช้ในลักษณะนี้:
- View เองสร้างอินสแตนซ์ ViewModelLocator เป็นส่วนหนึ่งของรีซอร์สและฐานข้อมูล DataContext ของมันไปยังคุณสมบัติ ViewModel ของตัวระบุตำแหน่ง
- ตัวระบุตำแหน่งจะตรวจพบว่าเราอยู่ในโหมดออกแบบหรือไม่
- หากไม่ได้อยู่ในโหมดออกแบบตัวระบุตำแหน่งจะส่งกลับ ViewModel ที่แก้ไขจากคอนเทนเนอร์ DI ตามที่อธิบายไว้ข้างต้น
- หากอยู่ในโหมดการออกแบบตัวระบุตำแหน่งจะส่งกลับ ViewModel "จำลอง" แบบคงที่โดยใช้ตรรกะของตัวมันเอง (จำไว้ว่าไม่มีคอนเทนเนอร์ในเวลาออกแบบ!); โดยทั่วไปแล้ว ViewModel นี้จะมาพร้อมกับข้อมูลจำลองล่วงหน้า
ซึ่งหมายความว่า View ต้องมีตัวสร้างแบบไม่มีพารามิเตอร์เพื่อเริ่มต้นด้วย (มิฉะนั้นผู้ออกแบบจะไม่สามารถสร้างอินสแตนซ์ได้)
สรุป
ViewModelLocator เป็นสำนวนที่ให้คุณเก็บประโยชน์ของ DI ไว้ในแอปพลิเคชัน MVVM ของคุณในขณะเดียวกันก็ช่วยให้โค้ดของคุณเล่นได้ดีกับนักออกแบบภาพ บางครั้งเรียกว่า "ความสามารถในการผสมผสาน" ของแอปพลิเคชันของคุณ (หมายถึง Expression Blend)
หลังจากการย่อยดังกล่าวข้างต้นให้ดูตัวอย่างการปฏิบัติที่นี่
สุดท้ายการใช้เทมเพลตข้อมูลไม่ใช่ทางเลือกในการใช้ ViewModelLocator แต่เป็นอีกทางเลือกหนึ่งในการใช้คู่ View / ViewModel ที่ชัดเจนสำหรับส่วนต่างๆของ UI ของคุณ บ่อยครั้งคุณอาจพบว่าไม่จำเป็นต้องกำหนด View สำหรับ ViewModel เนื่องจากคุณสามารถใช้เทมเพลตข้อมูลแทนได้