I'm not 100% sure this is the right place to post this. It is not about the specific code being written, but more about the design of the code. (Of course any constructive criticism is appreciated)
I have the following interfaces for dealing with talking to a server:
public interface IAction
{
GatewayResponseHandlerResult Run();
}
public abstract class GatewayResponseHandler
{
protected GatewayResponseHandlerResult Result = new GatewayResponseHandlerResult();
private GatewayResponseHandlerResult Handle(GatewayResponse Response)
{
//specific handling code omitted
}
}
The idea is each type of action (ex. Login, Logout) is its own class that implements the IAction
interface. The action performs the call to the servers and passes the response to the GatewayResponseHandler.Handle()
function which then passes back a GatewayResponseHandlerResult
object. The specific IAction
object then passes the GatewayResponseHandleResult
object back to the caller.
This is the GatewayResponseHandlerResult
class:
public class GatewayResponseHandlerResult
{
public bool Succeeded;
public bool Failed;
public bool Errored;
public object Result { get; set; }
public void SetAsSuccess()
{
Succeeded = true;
Failed = false;
Errored = false;
}
public void SetAsFailure()
{
Succeeded = false;
Failed = true;
Errored = false;
}
public void SetAsError()
{
Succeeded = false;
Failed = false;
Errored = true;
}
}
This is what an Action
would look like:
public sealed class LoginAction : IAction
{
private string Username;
private string Password;
public LoginAction(string Username, string Password)
{
this.Username = Username;
this.Password = Password;
}
public GatewayResponseHandlerResult Run()
{
try
{
GatewayResponse LoginResponse = GatewayRequest.Login(Username, Password);
return GatewayResponseHandler.Handle(LoginResponse);
}
catch (Exception ex)
{
throw ex.Log();
}
}
}
And the corresponding derived response handler:
sealed class LoginHandler : GatewayResponseHandler
{
protected override bool CanHandle()
{
return Response.action == GatewayAction.LOGIN;
}
protected override GatewayResponseHandlerResult ProcessError(Exception ex = null)
{
Result.SetAsError();
return Result;
}
protected override GatewayResponseHandlerResult ProcessFailure()
{
Result.SetAsFailure();
return Result;
}
protected override GatewayResponseHandlerResult ProcessSuccess()
{
try
{
if (Response.result_code == (int)ResponseCodes.SuccessCode.MFA_REQUIRED)
{
//specific MFA handling omitted
}
else if (Response.result_code == (int)ResponseCodes.SuccessCode.PW_RESET_REQUIRED)
{
//specific password reset code omitted
}
MessageBox.Show("Login successful");
Result.SetAsSuccess();
}
catch (Exception Ex)
{
ProcessError(Ex);
Result.SetAsError();
}
return Result;
}
}
As you can see the LoginResponseHandler
can need to deal with several possible responses from a successful login since it can require an MFA code to be passed back to complete the login or a password reset might be required on login. This can mean running new Action
s. Should the LoginResponseHandler
be executing those directly or passing back something to let the caller of the action know they need to perform further Action
s?
I'm also not sure if I should have the Action
s be instantiated, or perhaps make the Run()
method static and have some sort of abstract argument object with specific implementation for each action that actually need it.
For example:
public interface IActionArgs{}
public class LoginActionArgs : IActionArgs
{
public string Username;
public string Password;
}
public abstract class Action
{
public static GatewayResponseHandlerResult Run(IActionArgs ActionArgs)
{
throw new NotImplementedException();
}
}
public sealed class LoginAction : IAction
{
public static new GatewayResponseHandlerResult Run(IActionArgs ActionArgs)
{
//code omitted
}
}
While I like the idea of the methods being static, I don't like the way the code looks, and the fact that you could call Run
on the abstract class since interface can't have static methods.
Any general advice on how to improve the architecture of the code is very much appreciated.