Modern Android Architectures – MVC/MVP/MVVM – Phần 2: Kiến Trúc MVC

Posted by
Rating: 5.0/5. From 7 votes.
Please wait...

Chào mừng các bạn đã đến với loạt bài viết về chủ đề Modern Android Architectures.

phần trước chúng ta đã cùng nhau tìm hiểu về tổng quan các mô hình Kiến trúc trong lập trình Android rồi. Dựa trên những điều sơ khởi đó chúng ta còn cùng nhau xây dựng một ứng dụng thực tế, được xây dựng dựa trên một Kiến trúc custom, tức là theo cách tổ chức lớp lang theo kinh nghiệm của bản thân.

Do project sơ khởi của chúng ta khá nhỏ, số lượng lớp rất ít, nên bạn cũng sẽ khó hình dung được tại sao phải cần đến một Kiến trúc chuẩn nào đó thay vì custom. Bạn chỉ có thể tưởng tượng rằng tương lai ứng dụng của bạn sẽ có thể lên tới vài chục màn hình, và do đó số lượng các Activity hay Fragment cũng sẽ nhiều tương ứng. Rồi từ đó các lớp liên quan đến logic của ứng dụng cũng xuất hiện ngày càng nhiều. Có thể lên đến hàng trăm lớp trong ứng dụng của bạn. Mà chưa kể mỗi lớp như vậy có thể lên tới hàng ngàn dòng code. Thì khi đó việc tìm kiếm, chỉnh sửa, cũng như thêm mới các dòng code hoặc các lớp sẽ trở nên đau đầu đến nhường nào.

Vậy nên chúng ta sẽ rất cần tìm hiểu các cách thức chung nhất, các quy tắc rõ ràng cho chính bản thân chúng ta, và cho những thành viên khác trong team, cùng hướng tới, để tạo nên một sự nhất quán trong project. Hôm nay bạn thể hiện rõ ràng kiến trúc của ứng dụng với một màn hình hiển thị danh sách các quốc gia. Thì hôm sau bạn hoặc đồng nghiệp cũng sẽ dễ dàng thêm vào màn hình, API, cũng như logic cho chức năng tiếp theo của ứng dụng. Ví dụ như, hiển thị thông tin đầy đủ của từng quốc gia, bao gồm cả thể hiện biểu đồ chẳng hạn. Và hôm sau nữa ứng dụng lại có thêm chức năng hiển thị bản đồ thế giới, chức năng hiển thị Maps,… Các chức năng được thêm vào theo một nguyên tắc, một trật tự ngăn nắp theo ý bạn. Để làm được vậy mời bạn cùng bắt đầu với MVC.

Giới Thiệu Về Mô Hình MVC

Theo như mình biết, thì MVC sinh ra không phải dành cho phát triển phần mềm Android. MVC có trước khi Android xuất hiện, khi đó MVC được tạo ra để giải quyết các vấn đề về kiến trúc cho lập trình Web. Sau đó MVC được ứng dụng trên các ứng dụng desktop khác.

Sau này khi công nghệ lập trình Android phát triển, MVC được các lập trình viên đưa vào Android để thử nghiệm. Dù cho MVC khi đó có thể không hoàn toàn phù hợp với Android, nhưng nó đã mở đầu và đã trở thành nền móng cho các sáng kiến, để sau đó, các mô hình kiến trúc phù hợp hơn với Android sau này ra đời, như MVP hay MVVM mà chúng ta sẽ nói đến ở bài tiếp theo.

Quy Tắc MVC

MVC là viết tắt của 3 chữ: Model – View – Controller.

3 chữ này đại diện cho 3 layer. Các lập trình viên sẽ phải hiểu nguyên tắc của từng layer, để mà tạo ra các lớp tương ứng với các layer đó. Lớp nào thuộc layer nào sẽ tuân thủ theo nguyên tắc của layer đó. Việc đặt tên lớp hay package/directory có thể bao hàm cả tên layer để dễ hiểu và dễ quản lý. Dưới đây là chức năng cụ thể của từng layer.

  • Model: Layer này sẽ chứa các lớp liên quan đến lưu trữ dữ liệu, hay đảm nhiệm xử lý các nghiệp vụ logic của ứng dụng. Bạn tưởng tượng model giống như bộ não của con người, nó giúp xử lý và lưu trữ dữ liệu.
  • View: layer này sẽ chứa các lớp liên quan đến hiển thị, và nhận tương tác từ phía người dùng. Nếu tưởng tượng đến các cơ quan của con người, thì view chính là các giác quan, giúp “nghe”, “ngửi”, “nếm”, “nhìn”, “cảm giác” từ bên ngoài rồi chuyển vào cho model xử lý. Đồng thời có thể “nói” ra môi trường bên ngoài sau khi nhận kết quả xử lý từ model. Thực sự thì view nên trung lập nhất có thể, nó chỉ có thể nhận dữ liệu vào, và hiển thị dữ liệu ra, nó không có “cảm xúc” hay xử lý logic gì cả. Tất cả những gì nó cần làm là lấy dữ liệu từ người dùng rồi gọi đến controller hoặc model để thực hiện tiếp các tác vụ. Rồi sau đó view cũng sẽ hiển thị kết quả sau khi nhận được xử lý từ controller.
  • Controller: Layer này chứa các lớp đảm nhận vai trò là cầu nối giữa viewmodel. Những tương tác của người dùng từ view sẽ được controller chuyển đến model. Và ngược lại, những thay đổi từ model sẽ được controller cập nhật lên view. Như vậy controller giống như các liên kết giúp dẫn truyền “cảm giác” đến não và ngược lại.

Bạn đã bắt đầu thấy sự rõ ràng trong việc phân tách các nhiệm vụ của các lớp trong Android vào các nhóm chức năng chưa nào. Bắt đầu thú vị rồi đúng không. Để minh họa cho các layer trên, người ta vẽ ra một sơ đồ cho MVC như sau.

Sơ đồ kiến trúc MVC
Sơ đồ kiến trúc MVC

Chúng ta hãy dành để nói đến các ưu điểm và khuyết điểm của mô hình MVC này ở cuối bài, khi mà tất cả đều rõ ràng MVC là gì sau khi áp dụng vào thực tế xây dựng ứng dụng Android ở phần kế tiếp.

MVC Trong Android

Tại sao lại nói thêm ý ở mục này? Vì như mình có nói, MVC thực chất sinh ra không phải cho riêng lập trình Android. Mà chính các lập trình viên Android lúc bấy giờ mong muốn áp dụng MVC vào Android để biến việc lập trình này trở nên đúng chuẩn với những gì mà cộng đồng lập trình viên khác đang dùng.

Chính vì vậy nên việc áp dụng MVC vào Android rất khác với các nền tảng lập trình Web cũng như desktop. Một câu hỏi rất lớn, và gây nhiều tranh cãi trong việc áp dụng này, là Activity, Fragment, hay các lớp liên quan đến View khác sẽ nằm trong layer nào theo mô hình MVC? Trong quá trình tìm hiểu mình nhận ra có 2 trường phái lớn trong nhận định này.

Có trường phái cho rằng Activity, Fragment hay các lớp liên quan đến View trong Android vừa thuộc layer view vừa đảm nhiệm luôn vai trò controller. Chính vì vậy mà các lớp này vừa chứa đựng các chức năng hiển thị UI, tương tác với người dùng, và làm các thao tác kết nối với model. Thực sự thì mình nhận thấy sở dĩ có trường phái này là bởi vì nó hoàn toàn giống với cách tổ chức theo Kiến trúc custom mà chúng ta đã thử làm quen ở bài trước, và vì vậy mà lập trình viên Android khi này không cần phải chỉnh sửa project quá nhiều để có được một MVC. Chỉ cần tách một số xử lý liên quan đến lưu trữ, hay logic của ứng dụng vào Model là được.

Trường phái còn lại thì khắt khe hơn khi giới hạn Activity, Fragment hay các lớp liên quan đến View trong Android vào trọng trách của layer view, tức là chỉ nhận tương tác từ người dùng, và phản hồi lại người dùng, thế thôi. Model vẫn như trường phái trên. Controller thì tách ra thành các lớp riêng, lo việc kết nối giữa view với model. Việc tổ chức theo trường phái này theo mình là rõ ràng hơn, và có thể dễ dàng xây dựng các Unit Test cho từng layer một cách dễ dàng hơn. Và phần xây dựng ứng dụng bên dưới mình sẽ hướng theo trường phái này.

Ngoài ra còn có các trường phái nhỏ khác như view chính là các file XML. Activity, Fragment, các lớp View khác là controller. Rồi model thì có model bị động (chỉ trả dữ liệu về cho view khi được yêu cầu) hay model chủ động (tự động gọi cập nhật dữ liệu trên view dựa vào observer). Tuy nhiên mình không nói quá nhiều về các kiểu MVC Android này. Mình chỉ kể ra cho các bạn đủ thấy rằng áp dụng MVC vào Android vẫn là một cuộc chiến, và mỗi người, mỗi team sẽ có một MVC riêng phù hợp với nhu cầu của họ hơn. Rất khó để đưa ra kết luận MVC Android nào là đúng chuẩn MVC nhất.

Bắt Tay Xây Dựng Ứng Dụng

MVC còn khá nhiều cách áp dụng khác nhau như mình có nói trên đây, thì mình vẫn sẽ chọn ra một cách thức phù hợp nhất để chúng ta cùng nhau xây dựng để có thể hiểu rõ hơn về mô hình Kiến trúc này.

Nếu bạn chưa xây dựng ứng dụng mẫu từ bài trước, thì mình khuyên bạn nên bắt tay vào xây dựng nhanh. Hoặc bạn có thể lấy nhanh source code từ Github của bài hôm trước theo link này. Dựa trên ứng dụng của bài trước, chúng ta sẽ chỉnh sửa lại, hay kỹ thuật gọi là refactor code, để trở thành kiến trúc MVC của bài hôm nay.

Nào giờ hãy mở project ModernAndroidArchitectures ra.

Tổng Quan Project

Cái này mình giới thiệu lại project, đã được nói tới ở mục này của bài trước rồi.

Project của tất cả bài viết trong chủ đề Modern Android Architectures này đều có chung một kết quả màn hình như sau.

Màn hình ứng dụng mẫu
Màn hình ứng dụng mẫu

Ứng dụng sẽ kết nối đến Web Service để lấy về danh sách các quốc gia kèm thủ đô của nó. Web Service này được xây dựng sẵn trên trang restcountries.eu. Ngoài việc hiển thị danh sách các quốc gia, ứng dụng còn có chức năng tìm kiếm theo tên quốc gia. Khi click vào bất kỳ quốc gia nào trên danh sách sẽ hiển thị một message dạng Toast.

Tổng Quan MVC Trong Project Này

Trước hết hãy cùng xem lại các lớp và cách tổ chức chúng vào các package của bài trước sẽ như thế này.

Kiến trúc Custom của bài trước
Kiến trúc Custom của bài trước

Với MVC thì dĩ nhiên để dễ nhìn nhất, chúng ta nên tạo ra các package với các tên gọi tương ứng, là view, model, và controller. Rồi để các lớp tương ứng vào. Với project chúng ta cần xây dựng thì các layer MVC sẽ chứa đựng các lớp sau.

Các lớp trong project tương ứng với MVC
Các lớp trong project tương ứng với MVC

Bạn lưu ý là MainActivity ở bài trước sẽ được chuyển thành CountriesActivity ở bài này, để cho cái tên của nó được nhất quán (vì màn hình này là hiển thị các danh sách các quốc gia, nên các lớp phục vụ cho hiển thị, cũng như logic của màn hình này đều bắt đầu bằng cái tên Country hoặc Countries). Chúng ta sẽ bắt tay vào thay đổi MainActivity thành CountriesActvity ở mục bên dưới sau nhé.

Nhưng như bạn cũng thấy đó, câu hỏi đặt ra là các lớp liên quan đến kết nối Web Service, như CountriesApi, hay CountriesService, chúng đâu rồi.

Thực ra thì khi xây dựng các kiến trúc này, bạn cũng đừng cứng nhắc quá. Có một số lớp hay package vốn dĩ nó không thuộc về view, model hay controller. Như các lớp kết nối với Web Service trên đây. Vì chúng không làm nhiệm vụ hiển thị UI, hay tương tác với người dùng, hay lưu trữ dữ liệu, hay xử lý nghiệp vụ logic, hay cầu nối giữa viewmodel gì cả. Chúng “độc lập” về chức năng với view, model, và controller. Hay sau này bạn có thêm các lớp khác, như Util, Config, Constant,… chẳng hạn. Thì các lớp này vẫn nên được tổ chức vào các package riêng khác, như package networking với project này, hoặc util gì đó cho các project khác của bạn. Bạn đã hiểu chưa nào.

Nói như vậy có nghĩa là package networking và các lớp bên trong package này mình sẽ giữ nguyên như project xây dựng ở bài trước.

Xây Dựng Layer Model

Bạn có nhớ là bài trước chúng ta xây dựng một lớp Country trong package model rồi đúng không nào. Theo như mô hình MVC thì lớp thuộc layer model sẽ chịu trách nhiệm chính trong việc quản lý dữ liệu, quản lý nghiệp vụ logic của ứng dụng.

Nhưng với project ví dụ này thì ứng dụng của chúng ta chẳng có mấy logic gì cả, trừ một điều là khi nhấn vào một item trên danh sách các quốc gia ở CountriesActivity thì một message dạng Toast hiện ra. Với bài trước thì message này khá đơn giản nên mình cho Activity tự hiển thị. Nhưng với bài học hôm nay, mình giao cho Country một trọng trách nặng nề hơn, đó là lo luôn nội dung của message, mình xem tác vụ soạn nội dung message này là một nghiệp vụ logic của ứng dụng. Sau này các bạn có các nghiệp vụ phức tạp hơn, như xử lý thông tin về dân số, diện tích, đưa ra các nhận định về phát triển dân số, hay các phương thức lưu trữ thông tin quốc gia vào máy chẳng hạn, thì cứ để model Country làm cho nhé.

Cũng có một lưu ý rằng lớp Country ở bài học trước sẽ được mình đổi tên thành CountryModel ở bài hôm nay như quy luật đặt tên mình nói đến trên kia.

Để thay đổi tên mộp lớp thì bạn hãy click chuột phải vào lớp đó trong cửa sổ Project, rồi chọn Refactor > Rename….

Sửa tên lớp

Cửa sổ tiếp theo xuất hiện thì bạn hãy điền vào tên lớp mới, rồi nhấn Refactor. Tiếp đó nếu có popup nào hiện ra thì bạn cứ nhấn OK nữa thôi.

Sửa tên lớp
Sửa tên lớp

Và code của CountryModel như sau. Bạn thấy có thêm một phương thức lo logic cho ứng dụng được mình tô sáng lên.

data class CountryModel(val name: String, val capital: String) {

    fun getCountryInfo() = "Country $name, capital is $capital clicked"
}

Xây Dựng Layer Controller

Đến đây thì bạn phải tạo mới một package có tên controller vì bài hôm trước chưa có package này. Hình ảnh sau khi tạo thêm package controller như sau.

Kiến trúc project khi thêm package controller

Sau đó hãy tạo mới một lớp có tên CountriesController trong package controller này.

Bạn cũng đã biết, trong mô hình MVC, lớp thuộc layer controller sẽ làm nhiệm vụ kết nối giữa viewmodel. Và một số kết nối đến các package khác nếu cần. Việc kết nối này cũng nhằm giúp giảm tải một số xử lý từ view. Trong project của chúng ta hôm trước thì các phương thức kết nối đến Web Service chính là vai trò mà CountriesController sẽ đảm nhận trong bài hôm nay. Bạn có thể xây dựng thêm các kết nối sau này trong CountriesController, như gọi đến CountryModel để lưu dữ liệu, rồi còn lấy dữ liệu cho CountriesActivity,…

Vậy bạn hãy mang các khai báo và các xử lý liên quan đến Web Service vào bên trong CountriesController này như sau.

class CountriesColtroller {

    private var apiService: CountriesApi? = null

    constructor() {
        apiService = CountriesService.create()
    }

    fun onFetchCountries() {
        apiService?.let {
            it.getCountries()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe({ result ->
                    // Trả về kết quả thành công, sẽ làm sau
                }, { error ->
                    // Trả về kết quả thất bại, sẽ làm sau
                })
        }
    }
}

Bạn có thể so sánh một chút với MainActivity đã xây dựng ở bài trước để thấy việc giảm tải bằng cách mang onFetchCountries() vào trong CountriesController này. Việc làm này của CountriesController giúp lớp này và MainActivity có quan hệ với nhau. MainActivity là một view sẽ không còn phải lo lấy dữ liệu nữa, mà MainActivity sẽ “nhờ” CountriesController giúp. Thực ra thì CountriesController cũng chỉ làm nhiệm vụ giao tiếp, giúp khai báo và gọi tới CountriesApi để thực hiện việc lấy dữ liệu các quốc gia về. Rồi CountriesController cũng sẽ gọi đến CountryModel để lớp này lo xử lý dữ liệu hoặc lưu trữ nếu có mà thôi. Bạn đã thấy vai trò cầu nối của ContriesController rồi đúng không nào.

Và kiến trúc của project đến bước này như sau.

Kiến trúc của project đến giai đoạn này
Kiến trúc của project đến giai đoạn này

Xây Dựng Layer View

Như mình đã nói trên kia, mình đi theo hướng view trong mô hình MVC cho Android sẽ chứa các lớp liên quan đến giao diện, như Activity, Fragment, Dialog hay các View khác.

Do đó package view của mình sẽ chứa các lớp như thế này. Bạn có thể xây dựng package view, rồi trong đó phân cấp ra thành các package con khác như activity, fragment, dialog, adapter,… khi project đã quá lớn rồi nhé.

À, thực ra chúng ta không cần tạo mới một package nào cả, mình sẽ đổi package activity của bài trước thành view của bài hôm nay. Để làm như vậy thì tương tự như khi bạn đổi tên lớp, bạn hãy click chuột phải vào package activity rồi chọn Refactor > Rename…

Đổi tên package activity thành view
Đổi tên package activity thành view

Cũng tương tự như việc bạn đổi tên lớp, popup tiếp theo xuất hiện bạn hãy gõ view.

Đổi tên package
Đổi tên package

Sau đó hãy đổi MainActivity thành CountriesActivity như chúng ta đã nói đến trên kia nhé.

Còn CountriesAdapter? Như mình cũng có nói, lớp này thuộc layer view. OK, kéo lớp này vào view. Sau đó xóa package adapter bằng cách click chuột phải vào package này và chọn Delete.

Xóa một package
Xóa một package

Và cấu trúc của project chúng ta sẽ như sau.

Cấu trúc project đến bước này
Cấu trúc project đến bước này

Việc tiếp theo cần làm là chỉnh sửa lại CountriesActivity bằng cách xóa các dòng code liên quan đến việc tự khai báo và kết nối API, rồi gọi đến CountriesController để nhờ lớp này lo phần này.

Lớp CountriesActivity sẽ được sửa như sau. Mình sẽ tô sáng vài chỗ quan trọng, bạn nhìn vào sẽ hiểu ngay thôi. Mình sẽ giải thích một tí bên dưới.

class CountriesActivity : AppCompatActivity() {

    lateinit var countriesController: CountriesController
    private val countriesAdapter = CountriesAdapter(arrayListOf())
    private var countries: List<CountryModel> = listOf()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        countriesController = CountriesController()

        listView?.apply {
            layoutManager = LinearLayoutManager(context)
            adapter = countriesAdapter
        }
        countriesAdapter.setOnItemClickListener(object : CountriesAdapter.OnItemClickListener {
            override fun onItemClick(country: CountryModel) {
                Toast.makeText(this@CountriesActivity, "Country ${country.name}, capital is ${country.capital} clicked", Toast.LENGTH_SHORT).show()
            }
        })

        searchField.addTextChangedListener(object : TextWatcher {

            override fun afterTextChanged(s: Editable) {
                if (s.isNotEmpty()) {
                    val filterCountries = countries?.filter { country ->
                        country.name.contains(s.toString(), true)
                    }
                    filterCountries?.let { countriesAdapter.updateCountries(it) }
                } else {
                    countries?.let { countriesAdapter.updateCountries(it) }
                }
            }

            override fun beforeTextChanged(
                s: CharSequence, start: Int,
                count: Int, after: Int
            ) {
            }

            override fun onTextChanged(
                s: CharSequence, start: Int,
                before: Int, count: Int
            ) {
            }
        })

        onFetchCountries()
    }

    fun onFetchCountries() {
        listView.visibility = View.GONE
        progress.visibility = View.VISIBLE
        searchField.isEnabled = false

        countriesController.onFetchCountries()
    }

    fun onSuccessful(result: List<CountryModel>) {
        listView.visibility = View.VISIBLE
        progress.visibility = View.GONE
        searchField.isEnabled = true

        countries = result
        countriesAdapter.updateCountries(countries)
    }

    fun onError() {
        listView.visibility = View.GONE
        progress.visibility = View.GONE
        searchField.isEnabled = false

        Toast.makeText(this, "Error", Toast.LENGTH_SHORT).show()
    }
}

Đầu tiên bạn có thể thấy mình bỏ đi việc khai báo thuộc tính CountriesApi. Vì mình đã chuyển thuộc tính này vào trong lớp CountriesController rồi. Do đó biến CountriesController sẽ được thay thế cho CountriesApi chỗ này.

Tương tự cho việc thay thế này ở dòng khai báo CountriesController trong phương thức onCreate().

onFetchCountries() lúc này CountriesActivity chỉ việc “nhờ” CountriesController làm việc kết nối đến Web Service dùm. Thế thôi, cho code của Activity được ngắn bớt.

À mà bởi vì nhờ CountriesController lo việc gọi API, nên CountriesActivity nên làm thêm phương thức onSuccessful(), phương thức này chứa các code liên quan đến lời gọi thành công từ Web Service, chúng ta tách nó ra để CountriesController gọi về sau đó mà thôi.

Nhưng khoan, project vẫn còn thiếu cái gì đó. Đúng rồi, chúng ta chưa thực sự xây dựng hoàn chỉnh sự kết nối từ CountriesActivityCountriesController. Cụ thể khi mà CountriesController gọi Web Service thành công, nó sẽ update dữ liệu này đến CountriesActivity thế nào? Để trả lời cầu hỏi này thì mình mời bạn mở lại CountriesController để cùng thêm vào một số thay đổi sau.

class CountriesController {

    private var view: CountriesActivity
    private var apiService: CountriesApi? = null

    constructor(view: CountriesActivity) {
        this.view = view
        apiService = CountriesService.create()
    }

    fun onFetchCountries() {
        apiService?.let {
            it.getCountries()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe({ result ->
                    view.onSuccessful(result)
                }, { error ->
                    view.onError()
                })
        }
    }
}

Sự thay đổi này có nghĩa chúng ta sẽ truyền CountriesActivity vào trong CountriesController luôn. Sau khi kết thúc việc gọi API, CountriesController sẽ gọi về lại cho CountriesActivity cập nhật lại UI thông qua các lời gọi view.onSuccessful()view.onError().

Sự thay đổi này có nhiều điều để nói. Mình muốn nói thêm cho bạn hiểu hơn về việc tổ chức này của MVC.

  • Thứ nhất, lỡ đâu chúng ta muốn dùng chung một CountriesController cho nhiều view khác nhau chứ không riêng gì CountriesActivity, thì việc truyền vào này sẽ gây nên lỗi compiler, CountriesController không chịu chấp nhận bất kỳ lớp nào khác ngoài CountriesAcvitiy. Để giải quyết vấn đề này chúng ta có thể dùng đến một lớp Activity cha. Rồi CountriesActivity hay bất kỳ Activity nào dùng chung CountriesController cũng phải kế thừa Activity cha này. Sau đó ở tham số truyền vào hàm khởi tạo của CountriesController chúng ta chỉ truyền lớp Activity cha thôi, khi gọi kết quả trả về CountriesController sẽ biết Activity con nào gọi đến để mà trả kết quả về cho đúng Activity con đó.
  • Thứ hai, bạn lo lắng về việc lỗi liên quan đến leak memory. Chẳng hạn khi bạn truyền view vào cho controller xong rồi gọi kết nối API, trước khi controller nhận kết quả trả về thì view bị hệ thống hủy bỏ (người dùng tắt ứng dụng, hoặc chuyển sang view khác), thì việc gọi trả kết quả về sau đó sẽ gây lỗi. Để giải quyết vấn đề này bạn nên check view khác null trước khi trả về, hoặc có thể tạo Interface và truyền vào controller như một listener lắng nghe kết quả trả về thay vì truyền vào đó là một view. Chà lung tung nhỉ nhưng mình nghĩ là bạn hiểu.
  • Thứ ba, đây là một nâng cấp chứ không phải lo lắng. Đó là thay vì truyền vào controller là một view, bạn có thể xây dựng các cấu trúc chủ động hơn trong việc cập nhật dữ liệu cho view sau khi dữ liệu đã được gọi thành công, bằng Observer chẳng hạn. Sẽ tuyệt hơn nhiều đấy.

Dù sao thì chúng ta cũng đã đến gần đích lắm rồi, hãy thay đổi hai chỗ ở CountriesActivity, một là truyền this vào CountriesController vì thay đổi ở controller như trên kia. Hai là gọi đến CountryModel nhờ “soạn” giúp câu chữ in ra vì CountryModel “am hiểu” rất rõ dữ liệu và nghiệp vụ logic của nó. Xong rồi thực thi ứng dụng nào.

class CountriesActivity : AppCompatActivity() {

    // Code chỗ này không quan tâm, mình ẩn đi

    override fun onCreate(savedInstanceState: Bundle?) {
        // Code chỗ này không quan tâm, mình ẩn đi

        countriesController = CountriesController(this)

        // Code chỗ này không quan tâm, mình ẩn đi
        
        countriesAdapter.setOnItemClickListener(object : CountriesAdapter.OnItemClickListener {
            override fun onItemClick(country: CountryModel) {
                Toast.makeText(this@CountriesActivity, country.getCountryInfo(), Toast.LENGTH_SHORT).show()
            }
        })

        // Code chỗ này không quan tâm, mình ẩn đi
    }

    // Code chỗ này không quan tâm, mình ẩn đi
}

Kết Luận

Mình hi vọng bài viết nhỏ này giúp bạn hiểu rõ hơn về mô hình Kiến trúc MVC. Mặc dù kiến trúc này có vẻ lỗi thời, hầu như các project Android mới làm bây giờ không ai tổ chức theo MVC cả. Một phần vì sự phân chia rạch ròi nhiệm vụ giữa các layer chưa cao, sự liên hệ và phụ thuộc vào nhau rất nhiều giữa các layer có thể khiến ứng dụng Android trở nên mỏng manh và dễ crash hơn sau này (dù mục đích Unit Test hay tổ chức code cho dễ đọc và bảo trì hơn chắc chắn là có các tác dụng tốt). Nhưng MVC vẫn được các lập trình viên Android chúng ta nhắc đến để làm nền tảng cho các Kiến trúc sau này. MVC sẽ luôn hiện diện trong các kiến trúc sau, chỉ là có sự thay đổi sao cho phù hợp hơn với Android mà thôi. Mặt khác thì tìm hiểu MVC cũng giúp bạn dễ dàng đọc hiểu các source code của các project cũ còn viết theo mô hình này.

Download Source Code Mẫu

Bạn có thể download source code mẫu của bài này ở đây.

Cảm ơn bạn đã đọc các bài viết của Yellow Code Books. Bạn hãy ủng hộ blog bằng cách:

Đánh giá 5 sao ở mỗi bài viết nếu thấy thích.
Comment bên dưới mỗi bài viết nếu có thắc mắc.
Để lại địa chỉ email của bạn ở thanh bên phải để nhận được thông báo sớm nhất khi có bài viết mới.
Chia sẻ các bài viết của Yellow Code Books đến nhiều người khác.
Ủng hộ blog theo hướng dẫn ở thanh bên phải để blog ngày càng phát triển hơn.

6 comments

  1. Cảm ơn anh, bài viết rất hay và chi tiết. Hi vọng sẽ sớm có phần 3.

    Rating: 5.0/5. From 2 votes.
    Please wait...
  2. huhu anh mau ra MVVM đi anh ơi

    Rating: 5.0/5. From 2 votes.
    Please wait...
  3. Mình cũng đang hóng bạn ra mắt bài viết về MVVM.
    Về MVC thì mình cũng đã hiểu đôi chút, và có note lại tại đây https://vntalking.com/giai-thich-de-hieu-ve-mvc-mo-hinh-mvc-la-gi.html
    Mong bạn chỉ giáo ạ.

    Rating: 5.0/5. From 2 votes.
    Please wait...
  4. MVP và MVVM đi anh ơi, bữa nào củng ghé xem update

    Rating: 5.0/5. From 1 vote.
    Please wait...
  5. ra MVVM đi a ơi, hóng hoài luôn

    No votes yet.
    Please wait...
  6. Mong anh ra video tiếp theo

    No votes yet.
    Please wait...

Leave a Reply