생성자를 직접 호출했을 때 오작동 할 여지가 있는 클래스가 있다고 가정해봅시다. 이럴 때 별도의 Builder 클래스를 통해 인스턴스를 생성하면 좋은데요,
public class ActivationViewer
{
public ActivationViewer(..., ..., ...) // 신중하게 인자를 입력해야 한다고 가정합니다.
}
이럴 때 public
생성자 대신 internal
로 주고, Builder를 통해 생성하도록 할 수 있습니다.
public class ActivationViewer : IActivationViewer
{
public static readonly string DefaultId = "DEFAULT";
private readonly Dictionary<Type, Type> _map = new();
private readonly Dictionary<string, Type> _viewMap = new();
private readonly Dictionary<string, Type> _viewModelMap = new();
private readonly IServiceProvider _sp;
public string StartupId { get; } = DefaultId;
internal ActivationViewer(IServiceProvider sp, IReadOnlyDictionary<Type, Type> map, string startupId = "")
{
_sp = sp;
foreach (var kv in map)
{
_map[kv.Key] = kv.Value;
_viewModelMap[kv.Key.Name] = kv.Key;
_viewMap[kv.Value.Name] = kv.Value;
}
if (string.IsNullOrWhiteSpace(startupId) == false)
StartupId = startupId;
}
public IView Get(object? activationArgs = null) => Get(StartupId, activationArgs);
public IView Get(string id, object? activationArgs = null)
{
var viewType = _map[_viewModelMap[id]];
if (_sp.GetService(viewType) is not IView view)
throw new ArgumentException($"'{id}' is an unregistered ViewModel.");
return view;
}
public sealed class Builder : IBuilder
{
private readonly Dictionary<Type, Type> _map = new();
private Builder()
{
}
public static IBuilder Create() => new Builder();
public IBuilder AddMap(Type viewModelType, Type view)
{
_map[viewModelType] = view;
return this;
}
public IBuilder AddMap<TViewModelType, TView>() => AddMap(typeof(TViewModelType), typeof(TView));
public ActivationViewer Build(IServiceProvider sp, string defaultId) => new ActivationViewer(sp, _map, defaultId);
}
public interface IBuilder
{
IBuilder AddMap(Type viewModelType, Type view);
IBuilder AddMap<TViewModelType, TView>();
ActivationViewer Build(IServiceProvider sp, string defaultId);
static abstract IBuilder Create();
}
}
외부 모듈에서는 ActivationViewer
의 생성자가 internal
이므로 직접 생성자를 호출할 수 없고, Builder에 의해서만 생성할 수 있게 됩니다.
사용은 다음과 같습니다.
...
var s = new ServiceCollection()
.AddSingleton<IActivationViewer>(p => ActivationViewer.Builder.Create()
.AddMap<MainViewModel, MainPage>()
.Build(p, nameof(MainViewModel))
);
...