Design Patterns – Using Chain of Responsibility to Simplify Complex if..else Statements

asp.netcdesign-patternsrefactoring

I've an HttpHandler, which allows users to login to a system by passing in an encrypted code.

Inside the ProcessRequest it performs quite a few steps.

  1. Retrieve the encrypted code from request (could be in Form/X-Header/Query)
  2. Decrypt it. (Results in an JSON string)
  3. Deserialize to a dynamic type
  4. Determine the type of request, could be login/register/activate
  5. Validate request
  6. Load user subscription
  7. Load User entity
  8. If Req.Type = Login -> Login using FormsAuth
  9. If Req.Type = Register -> Create user
  10. If Req.Type = Register -> Send Activation email

This goes on. Currently this is something like a long if/else statement which I'm trying to refactor.
I'm thinking of using the Chain Of Responsibility pattern to convert these steps in if/else to a chain which executes sequentially.
However, this will be different from original CoR as only last few steps will actually "handle" the request, first steps will just do some pre-processing and make stage for last steps to perform it's job.

My main problem is, different steps in the chain will work on different input data. First few steps (Decryption, Deserialization, etc) will work on strings while latter half should ideally work on deserialized dynamic object. I don't like the way it sounds.

Am I trying to fit a round lid to an square bottle? Is this not a scenario where CoR can/should be applied? Are there any other patterns/strategies I could use to solve such scenarios.

I thought of using the decorator pattern as well, but that doesn't suite me very well, as I'd like to be able to switch certain steps in and out (ex: email activation) for some scenarios.

NOTE: I've seen this question as well, but not sure if it answers my question.

Best Answer

Your ProcessRequest method is doing too much in one block of code. Chain of Responsibility is an inappropriate pattern because some of these steps are mandatory and some are alternatives.

Look at the last 3 steps - 8 to 10. Your main method should not care what has to be done for each individual type of request. All that should be happening here is

  1. Is the request type in my set of permitted types?
  2. If yes, invoke the corresponding class
  3. If no, handle the error.

That's going to be a very short block of code. It does require you, somewhere else in your code, to create your request-type-specific class (and subclasses) and the set to contain them, but the benefit is that this block of code in your main method need never change.

Step 5 should be a method of the type-specific class.

Steps 6 and 7 can probably be encapsulated in a User object but also only mean something for existing users, so the User-object handling stuff should go inside the type-specific class.

So we've already reduced the code to

  1. Retrieve the encrypted code from request (could be in Form/X-Header/Query)
  2. Decrypt it. (Results in an JSON string)
  3. Deserialize to a dynamic type
  4. Determine the type of request, could be login/register/activate
  5. if type in set-of-allowed-types then request-type = new type-specific-class else fail.
  6. if not request-type.validate() then fail
  7. request-type.dostuff()

See? No patterns involved. If-then-else chains are a bad smell, yes, but they can usually be replaced with a set of valid options or a switch statement. Try that simple approach before going all pattern-mad.

Related Topic