Search code examples
c#singletonmauifactory-pattern

Is this right for a factory method producing singletons?


I've got this bit of code for a factory method to produce singletons. I have a small MAUI app and want to have persistent global data.

public static class FactoryService
{
    private static BudgetViewModel budgetViewModelInstance;
    private static AccountsViewModel accountsViewModelInstance;
    private static PayeesViewModel payeesViewModelInstance;

    public static BaseViewModel BuildViewModel(Type type)
    {
        if(type == typeof(BudgetViewModel))
        {
            budgetViewModelInstance ??= new BudgetViewModel();
            return budgetViewModelInstance;
        }
        else if(type == typeof(AccountsViewModel))
        {
            accountsViewModelInstance ??= new AccountsViewModel();
            return accountsViewModelInstance;
        }
        else if(type == typeof(PayeesViewModel))
        {
            payeesViewModelInstance ??= new PayeesViewModel();
            return payeesViewModelInstance;
        }
        else
        {
            throw new Exception($"Can't build View Model. Invalid View Model: {type}");
        }
    }
}

Then when I want a view model reference, I have to cast the type I want before calling BuildViewModel:

BudgetViewModel budgetViewModel = (BudgetViewModel)FactoryService.BuildViewModel(typeof(BudgetViewModel));

Is this.... right? Is there a better way to accomplish this? I can't use static classes for the view models because they have to inherit from ObservableObject. (from the Community MVVM Toolkit)


Solution

  • I would use generics. Something like this:

    public static class FactoryService
    {
        private static BudgetViewModel budgetViewModelInstance;
        private static AccountsViewModel accountsViewModelInstance;
        private static PayeesViewModel payeesViewModelInstance;
        private static readonly object _lock = new object();
    
        public static T BuildViewModel<T>() where T : BaseViewModel, new()
        {
            if (typeof(T) == typeof(BudgetViewModel))
            {
                if (budgetViewModelInstance == null)
                {
                    lock (_lock)
                    {
                        if (budgetViewModelInstance == null)
                        {
                            budgetViewModelInstance = new();
                        }
                    }
                }
    
                return budgetViewModelInstance as T;
            }
            else if (typeof(T) == typeof(AccountsViewModel))
            {
                if (accountsViewModelInstance == null)
                {
                    lock (_lock)
                    {
                        if (accountsViewModelInstance == null)
                        {
                            accountsViewModelInstance = new();
                        }
                    }
                }
    
                return accountsViewModelInstance as T;
            }
            else if (typeof(T) == typeof(PayeesViewModel))
            {
                if (payeesViewModelInstance == null)
                {
                    lock (_lock)
                    {
                        if (payeesViewModelInstance == null)
                        {
                            payeesViewModelInstance = new();
                        }
                    }
                }
    
                return payeesViewModelInstance as T;
            }
            else
            {
                throw new Exception($"Can't build View Model. Invalid View Model: {typeof(T)}");
            }
        }
    }
    

    Then you can invoke like this: var viewModel = FactoryService.BuildViewModel<BudgetViewModel>();