12

Uwarunkowanie warunkowe to ostatnia rzecz, której nie rozumiem w tej chwili.Zależność wtrysku jedności - warunkowe rozwiązywanie

Powiedzmy mamy interfejs IAuthenticate:

public interface IAuthenticate{ 
    bool Login(string user, string pass); 
} 

teraz mam dwa typy uwierzytelniania.

Twitter auth

public class TwitterAuth : IAuthenticate 
{ 
    bool Login(string user, string pass) 
{ 
    //connect to twitter api 
} 

} 

Facebook Auth

public class FacebookAuth: IAuthenticate 
{ 
    bool Login(string user, string pass) 
{ 
    //connect to fb api 
} 

} 

Rejestrowanie typy w jedności config:

unityContainer.RegisterType<IAuthenticate, TwitterAuth>(); 
unityContainer.RegisterType<IAuthenticate, FacebookAuth>(); 

wstrzyknąć obiekty via di naszego kontrolera:

private readonly IAuthenticate _authenticate; 

public AuthenticateController(IAuthenticate authenticate) 
{ 
    _authenticate = authenticate; 
} 



// login with twitter 
public virtual ActionResult Twitter(string user, string pass) 
{ 
    bool success = 
      _authenticate.Login(user, pass); 
} 



// login with fb 
public virtual ActionResult Facebook(string user, string pass) 
{ 
    bool success = 
      _authenticate.Login(user, pass); 
} 



// login with google 
public virtual ActionResult Google(string user, string pass) 
{ 
    bool success = 
      _authenticate.Login(user, pass); 
} 

Jak dokładnie jedność będzie wiedzieć, który obiekt musi rozwiązać w przypadku różnych typów uwierzytelniania? Jak mogę w tym przypadku wykonać rozwiązywanie warunkowe?

Rozmawiałem z moim przyjacielem, a on wyjaśnił, czy ta sytuacja wydaje się błędnym projektem, ale jest to tylko wzór fabryczny.

+0

Twój przyjaciel może mieć rację. Przyjrzyj się uważnie, jeśli nie naruszasz [zasady substytucji Liskova] (https://en.wikipedia.org/wiki/Liskov_substitution_principle).Jeśli implementacje "IAuthenticate" nie są wzajemnie zamienne (co oznacza, że ​​konsument taki jak "AuthenticateController" nie będzie działał poprawnie, jeśli otrzyma błędną implementację), naruszysz LSP. Rozwiązaniem w tym przypadku jest często nadanie każdej implementacji własnej abstrakcji. – Steven

Odpowiedz

32

Prostym sposobem rozwiązania tego problemu jest strategy pattern. Pamiętaj, że możesz dodawać lub usuwać dostawców logowań bez zmiany projektu - wystarczy zmienić konfigurację DI.

Interfejsy

public interface IAuthenticate{ 
    bool Login(string user, string pass); 
    bool AppliesTo(string providerName); 
} 

public interface IAuthenticateStrategy 
{ 
    bool Login(string providerName, string user, string pass); 
} 

uwierzytelniać Providers

public class TwitterAuth : IAuthenticate 
{ 
    bool Login(string user, string pass) 
    { 
     //connect to twitter api 
    } 

    bool AppliesTo(string providerName) 
    { 
     // I used the type name for this example, but 
     // note that you could use any string or other 
     // datatype to select the correct provider. 
     return this.GetType().Name.Equals(providerName); 
    } 
} 

public class FacebookAuth: IAuthenticate 
{ 
    bool Login(string user, string pass) 
    { 
     //connect to fb api 
    } 

    bool AppliesTo(string providerName) 
    { 
     return this.GetType().Name.Equals(providerName); 
    } 
} 

strategiczne

public class AuthenticateStrategy: IAuthenticateStrategy 
{ 
    private readonly IAuthenticate[] authenticateProviders; 

    public AuthenticateStrategy(IAuthenticate[] authenticateProviders) 
    { 
     if (authenticateProviders == null) 
      throw new ArgumentNullException("authenticateProviders"); 

     this.authenticateProviders = authenticateProviders; 
    } 

    public bool Login(string providerName, string user, string pass) 
    { 
     var provider = this.authenticateProviders 
      .FirstOrDefault(x => x.AppliesTo(providerName)); 

     if (provider == null) 
     { 
      throw new Exception("Login provider not registered"); 
     } 

     return provider.Login(user, pass); 
    } 
} 

Unity rejestracyjne

// Note that the strings used here for instance names have nothing 
// to do with the strings used to select the instance in the strategy pattern 
unityContainer.RegisterType<IAuthenticate, TwitterAuth>("twitterAuth"); 
unityContainer.RegisterType<IAuthenticate, FacebookAuth>("facebookAuth"); 
unityContainer.RegisterType<IAuthenticateStrategy, AuthenticateStrategy>(
    new InjectionConstructor(
     new ResolvedArrayParameter<IAuthenticate>(
      new ResolvedParameter<IAuthenticate>("twitterAuth") 
     ), 
     new ResolvedArrayParameter<IAuthenticate>(
      new ResolvedParameter<IAuthenticate>("facebookAuth") 
     ) 
    )); 

Użycie

private readonly IAuthenticateStrategy _authenticateStrategy; 

public AuthenticateController(IAuthenticateStrategy authenticateStrategy) 
{ 
    if (authenticateStrategy == null) 
     throw new ArgumentNullException("authenticateStrategy"); 

    _authenticateStrategy = authenticateStrategy; 
} 



// login with twitter 
public virtual ActionResult Twitter(string user, string pass) 
{ 
    bool success = 
      _authenticateStrategy.Login("TwitterAuth", user, pass); 
} 



// login with fb 
public virtual ActionResult Facebook(string user, string pass) 
{ 
    bool success = 
      _authenticateStrategy.Login("FacebookAuth", user, pass); 
} 
3

Jedność nie będzie bez twojej pomocy. Można podać nazwę po zarejestrowaniu rodzajów IAuthenticate:

unityContainer.RegisterType<IAuthenticate, TwitterAuth>("Twitter"); 
unityContainer.RegisterType<IAuthenticate, FacebookAuth>("Facebook"); 

ty nie zechcesz bezpośrednio wstrzykiwać instancję IAuthenticate do swojej AuthenticateController. Będziesz albo dostać instancji, która ma na podstawie warunku prosto z jedności:

myContainer.Resolve<IAuthenticate>("Twitter"); 

lub będziesz wstrzyknąć fabryka, która robi to za ciebie (jeśli lubisz ścisły styl DI).