Screen Shot 2558-11-26 at 10.38.29 AM
จากงาน Android Dev Summit 2015 นั้น
มีหัวข้อที่น่าสนใจเยอะเลย หนึ่งในนั้นคือ Android Application Architecture
ได้อธิบายสิ่งต่าง ๆ ที่น่าสนใจสำหรับการพัฒนา Android application
แต่หัวข้อที่ชอบมาก ๆ คือ โครงสร้างการทำงานของระบบ
มาดูกันว่าเป็นอย่างไร

เริ่มต้นได้อย่างน่าคิด

อะไรที่คุณตัดสินใจไปแล้วมันจะอยู่กับคุณไปตลอด
ดังนั้นคิดให้ดี ๆ
โดยโครงสร้างของ app ที่คิดขึ้นมานั้น มันจะง่าย หรือ ยาก
ให้คิดถึงปัญหาที่ต้องการแก้ไขเป็นหลัก
ไม่ใช่คิดถึง วิธีการแก้ไขปัญหา หรือ framework หรือ library เป็นหลัก !!

คำถามต่อมา น่าคิดมาก ๆ คือ แล้วโครงสร้างของระบบจะเป็นแบบไหนดีล่ะ ?

  • MVC
  • MVP
  • MVP-I
  • MVVM
  • MVVM-I
  • VIPER
  • Clean Architecture
  • Reactive
  • Flux

พบว่ามันมีหลายแนวทางมาก ๆ !! (Paradox of choices)

แต่แนะนำว่า
อย่าไปยึดติดกับ desing pattern
อย่าไปยึดติดกับ Library
อย่าไปยึดติดกับ Framework
เพราะว่ามันจะจำกัดกรอบความคิดของเรามากเกินไป
ให้เน้นไปที่การแก้ไขปัญหาดีกว่า

ทุกแนวทางนั้น ล้วนมีเป้าหมายเดียวกัน คือ
สามารถเปลี่ยนแปลงได้รวดเร็ว
นั่นคือ สามารถ refactoring ได้ง่าย
รวมไปถึงทดสอบการทำงานแต่ละส่วนได้ง่าย
เพื่อป้องกันไม่ให้เกิดสิ่งที่เรียกว่า Lava flow

สิ่งที่แนะนำในการออกแบบ คือ Architect for UX (User Experience)

มันเป็นอย่างไรล่ะ ?

ตัวอย่าง App ต้องทำการส่งข้อมูลไปยัง server
คำถามคือ จะขึ้นรูป loading ทำไม ?
แถมบางครั้งถ้า network มีปัญหา
รูป loading มันไม่หายไปด้วย
ทำให้ app ปิดตัวเองอีก
ซึ่งเป็นประสบการณ์การใช้งานที่ไม่ดีเลยใช่ไหม ?

แสดงการทำงานดังรูป
Screen Shot 2558-11-26 at 10.53.04 AM

ปัญหาจากรูปแบบการทำงานนี้ คือ
View จะรอไปจนกว่าจะได้ข้อมูลจาก ViewController และ Network
นั่นคือ ViewController มันผูกติดกับ Network อย่างมาก !!

สามารถแก้ไขด้วยการแยก ViewController ออกจาก Network ซะ
ด้วยการเพิ่มอีกหนึ่งส่วน หรือ อีกหนึ่ง layer นั่นคือ Model
เนื่องจาก ViewController นั้นต้องการเพียงข้อมูลเพื่อส่งไปแสดงผลที่ View เท่านั้น
ไม่ได้ต้องการ Network เลย

แสดงการทำงานดังรูป
Screen Shot 2558-11-26 at 10.55.30 AM

แต่จากรูปแบบนี้ก็ยังคงมีปัญหากับ Network อยู่ใช่ไหม ?
เนื่องจากเราเพียงย้ายให้ Network ไปทำงานกับ Model เท่านั้นเอง
ดังนั้น จะแก้ไขปัญหานี้อย่างไรดีล่ะ ?

สิ่งที่ต้องทำก็คือ Model นั้นให้เป็น Persistent model ที่อยู่บนเครื่อง หรือ Local ซะ
และเพิ่มส่วนของ Application Logic ขึ้นมา
เพื่อทำงานร่วมกับ Network แทน

โดยแบ่งการทำงานเป็นส่วน ๆ ดังนี้

ส่วนที่ 1
เมื่อ Application logic ได้ข้อมูลจาก Network หรือ sync data เรียบร้อยแล้ว
จะทำการส่ง หรือ จัดเก็บไปที่ Persistent Model
แสดงการทำงานดังรูป
Screen Shot 2558-11-26 at 11.06.54 AM

ส่วนที่ 2
ส่วนของ ViewController เมื่อต้องการข้อมูลไปแสดงผลที่ View
ก็ให้ทำการดึงข้อมูลจาก Persistent Model แทน
ซึ่งไม่ต้องรออะไรเลย
แสดงการทำงานดังรูป
Screen Shot 2558-11-26 at 11.09.24 AM

ส่วนที่ 3
เมื่อมีการร้องขอ หรือ ส่งข้อมูลมาจากผู้ใช้งานผ่าน View
จะทำการส่งการร้องขอมายัง ViewController
จากนั้นจะส่งการร้องขอไปยัง Application Logic เพื่อทำงาน
ซึ่งจะทำงาน 2 อย่าง คือ

  1. ทำการ update ข้อมูลที่ Persistent Model
  2. ทำการส่งข้อมูลไปยัง Network

จากนั้นเมื่อฝั่ง Network ส่งข้อมูลกลับมายัง Application Logic แล้ว
จะทำการ update ข้อมูลที่ Persistent Model
และแจ้งกลับไปยัง ViewController ว่าคำร้องขอเสร็จเรียบร้อยแล้ว
แสดงการทำงานดังรูป
Screen Shot 2558-11-26 at 11.10.50 AM

มาถึงตรงนี้อาจจะงงอีกว่า จะแจ้งกลับไปยัง ViewController ยังไงว่าทำงานเสร็จแล้ว ?

น่าคิดมาก ๆ
ดังนั้นในขั้นตอนการส่งคำร้องขอตั้งแต่แรก
ต้องมีการจัดการ event หรือ เหตุการณ์ต่าง ๆ ไว้ก่อนนะ
นั่นคือมีการ register event และ subscribe ไว้
เมื่อมีการทำการเสร็จสิ้นก็ให้ทำการส่ง notify ไปยัง subscribe นั้น ๆ
จากนั้นก็ทำการ unsubscribe ซะ
เป็นอันพบขั้นตอนการทำงาน
แนวคิดพวกนี้มันคือ EventBus นั่นเอง

แต่ทำมาเยอะขนาดนี้ มันก็ยังไม่ปัญหาตามมาอีก
นั่นคือ เรื่องของ Background process
ถ้า queue การทำงานมันค้าง และ เต็ม
ทำให้ process อื่น ๆ ที่อยู่ใน queue รอไปเรื่อย ๆ
จะส่งผลให้ app ค้าง หรือ ปิดตัวไปอีก
แสดงการทำงานดังรูป

Screen Shot 2558-11-26 at 11.24.26 AM

การแก้ไขปัญหานี้คือ ก็ให้แยก queue ของการทำงานให้ทำงานคนละ Thread ไป
นั่นคือ

  • Network task queue
  • Local task queue

ส่งผลให้ app ของเราทำงานได้รวดเร็ว (Responsive)
แสดงการทำงานดังรูป

Screen Shot 2558-11-26 at 11.28.45 AM

สามารถสรุปการทำงานในส่วนของ Activity สำหรับการ Notify ได้ดังนี้ ( Activity State Machine )

  • onCreate() ทำการ setup พวก User Interface ต่าง ๆ
  • onStart() ทำการ register event เช่น การดึงข้อมูล
  • เมื่อเกิด event ขึ้นมา หรือ onEvent() หรือ ดึงข้อมูลเสร็จแล้ว จะทำการ refesh ข้อมูลใหม่
  • onStop() ทำการ unregister event

ซึ่งจะเห็นได้ว่ามันเรียบง่าย
และง่ายต่อการทำความเข้าใจอย่างมาก

Key Takeaway มีดังนี้

  • Design for offline เนื่องจากเรื่องของ network มันไม่เคยเป็นมิตรกับใครเลย
    • ให้ส่วนของ User Interface ใช้ข้อมูลจาก model
    • ส่วนของ network และการ update model ให้ Application Logic จัดการ
    • ให้แต่ละส่วนทำงานเป็นอิสระจากกัน อาจจะใช้งาน Event, Callback หรืออะไรก็ได้ที่จำเป็น
  • Decoupling แต่ละส่วนแยกกันทำงาน
    • สามารถนำ Dependency Injection มาใช้งานได้
    • มันมีทั้งข้อดีและข้อเสีย ต้องทำความเข้าใจก่อน ไม่ใช่ทำตาม ๆ กันไป
    • อย่าใช้ Reflection API โดยเด็ดขาด เพราะว่ามันทำให้ performance ต่ำลงไปมาก ดังนั้นเลือก library ให้ระวังกันด้วย
  • Design API
    • ออกแบบ API เพื่อฝั่ง client หรือ ผู้ใช้งานนะ
    • ให้ฝั่ง client ทำงานน้อยที่สุด
    • ให้ฝั่ง server ทำงานมากที่สุด
  • Act locally, Sync Globally !!

และยังมีเรื่องอื่น ๆ อีกมากมาย
ลองดูต่อจาก VDO ข้างต้นได้เลยครับ
มีสิ่งที่น่าสนใจให้เราเรียนรู้อีกมากมาย

Tags: