Android MVP architecture

Mỗi ngôn ngữ hoặc công nghệ thì sẽ có các architecture (kiến trúc) tương ứng nhằm tối ưu hóa việc code, dễ phát triển và maintain. Android cũng có các mô hình được ưa thích và khá tương đồng với web: MVC, MVVM, MVP hay mới đây có MVI, hoặc kết hợp MVVMP, kinh điển nhất vẫn là AIO (All In One) của các cháu newbie 😂😂

Đã từng làm việc với MVVM và đọc nhiều bài review đánh giá nữa MVVM và MVP, mình thấy mô hình MVVM đúng là có nổi trội hơn phần nào so với MVP. Nhưng không có nghĩa MVP đã lỗi thời và bị ruồng bỏ. Thực tế, việc xử lí nghiệp vụ có thể sẽ không chỉ sử dụng một, mà sẽ đan xen sử dụng các architecture dựa trên đánh giá của người code.

Mình là fan của Viblo cũng như Sun*, trên đây cũng có quá nhiều bài viết đã nói về mô hình này. Các bạn có thể tìm đọc của các tiền bối uyên thâm khác để có được cái nhìn sâu sắc hơn.

Quay trở lại với MVP (Model - View - Presenter), dù không muốn nhưng mình vẫn sẽ nói lại chức năng của bọn này:

  • Model: chứa đối tượng, truy cập database, logic.
    
  • Presenter: thực hiện đưa data, xử lí logic và đẩy lên view.
    
  • View: cái sẽ được hiển thị cho người dùng. Thực tế, thì cái hiển thị cho người dùng sẽ là Activity, Fragment, FragmentActivity....etc. 
    

Nhìn vào hình này bạn sẽ thấy....cái sơ đồ, và cũng méo hiểu nó chứa cái gì đâu 😂

Bây giờ để Mị nói tiếp về nguyên nhân và cách thực hiện mô hình MVP :hushed :

Ngày mới học Android, chắc chắn bạn sẽ All In One xử lí hết logic, view ở activity. Nhưng rồi chợt nhận ra cứ kiểu này không ổn, một mình mình làm thì ok chứ teamwork thì chết. Cần chuyên biệt hóa công việc để sau anh em còn maintain chứ, vậy là người ta chia ra một thằng làm logic khi nào có kết quả thì đẩy về cho thằng view. Một thằng chuyên làm view, nhận kết quả của thằng làm logic để hiển thị. Nghe có vẻ êm hơn rồi chứ 😆

Các nguyên liệu cần có cho món MvpSample:

  • HomeActivity (class)
  • HomeView (interface)
  • HomePresenter (class)
  • HomeModel (tạm thời không cần thiết đối với ví dụ này)

HomeView

    interface HomeView {
        fun showToast()  //*1
    }

HomePresenter

    class HomePresenter(val view: HomeView) {  //*2 Chưa cần để ý cái này, tí mình giải thích sau
            fun handleAndShowToast() {
                    Log.d("tag", "Hello world. Logged")
                    view.showToast() 
            }
    }

HomeActivity

    class HomeActivity: AppCompatActivity(), HomeView {
            
            private val presenter = HomePresenter(this) //*3
            
            override fun onCreateView(savedInstanceState: Bundle?) {
                    super.onCreate(savedInstanceState)
                    //setContentView nhé.
                    //Ở XML layout, các bạn tạo một button có id là btn nhé.
                    
                    btn.setOnClickListener {
                            presenter.handleAndShowToast()  //*4
                    }
            }
            
            override fun showToast() {
                Toast.makeText(this, "Hello world. I'm toast!!!")  //*5
            }
          
    }

Giải thích:

  1. Mục đích của interface sinh ra để làm cái khuôn mẫu để những thằng khác tuân theo, đồng nhất các phương thức để tối đa abstraction. Nếu như đã tìm hiểu về nguyên tắc S.O.L.I.D, thì nó sẽ là chữ D (Dependency inversion principle)

Các module cấp cao không nên phụ thuộc vào các modules cấp thấp. Cả 2 nên phụ thuộc vào abstraction. Interface (abstraction) không nên phụ thuộc vào chi tiết, mà ngược lại.
Các class giao tiếp với nhau thông qua interface, không phải thông qua implementation.)

  1. *2 và * 3 mới là mấu chốt của vấn đề. Ở presenter yêu cầu truyền vào một HomeView khi khởi tạo object, ở HomeActivity khởi tạo một đối tượng presenter nhưng tham số truyền vào là this. Tại sao lại là this, trong khi constructor của Presenter yêu cầu một HomeView ? Đó là tính đa hình của OOP, khi một function yêu cầu một object cha nhưng lại được truyền object con thì đó gọi là upcasting. HomeAcitivity đã implement HomeView nên nó là con của HomeView, do vậy việc truyền this khi khởi tạo presenter là hoàn toàn hợp lí và dễ hiểu.

  2. *4 đối tượng presenter đã được khởi tạo và hoàn toàn có thể sử dụng các phương thức bên trong.

  3. *5 chỉ đơn giản là override lại abstract fuction trong HomeView.

Hi vọng các bạn hiểu, chúc các bạn thành công ạ 😄