MVVM Pattern

MVVM模式(Model–View–ViewModel):MVVM將程式劃分為 ModelViewViewModel ,它透過觀察者模式將 ViewViewModel 連接在一起, 在 ViewModel 中進行資料綁定(data binding)。當 View Model 的狀態改變時自動通知 View

  • Model:主要負責管理資料邏輯。
  • View:負責顯示資料以及與使用者互動。
  • ViewModel:是連接 ModelView 的橋樑。與 MVC中的 Controller 和 MVP 中的 Presenter 不同在於 ViewModel 它會綁定(Binder) View 要顯示的資料,當資料改變時,自動通知 View 要更新UI。
    • 通常使用資料綁定(Data Binding)機制來實現這一點
graph TD
    A[ View ]
    B[ ViewModel ]
    C[ Model ]
    
    A -->| User Interaction | B
    B -->| Notify Changes | A
    B <-->| Data Binding | A
    B -->| Calls | C
    C -->| Data | B

Unity中,UGUI沒有內建Data Binding,需要自己實作一個,以下是一個簡單的範例

定義一個 Model

1
2
3
4
5
public class Model
{
public string Name { get; set; }
public string Job { get; set;}
}

定義一個Model Service,其職責為從PlayerPrefs拿資料,或是將資料寫入PlayerPrefs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class ModelService
{

public Model GetData()
{
Model m = new Model()
{
Name = PlayerPrefs.GetString("name"),
Job = PlayerPrefs.GetString("job"),
};

return m;
}

public void SaveName(string name)
{
PlayerPrefs.SetString("name", name);
}

public void SaveJob(string job)
{
PlayerPrefs.SetString("job", job);
}
}

定義一個BindableProperty類,它含有一個OnValueChanged事件,當Value的值發生變化(通過set方法)時,就會呼叫 OnValueChanged ,從而通知有註冊這個事件的物件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class BindableProperty<T>
{
public delegate void ValueChangedHandler(T oldValue, T newValue);

public event ValueChangedHandler OnValueChanged;

private T _value;
public T Value
{
get
{
return _value;
}
set
{
if (!object.Equals(_value, value))
{
T oldValue = _value;
_value = value;
// 通知值發生改變
OnValueChanged?.Invoke(oldValue, _value);
}
}
}

public override string ToString()
{
return (Value != null ? Value.ToString() : "null");
}
}

接下來定義一個 ViewModel , 這個 ViewModel 將負責為 View 提供資料,但它不知曉 View 的任何資訊,與 View 解耦,專注於資料的處理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class ViewModel
{

public BindableProperty<string> Name = new BindableProperty<string>();
public BindableProperty<string> Job = new BindableProperty<string>();

private ModelService service = new ModelService();

public ViewModel()
{}

public void GetModel()
{
Model m = service.GetData();
Name.Value = m.Name;
Job.Value = m.Job;

Debug.Log(m.Name + " " + m.Job);
}

public void SaveModel()
{
// do some check
string name = Name.Value;
string job = Job.Value;

if (name == null || name.Length > 0)
{
return;
}
if (job == null || job.Length > 0)
{
return;
}

service.SaveName(Name.Value);
service.SaveJob(Job.Value);
}

}

最後定義一個 View ,這個 View 包含了要操控的UI元件,因此繼承了MonoBehaviour,它不處理資料邏輯,只負責顯示格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public class View : MonoBehaviour
{
public TMP_InputField nameInputField;
public TextMeshProUGUI nameMessageText;

public TMP_InputField jobInputField;
public TextMeshProUGUI jobMessageText;

public TextMeshProUGUI resultText;
public Button applyButton;

public ViewModel viewModel;

private void Start()
{
viewModel = new ViewModel();
viewModel.Name.OnValueChanged += OnNameChanged;
nameInputField.onValueChanged.AddListener(OnNameInputChanged);

viewModel.Job.OnValueChanged += OnJobChanged;
jobInputField.onValueChanged.AddListener(OnJobInputChanged);

applyButton.onClick.AddListener(OnApplyButtonClick);

viewModel.GetModel();
}


private void OnDestroy()
{
viewModel.Name.OnValueChanged -= OnNameChanged;
nameInputField.onValueChanged.RemoveListener(OnNameInputChanged);

viewModel.Job.OnValueChanged -= OnJobChanged;
jobInputField.onValueChanged.RemoveListener(OnJobInputChanged);

applyButton.onClick.RemoveListener(OnApplyButtonClick);
}

private void OnNameChanged(string oldVal, string newVal)
{
nameMessageText.text = newVal;
}

private void OnNameInputChanged(string value)
{
viewModel.Name.Value = value;
}

private void OnJobChanged(string oldVal, string newVal)
{
jobMessageText.text = newVal;
}

private void OnJobInputChanged(string value)
{
viewModel.Job.Value = value;
}

private void OnApplyButtonClick()
{
resultText.text = viewModel.Name.Value + ", " + viewModel.Job.Value + " have been saved";
viewModel.SaveModel();
}
}

程式碼: https://github.com/mystudybook/Unity-MVVM

Reference: https://www.cnblogs.com/OceanEyes/p/unity3d_framework_designing_get_started_with_mvvm_part1.html

評論