From 51099afc7936c8928a20ff1686416e42fda58d67 Mon Sep 17 00:00:00 2001 From: Konstantin Dyachenko Date: Wed, 27 May 2026 04:18:52 +0700 Subject: [PATCH] =?UTF-8?q?=EF=BB=BFfeat(task-0004):=20add=20ViewModel=20l?= =?UTF-8?q?ayer=20and=20improve=20UIView=20initialization=20safety?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Create SplashUIViewModel, MenuUIViewModel and LoadingUIViewModel classes implementing IUIViewModel - Convert UI views to use generic UIView pattern for strong typing - Bind ViewModels in state EnterAsync methods before view initialization - Add null check validation in UIView.Bind method with ArgumentNullException - Track initialization state with _isInitialized flag to prevent duplicate Initialize/Release calls Выполнена задача TASK-0004 и добавлен слой ViewModel: - Созданы классы SplashUIViewModel, MenuUIViewModel и LoadingUIViewModel с реализацией IUIViewModel - Преобразованы UI представления для использования общего паттерна UIView - Добавлено связывание ViewModels в методах EnterAsync состояний перед инициализацией вида - Добавлена проверка на null в методе Bind с выбросом ArgumentNullException - Добавлен флаг _isInitialized для предотвращения повторных вызовов Initialize/Release --- Assets/Scripts/Boot/States/LoadState.cs | 1 + Assets/Scripts/Boot/States/MenuState.cs | 1 + Assets/Scripts/Boot/States/SplashState.cs | 1 + Assets/Scripts/Boot/UI/LoadingUIView.cs | 2 +- Assets/Scripts/Boot/UI/LoadingUIViewModel.cs | 8 ++++++++ Assets/Scripts/Boot/UI/LoadingUIViewModel.cs.meta | 11 +++++++++++ Assets/Scripts/Boot/UI/MenuUIView.cs | 2 +- Assets/Scripts/Boot/UI/MenuUIViewModel.cs | 8 ++++++++ Assets/Scripts/Boot/UI/MenuUIViewModel.cs.meta | 11 +++++++++++ Assets/Scripts/Boot/UI/SplashUIView.cs | 2 +- Assets/Scripts/Boot/UI/SplashUIViewModel.cs | 8 ++++++++ Assets/Scripts/Boot/UI/SplashUIViewModel.cs.meta | 11 +++++++++++ Assets/Scripts/Common/UI/UIView.Generic.cs | 7 +++++++ Assets/Scripts/Common/UI/UIView.cs | 14 ++++++++++++++ 14 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 Assets/Scripts/Boot/UI/LoadingUIViewModel.cs create mode 100644 Assets/Scripts/Boot/UI/LoadingUIViewModel.cs.meta create mode 100644 Assets/Scripts/Boot/UI/MenuUIViewModel.cs create mode 100644 Assets/Scripts/Boot/UI/MenuUIViewModel.cs.meta create mode 100644 Assets/Scripts/Boot/UI/SplashUIViewModel.cs create mode 100644 Assets/Scripts/Boot/UI/SplashUIViewModel.cs.meta diff --git a/Assets/Scripts/Boot/States/LoadState.cs b/Assets/Scripts/Boot/States/LoadState.cs index 4820125..a259926 100644 --- a/Assets/Scripts/Boot/States/LoadState.cs +++ b/Assets/Scripts/Boot/States/LoadState.cs @@ -16,6 +16,7 @@ namespace QuizPleaseTest.Boot.States public UniTask EnterAsync(CancellationToken ct) { + _view.Bind(new LoadingUIViewModel()); _view.Initialize(); return UniTask.CompletedTask; } diff --git a/Assets/Scripts/Boot/States/MenuState.cs b/Assets/Scripts/Boot/States/MenuState.cs index 18dae41..509b985 100644 --- a/Assets/Scripts/Boot/States/MenuState.cs +++ b/Assets/Scripts/Boot/States/MenuState.cs @@ -19,6 +19,7 @@ namespace QuizPleaseTest.Boot.States public async UniTask EnterAsync(CancellationToken ct) { + _view.Bind(new MenuUIViewModel()); _view.Initialize(); await _restartSignal.WaitAsync(ct); } diff --git a/Assets/Scripts/Boot/States/SplashState.cs b/Assets/Scripts/Boot/States/SplashState.cs index 0b8696e..dcf20e0 100644 --- a/Assets/Scripts/Boot/States/SplashState.cs +++ b/Assets/Scripts/Boot/States/SplashState.cs @@ -16,6 +16,7 @@ namespace QuizPleaseTest.Boot.States public UniTask EnterAsync(CancellationToken ct) { + _view.Bind(new SplashUIViewModel()); _view.Initialize(); return UniTask.CompletedTask; } diff --git a/Assets/Scripts/Boot/UI/LoadingUIView.cs b/Assets/Scripts/Boot/UI/LoadingUIView.cs index 6a42e5c..1395a3e 100644 --- a/Assets/Scripts/Boot/UI/LoadingUIView.cs +++ b/Assets/Scripts/Boot/UI/LoadingUIView.cs @@ -2,7 +2,7 @@ using QuizPleaseTest.Common.UI; namespace QuizPleaseTest.Boot.UI { - public class LoadingUIView : UIView + public class LoadingUIView : UIView { } } diff --git a/Assets/Scripts/Boot/UI/LoadingUIViewModel.cs b/Assets/Scripts/Boot/UI/LoadingUIViewModel.cs new file mode 100644 index 0000000..4a9d72e --- /dev/null +++ b/Assets/Scripts/Boot/UI/LoadingUIViewModel.cs @@ -0,0 +1,8 @@ +using QuizPleaseTest.Common.UI; + +namespace QuizPleaseTest.Boot.UI +{ + public class LoadingUIViewModel : IUIViewModel + { + } +} diff --git a/Assets/Scripts/Boot/UI/LoadingUIViewModel.cs.meta b/Assets/Scripts/Boot/UI/LoadingUIViewModel.cs.meta new file mode 100644 index 0000000..3c747c4 --- /dev/null +++ b/Assets/Scripts/Boot/UI/LoadingUIViewModel.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a5555555555555555555555555555555 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Boot/UI/MenuUIView.cs b/Assets/Scripts/Boot/UI/MenuUIView.cs index 9574baa..530bf4a 100644 --- a/Assets/Scripts/Boot/UI/MenuUIView.cs +++ b/Assets/Scripts/Boot/UI/MenuUIView.cs @@ -2,7 +2,7 @@ using QuizPleaseTest.Common.UI; namespace QuizPleaseTest.Boot.UI { - public class MenuUIView : UIView + public class MenuUIView : UIView { } } diff --git a/Assets/Scripts/Boot/UI/MenuUIViewModel.cs b/Assets/Scripts/Boot/UI/MenuUIViewModel.cs new file mode 100644 index 0000000..1bd8eb7 --- /dev/null +++ b/Assets/Scripts/Boot/UI/MenuUIViewModel.cs @@ -0,0 +1,8 @@ +using QuizPleaseTest.Common.UI; + +namespace QuizPleaseTest.Boot.UI +{ + public class MenuUIViewModel : IUIViewModel + { + } +} diff --git a/Assets/Scripts/Boot/UI/MenuUIViewModel.cs.meta b/Assets/Scripts/Boot/UI/MenuUIViewModel.cs.meta new file mode 100644 index 0000000..43568cb --- /dev/null +++ b/Assets/Scripts/Boot/UI/MenuUIViewModel.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a6666666666666666666666666666666 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Boot/UI/SplashUIView.cs b/Assets/Scripts/Boot/UI/SplashUIView.cs index 26982ad..a062e45 100644 --- a/Assets/Scripts/Boot/UI/SplashUIView.cs +++ b/Assets/Scripts/Boot/UI/SplashUIView.cs @@ -2,7 +2,7 @@ using QuizPleaseTest.Common.UI; namespace QuizPleaseTest.Boot.UI { - public class SplashUIView : UIView + public class SplashUIView : UIView { } } diff --git a/Assets/Scripts/Boot/UI/SplashUIViewModel.cs b/Assets/Scripts/Boot/UI/SplashUIViewModel.cs new file mode 100644 index 0000000..761b7ff --- /dev/null +++ b/Assets/Scripts/Boot/UI/SplashUIViewModel.cs @@ -0,0 +1,8 @@ +using QuizPleaseTest.Common.UI; + +namespace QuizPleaseTest.Boot.UI +{ + public class SplashUIViewModel : IUIViewModel + { + } +} diff --git a/Assets/Scripts/Boot/UI/SplashUIViewModel.cs.meta b/Assets/Scripts/Boot/UI/SplashUIViewModel.cs.meta new file mode 100644 index 0000000..8e5fcc1 --- /dev/null +++ b/Assets/Scripts/Boot/UI/SplashUIViewModel.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a4444444444444444444444444444444 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Common/UI/UIView.Generic.cs b/Assets/Scripts/Common/UI/UIView.Generic.cs index 7cf977e..78684a3 100644 --- a/Assets/Scripts/Common/UI/UIView.Generic.cs +++ b/Assets/Scripts/Common/UI/UIView.Generic.cs @@ -1,3 +1,5 @@ +using System; + namespace QuizPleaseTest.Common.UI { public class UIView : UIView where TVm : IUIViewModel @@ -6,6 +8,11 @@ namespace QuizPleaseTest.Common.UI public virtual void Bind(TVm viewModel) { + if (viewModel == null) + { + throw new ArgumentNullException(nameof(viewModel)); + } + ViewModel = viewModel; } diff --git a/Assets/Scripts/Common/UI/UIView.cs b/Assets/Scripts/Common/UI/UIView.cs index d32f872..3476783 100644 --- a/Assets/Scripts/Common/UI/UIView.cs +++ b/Assets/Scripts/Common/UI/UIView.cs @@ -4,13 +4,27 @@ namespace QuizPleaseTest.Common.UI { public class UIView : MonoBehaviour { + private bool _isInitialized; + public virtual void Initialize() { + if (_isInitialized) + { + return; + } + + _isInitialized = true; gameObject.SetActive(true); } public virtual void Release() { + if (!_isInitialized) + { + return; + } + + _isInitialized = false; gameObject.SetActive(false); } }