Шаблон архитектуры MVC (Model View Controller) в Android с примером
Разработчики всегда предпочитают разработку приложения для Android с применением шаблона архитектуры программного обеспечения. Шаблон архитектуры придает модульность файлам проекта и гарантирует, что все коды будут охвачены модульным тестированием. Это облегчает разработчикам задачу по сопровождению программного обеспечения и расширению функций приложения в будущем. Есть несколько архитектур, которые очень популярны среди разработчиков, и одна из них - это шаблон «Модель — Представление — Контроллер» (MVC). Шаблон MVC предлагает разделить код на 3 компонента. При создании класса / файла приложения разработчик должен разделить его на один из следующих трех уровней:
- Модель: этот компонент хранит данные приложения. Он ничего не знает об интерфейсе. Модель отвечает за обработку логики предметной области (бизнес-правила реального мира) и взаимодействие с базой данных и сетевыми уровнями.
- Просмотр: Это это Слой UI (пользовательский интерфейс), содержащий компоненты, видимые на экране. Кроме того, он обеспечивает визуализацию данных, хранящихся в модели, и предлагает взаимодействие с пользователем.
- Контроллер: этот компонент устанавливает связь между представлением и моделью. Он содержит основную логику приложения, получает информацию о поведении пользователя и обновляет Модель в соответствии с потребностями.

Несмотря на применение схемы MVC для создания модульного дизайна приложения, уровни кода зависят друг от друга. В этом шаблоне представление и контроллер зависят от модели. Возможны несколько подходов к применению шаблона MVC в проекте:
- Подход 1. Действия и фрагменты могут выполнять роль Контроллера и отвечать за обновление представления.
- Подход 2: используйте активность или фрагменты в качестве представлений и контроллера, в то время как модель будет отдельным классом, который не расширяет какой-либо класс Android.
В архитектуре MVC данные приложения обновляются контроллером, и View получает данные. Поскольку компонент модели отделен, его можно тестировать независимо от пользовательского интерфейса. Кроме того, если представление слоя отношений единого принцип ответственности , то их роль как раз для обновления контроллера для каждого события пользователя и только отображать данные из модели, без осуществления какой - либо бизнес - логики. В этом случае тестов пользовательского интерфейса должно быть достаточно, чтобы охватить функциональные возможности представления.
Пример архитектуры MVC
Чтобы лучше понять реализацию шаблона архитектуры MVC, вот простой пример приложения для Android. Это приложение будет иметь 3 кнопки, и каждая из них отображает счетчик того, сколько раз пользователь нажимал эту конкретную кнопку. Для разработки этого приложения код был разделен следующим образом:
- Контроллер и представление будут обрабатываться Activity. Каждый раз, когда пользователь нажимает кнопки, действие направляет Модель для обработки дальнейших операций. Активность будет выступать в роли наблюдателя .
- Модель будет отдельным классом, который будет содержать отображаемые данные. Операции с данными будут выполняться функциями этого класса, и после обновления значений данных этот класс Observable уведомляет Observer (Activity) об изменении.
Ниже приведена полная пошаговая реализация этого приложения для Android с использованием шаблона архитектуры MVC:
Note: Following steps are performed on Android Studio version 4.0
Шаг 1. Создайте новый проект
- Щелкните Файл, затем Новый => Новый проект.
- Выберите Пустое действие
- Выберите язык как Java / Kotlin
- Выберите минимальный SDK в соответствии с вашими потребностями.
Шаг 2. Измените файл String.xml
Все строки, которые используются в упражнении, перечислены в этом файле.
XML
< resources > < string name = "app_name" >GfG | MVC Architecture</ string > < string name = "Heading" >MVC Architecture Pattern</ string > < string name = "Text1" >Button_1</ string > < string name = "count" >Count:0</ string ></ resources > |
Шаг 3. Работа с файлом activity_main.xml
Откройте файл activity_main.xml и добавьте к нему 3 кнопки, которые будут отображать значения счетчика в соответствии с количеством нажатий пользователем на нем. Ниже приведен код для разработки правильного макета занятия.
XML
<?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="#168BC34A" tools:context=".MainActivity" > <!-- Provided Linear layout for the activity. --> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <!-- TextView to display heading of the activity. --> <TextView android:id="@+id/textView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="40dp" android:layout_marginBottom="60dp" android:fontFamily="@font/roboto" android:text="@string/Heading" android:textAlignment="center" android:textColor="@android:color/holo_green_dark" android:textSize="30sp" android:textStyle="bold" /> <!-- First Button of the activity. --> <Button android:id="@+id/button" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="20dp" android:layout_marginTop="30dp" android:layout_marginEnd="20dp" android:layout_marginBottom="20dp" android:background="#4CAF50" android:fontFamily="@font/roboto" android:text="@string/count" android:textColor="@android:color/background_light" android:textSize="24sp" android:textStyle="bold" /> <!-- Second Button of the activity. --> <Button android:id="@+id/button2" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="20dp" android:layout_marginTop="50dp" android:layout_marginEnd="20dp" android:layout_marginBottom="20dp" android:background="#4CAF50" android:fontFamily="@font/roboto" android:text="@string/count" android:textColor="@android:color/background_light" android:textSize="24sp" android:textStyle="bold" /> <!-- Third Button of the activity. --> <Button android:id="@+id/button3" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="20dp" android:layout_marginTop="50dp" android:layout_marginEnd="20dp" android:layout_marginBottom="20dp" android:background="#4CAF50" android:fontFamily="@font/roboto" android:text="@string/count" android:textColor="@android:color/background_light" android:textSize="24sp" android:textStyle="bold" /> <ImageView android:id="@+id/imageView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="30dp" app:srcCompat="@drawable/banner" /> </LinearLayout></androidx.constraintlayout.widget.ConstraintLayout> |
Шаг 4: Создание класса модели
Создайте новый класс с именем Model, чтобы разделить все данные и их операции. Этот класс не будет знать о существовании View Class.
Джава
import java.util.*; public class Model extends Observable { // declaring a list of integer private List<Integer> List; // constructor to initialize the list public Model(){ // reserving the space for list elements List = new ArrayList<Integer>( 3 ); // adding elements into the list List.add( 0 ); List.add( 0 ); List.add( 0 ); } // defining getter and setter functions // function to return appropriate count // value at correct index public int getValueAtIndex( final int the_index) throws IndexOutOfBoundsException{ return List.get(the_index); } // function to make changes in the activity button's // count value when user touch it public void setValueAtIndex( final int the_index) throws IndexOutOfBoundsException{ List.set(the_index,List.get(the_index) + 1 ); setChanged(); notifyObservers(); } } |
Котлин
import java.util.*import kotlin.collections.ArrayList class Model : Observable() { // declaring a list of integer val List: MutableList<Int> // constructor to initialize the list init { // reserving the space for list elements List = ArrayList( 3 ) // adding elements into the list List.add( 0 ) List.add( 0 ) List.add( 0 ) } // defining getter and setter functions // function to return appropriate count // value at correct index @Throws (IndexOutOfBoundsException:: class ) fun getValueAtIndex(the_index: Int): Int { return List[the_index] } // function to make changes in the activity button's // count value when user touch it @Throws (IndexOutOfBoundsException:: class ) fun setValueAtIndex(the_index: Int) { List[the_index] = List[the_index] + 1 setChanged() notifyObservers() }} |
Шаг 5: Определите функциональные возможности View и Controller в файле MainActivity
Этот класс устанавливает связь между представлением и моделью. Данные, предоставленные моделью, будут использоваться View, и в действие будут внесены соответствующие изменения.
Джава
import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.Button;import java.util.Observable;import java.util.Observer; public class MainActivity extends AppCompatActivity implements Observer, View.OnClickListener { // creating object of Model class private Model myModel; // creating object of Button class private Button Button1; private Button Button2; private Button Button3; @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); // creating relationship between the // observable Model and the // observer Activity myModel = new Model(); myModel.addObserver( this ); // assigning button IDs to the objects Button1 = findViewById(R.id.button); Button2 = findViewById(R.id.button2); Button3 = findViewById(R.id.button3); // transfer the control to Onclick() method // when a button is clicked by passing // argument "this" Button1.setOnClickListener( this ); Button2.setOnClickListener( this ); Button3.setOnClickListener( this ); } @Override // calling setValueAtIndex() method // by passing appropriate arguments // for different buttons public void onClick(View v) { switch (v.getId()){ case R.id.button: myModel.setValueAtIndex( 0 ); break ; case R.id.button2: myModel.setValueAtIndex( 1 ); break ; case R.id.button3: myModel.setValueAtIndex( 2 ); break ; } } @Override // function to update the view after // the values are modified by the model public void update(Observable arg0, Object arg1) { // changing text of the buttons // according to updated values Button1.setText( "Count: " +myModel.getValueAtIndex( 0 )); Button2.setText( "Count: " +myModel.getValueAtIndex( 1 )); Button3.setText( "Count: " +myModel.getValueAtIndex( 2 )); }} |
Котлин
import android.os.Bundleimport android.view.Viewimport android.widget.Buttonimport androidx.appcompat.app.AppCompatActivityimport java.util.* class MainActivity : AppCompatActivity(), Observer, View.OnClickListener { // creating object of Model class var myModel: Model? = null // creating object of Button class var Button1: Button? = null var Button2: Button? = null var Button3: Button? = null override fun onCreate(savedInstanceState: Bundle?) { super .onCreate(savedInstanceState) setContentView(R.layout.activity_main) // creating relationship between the // observable Model and the // observer Activity myModel = Model() myModel!!.addObserver( this ) // assigning button IDs to the objects Button1 = findViewById(R.id.button) Button2 = findViewById(R.id.button2) Button3 = findViewById(R.id.button3) // transfer the control to Onclick() method // when a button is clicked by passing // argument "this" Button1?.setOnClickListener( this ) Button2?.setOnClickListener( this ) Button3?.setOnClickListener( this ) } // calling setValueAtIndex() method // by passing appropriate arguments // for different buttons override fun onClick(v: View) { when (v.id) { R.id.button -> myModel?.setValueAtIndex( 0 ) R.id.button2 -> myModel?.setValueAtIndex( 1 ) R.id.button3 -> myModel?.setValueAtIndex( 2 ) } } // function to update the view after // the values are modified by the model override fun update(arg0: Observable, arg1: Any?) { // changing text of the buttons // according to updated values Button1!!.text = "Count: " + myModel!!.getValueAtIndex( 0 ) Button2!!.text = "Count: " + myModel!!.getValueAtIndex( 1 ) Button3!!.text = "Count: " + myModel!!.getValueAtIndex( 2 ) }} |