web analytics

Android Code : ลองแปลงโค้ดให้เป็น MVP pattern

cover-mvp

บทความนี้เป็นบทความสำหรับผู้เริ่มต้น ที่อยากรู้จัก MVP อาจจะเคยได้ยินมาแต่ไม่รู้จะเริ่มยังไง ผมเองก็เป็นผู้เริ่มต้นเช่นกัน เดี๋ยวเรามาลองทำกัน จะเน้นไปที่การแปลงโค้ดแบบปกติให้เป็น MVP โดยบทความนี้เป็นบทความแปลครับ โดยผมไปอ่านเจอใน medium ของคุณ Miquel เลยเอามาลองทำตาม แล้วก็มาเขียนบทความแปล สรุปตามความเข้าใจ ใครสนใจอยากอ่านต้นฉบับดูได้ที่ reference ท้ายบทความเลยนะ

Refactoring to MVP.
A way to learn MVP with real code.

 

อะไรคือ MVP

คงต้องร่ายยาวพอสมควร เกี่ยวกับ MVP แต่มีคนเขียนบทความอธิบายไว้แล้วอย่างละเอียด
ดังนั้นหากยังไม่เคยอ่าน MVP มาก่อน แนะนำให้อ่านบทความของคุณเอก เอ็กโออาซีไอเอสที  ก่อนจะดีมาก

http://www.akexorcist.com/2015/12/android-development-with-mvp-part-1.html

 

เรื่มต้น

มาดูตัวอย่าง code ของ Activity ข้างล่างนี้ ซึ่งเป็นการเรียก service มาแสดงข้อมูลใน listView โดยใช้ retrofit

 

ปัญหาคือ Activity รู้มากเกินไป

Activity เก็บโค้ดไว้กับตัวเองมากเกินไป มันรู้เกี่ยวกับการดึงข้อมูล service รู้ทั้งการรับข้อมูล การอัพเดท UI ทำให้แก้ไขภายหลังยากลำบาก  เราจึงต้องลดหน้าที่ของมันลง ให้ Activity ทำงานแค่งานของมันพอ คือรับข้อมูลแล้วแสดงผล

 

สร้าง Model

Model ก็คือข้อมูล เป็น POJO คงไม่มีอะไรมากนัก ในที่นี้ของผมคือ TransactionsDao ซึ่งก็ควรจะมีอยู่แล้ว

 

สร้าง Interactor

Interactor ทำหน้าที่ทำงานระหว่างกลาง ระหว่าง Model และ Presenter คอยดึงข้อมูลและส่งต่อข้อมูลไปยัง presenter ก่อนอื่นสร้าง Java interface class ที่ระบุพวก method ก่อนสำหรับจัดการข้อมูล แล้วค่อยสร้างคลาสของ InteractorImpl ที่ implement อีกที

ปัญหาอย่างแรกที่เห็นคือเจ้า Retrofit มันค่อนข้างเกะกะมาก ดังนั้นเราจะแยกมันออกมาเป็นคลาส Interactor
เริ่มจากสร้าง Interface class

TransactionsInteractor.java (Interface class)

 

จากนั้นก็สร้าง class implementation ผมใส่ Impl ต่อท้ายให้รู้ว่าเป็น Implement แล้วแต่สไตการเขียน ตัวอย่างนี้ทำตามเค้ามาอีกที
TransactionsInteractorImpl.java

เวลาจะใช้งาน ก็เพียง new Instance

แล้วก็เรียกผ่าน method

 

สร้าง View

ก่อนจะทำ Presenter จะต้องทำ Model กับ View ให้พร้อมก่อน เพราะ Presenter อยู่ตรงกลาง ใช้งานทั้งสองตัว

สร้าง Interface class ขึ้นมา ข้างในระบุ method ที่ view นั้นใช้ เช่น ซ่อนข้อมความนั้น แสดงปุ่มนี้ อัพเดท textview เป็นต้น
ในกรณีนี้ ก็เอาข้อมูลมาแสดงพอ

 

แล้วเอาไป implement กับ Activity หรือ Fragment ที่ต้องการ ดังนั้น Activity ก็จัดว่าเป็น View

 

แล้วก็ override method ใน Activity ก็จะได้แบบนี้
(ในที่นี้คือ set ข้อมูลไปแสดงใน listView)

 

สร้าง Presenter

Presenter จะต้องทำ 3 อย่าง

  • เชื่อมกับ Model&Interactor
  • เชื่อมกับ View
  • method ที่เกี่ยวข้อง

เริ่มจากสร้างคลาส Presenter ขึ้นมา

 

เชื่อม Presenter กับ Interactor Model

ก็แค่ประกาศตัวแปรของ interactor พร้อมกับ setter หรือ constructor ขึ้นมา

 

เชื่อม Presenter กับ View

ต่อมาก็ประกาศตัวแปร View เพิ่มใน Presenter แต่จะไม่ใช้ method setter หรือ constructor ในการกำหนดค่า มักจะใช้ method ชื่อเฉพาะแทน เช่น bind/unbind , attach/detach

 

ดังนั้น ก่อนจะใช้ Presenter ก็ต้องทำการ bind ก่อนโดยกำหนด View หรือ Activity ที่ implement view ให้มัน
ดังนั้นก็มักจะทำ bind ใน onCreate() และ unbind ตอน onDestroy เป็นต้น

 

กำหนด method ที่เกี่ยวข้อง

สุดท้ายก็กำหนดงานให้ presenter ซึ่งก็คือ เอา interactor กับ view มาใช้
ในกรณีของเราก็คือ สั่ง interactor ไปดึงข้อมูลมา และพอได้ข้อมูลก็ส่งกลับให้ view เพื่อแสดงผล ผ่าน method ของ View ดังนั้นเราจะย้าย โค้ดส่วน enqueue callback มาไว้ที่ Presenter

 

ทำการ bind View ให้กับ Presenter

กลับมาที่ MainActivity ก็ประกาศตัวแปร Presenter กำหนด interactor ให้มัน แล้วก็ bind View

 

อย่าลืม unbind ด้วย

 

สรุป MainActivity

 

สรุป

สรุป MVP เป็นการจัดระเบียบการนำเสนอของโค้ดรูปแบบหนึ่ง จะเห็นว่า MainActivity ไม่รู้อะไรเกี่ยวกับ Model เลย ไม่รู้แม้กระทั่งเกี่ยวกับ Retrofit ไม่รู้เลยว่าการ call service จะสำเร็จหรือไม่ มันมีหน้าที่แค่แสดงผลตามที่ได้รับมาเท่านั้น การทำงานหลักเกี่ยวกับ service อยู่ที่ Presenter ทั้งหมด ทำให้โค้ดใน Activity / Fragment จะกระชับขึ้น

 

หากมีข้อผิดพลาด หรือคำแนะนำ คอมเม้นไว้ได้เลยครับ (:

 

References

http://www.akexorcist.com/2015/12/android-development-with-mvp-part-1.html
https://medium.com/@Miqubel/refactoring-to-mvp-b504a3774ffd#.b8q77cdio
https://medium.com/@kenjuwagatsuma/mvp-architecture-in-android-development-3d63cc32707a#.agnr3h5r9