Search code examples
c#design-patternstemplate-method-pattern

Template method pattern where each implementation requires different arguments?


I have a base abstract class that needs an algorithm for Authentication. I have two implementations of this, one will hash a password and compare it to a stored hash, the other will use windows active directory.

But before actually doing the hash check or the windows authentication, I have extra workflow logic that must absolutely be implemented. So things like failedPasswordCount, lastAuthenticationAttemptDate, IsUserApproved, etc. must always be modified or used in the same way no matter the algorithm for Authentication.

This problem seems to me like it can be resolved using Template Method Pattern, except that, the information required to implement my two Authentication methods are different depending on which one is used.

public abstract class User
{
    public bool Authenticate() // my template method
    {
        lastAuthenticationAttemptDate = DateTime.UtcNow();
        if(IsUserApproved)
        {
            if(DoAuthenticate()) // implemented by childs
            {
                return true;
            }
            else
            {
                failedPasswordCount++;
                return false;
            }
        }
    }

    public abstract DoAuthenticate();
}

public UserWindowsLogon : User
{
    public override bool DoAuthenticate(string windowsDomain, string password)
    {
         ...
    }
}

public UserApplicationLogon : User
{
    public override bool DoAuthenticate(string password)
    {
         ...
    }
}

What is the best way to solve this problem? Is there another known pattern that already addresses this? Or anyone has a good idea?


Solution

  • Assuming your client code knows what to do (what actual parameters should be applied), you can easily introduce a class hierarchy around your authentication, thus making it possible to declare the contract on the base class of such hierarchy.

    public abstract DoAuthenticate( AuthenticationContext context );
    ...
    
    public UserWindowsLogon : User
    {
      public override bool DoAuthenticate( AuthenticationContext context )
      { 
         if ( context is UserWindowsAuthenticationContext )
         {
            // proceed
         }
      }
    }
    
    public UserApplicationLogon : User
    {
      public override bool DoAuthenticate( AuthenticationContext context )
      {
         if ( context is UserAplicationAuthenticationContext )
         {
            // proceed
         }
       } 
    }
    
    public abstract class AuthenticationContext { }
    
    public class UserWindowsAuthenticationContext : AuthenticationContext
    {
       public string windowsDomain;
       public string password;
    }
    
    public class UserApplicationAuthenticationContext : AuthenticationContext
    {
       public string password;
    }