.Net Core 中使用工廠模式

什麼是工廠模式

工廠模式是最常用的設計模式之一,屬於創建型模式。
有點:

  • 解耦,可以把對象的創建和過程分開
  • 減少代碼量,易於維護

什麼時候用?

當一個抽象類有多個實現的時候,需要多次實例化的時候,就要考慮使用工廠模式。
比如:登錄的抽象類ILoginBusiness,它有2個實現,一個用用戶名密碼登錄,一個用手機號驗證碼登錄

public interface ILoginBusiness
{
    Task Login(LoginInputDTO input);
}

public class MobileCodeLogin : ILoginBusiness
{
    public Task Login(LoginInputDTO input)
    {
        // todo 
    }
}

public class UserNameAndPasswordLogin : ILoginBusiness
{
    public Task Login(LoginInputDTO input)
    {
        // todo 
    }
}

按照原有方式,我們會根據某個條件去實例化,比如loginType

ILoginBusiness loginBusiness;
if (loginType == 1)
{
    loginBusiness = new MobileCodeLogin();
}
else
{
    loginBusiness = new UserNameAndPasswordLogin()
}

當有多種實例的話,就需要更多的ifelse或者switch,工廠模式的作用就是把這種建造對象的方法放在工廠類去解決,統一管理統一維護。

實現方式

public interface ILoginFactory
{
    ILoginBusiness Create(LoginInputDTO inputDto);
}

public class LoginFactory : BaseBusiness, ILoginFactory, ITransientDependency
{
    
    public ILoginBusiness Create(LoginInputDTO inputDto)
    {
        return inputDto.LoginType == LoginType.UserNameAndPwd
            ? AppDependencyResolver.Current.GetService<UserNameAndPwdLogin>()
            : (ILoginBusiness)AppDependencyResolver.Current.GetService<MobileCodeLogin>();

    }


    public LoginFactory(IRepository repository) : base(repository)
    {
        
    }
}

如果用new的方法去實例化,就需要把構造方法中的參數在工廠的構造方法里也加入進去,所以這裡用了一個AppDependencyResolver,這裡的AppDependencyResolver.Current.GetService<T>是從當前IOC容器中獲取注入的實例,所以之前的MobileCodeLoginUserNameAndPasswordLogin需要注入到IOC實例中,在我們項目就是繼承ITransientDependency

public class AppDependencyResolver
{
    private static AppDependencyResolver _resolver;
    public static AppDependencyResolver Current
    {
        get
        {
            if (_resolver == null)
                throw new Exception("AppDependencyResolver not initialized");
            return _resolver;
        }
    }
    
    // 在startUp的ConfigureService的最後,加入AppDependencyResolver.Init(services.BuildServiceProvider());
    public static void Init(IServiceProvider service)
    {
        _resolver = new AppDependencyResolver(service);
    }
    private AppDependencyResolver(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }
    private readonly IServiceProvider _serviceProvider;
    public object GetService(Type serviceType)
    {
        return _serviceProvider.GetService(serviceType);
    }
    public T GetService<T>()
    {
        return _serviceProvider.GetService<T>();
    }
}

關於工廠的單元測

關於工廠,我們只需要測返回的對象是否是我們需要的就可以了,至於對象的執行對不對不需要關心

[Theory(DisplayName = "測試登錄工廠")]
[InlineData(LoginType.MobileCode, typeof(MobileCodeLogin))]
[InlineData(LoginType.UserNameAndPwd, typeof(UserNameAndPwdLogin))]
public void CreateTests(LoginType loginType, Type expectedType)
{
    Mock<IRepository> mock = new Mock<IRepository>();
    ILoginFactory factory = new LoginFactory(mock.Object);
    var login =  factory.Create(new LoginInputDTO()
    {
        LoginType = loginType
    });
    Assert.IsType(expectedType, login);
}