Шаблон архитектуры MVC (Model View Controller) в Android с примером

Опубликовано: 5 Января, 2022

Разработчики всегда предпочитают разработку приложения для 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. Создайте новый проект

  1. Щелкните Файл, затем Новый => Новый проект.
  2. Выберите Пустое действие
  3. Выберите язык как Java / Kotlin
  4. Выберите минимальный 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 
    xmlns:tools="http://schemas.android.com/tools"
    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.Bundle
import android.view.View
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
import 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 )
}
}

Выход