1

I quite understand the basics of the Chain of Responsibility pattern. However, I would like to ask if it is possible to set the next receiver in the sequence dynamically.

The basic idea is that depending on the sequence of the approver, it will instantiate the next object.

Below is the example:

Abstract Base Class

public abstract class ApproverCategorizer { protected ApproverCategorizer NextApprover { get; private set; } public ApproverCategorizer RegisterNextApprover(ApproverCategorizer nextApprover) { NextApprover = nextApprover; return nextApprover; } public abstract void ApproveAmount(TestEntity entity); protected bool IsLast(Queue<string> approverQueue) { return string.IsNullOrEmpty(approverQueue.Peek()); } } 

Officer approver class

public class OfficerAApprover : ApproverCategorizer { public override void ApproveAmount(TestEntity entity) { entity.ApproverQueue.Dequeue(); if (entity.Amount <= 300) { entity.Status = "Approved"; return; } if (!IsLast(entity.ApproverQueue) && string.IsNullOrWhiteSpace(entity.Status)) { NextApprover.ApproveAmount(entity); return; } else { entity.Status = "Rejected"; } } } 

Officer B Class

public class OfficerBApprover : ApproverCategorizer { public override void ApproveAmount(TestEntity entity) { entity.ApproverQueue.Dequeue(); if (entity.Amount <= 300) { entity.Status = "Approved"; return; } if (!IsLast(entity.ApproverQueue) && string.IsNullOrWhiteSpace(entity.Status)) { NextApprover.ApproveAmount(entity); return; } else { entity.Status = "Rejected"; } } } 

Approver Chain Class

public class ApproverChain { public static TestEntity Entity { get; set; } public static ApproverCategorizer Approver { get; set; } public ApproverChain() { List<string> approverList = Entity.ApproverList.Split(',').ToList(); Queue<string> approverQueue = new Queue<string>(); Approver = new StartApprover(); // Note: The code below is working, but not the desired logic. //Approver.RegisterNextApprover(new OfficerAApprover()).RegisterNextApprover(new OfficerBApprover()); // Note: This code below is not working, but this is the desired logic. foreach (string approver in approverList) { switch (approver) { case "OfficerA": Approver.RegisterNextApprover(new OfficerAApprover()); break; case "OfficerB": Approver.RegisterNextApprover(new OfficerBApprover()); break; } approverQueue.Enqueue(approver); } Entity.ApproverQueue = approverQueue; } public string StartProcess() { Approver.ApproveAmount(Entity); return Entity.Status; } } 

Business Class

public string ProcessApproval() { TestEntity entity = new TestEntity { Amount = 500, ApproverList = "OfficerB,OfficerA" }; ApproverChain.Entity = entity; ApproverChain chain = new ApproverChain(); var result = chain.StartProcess(); return result; } 

This means that the OfficerB class will process first. If it fails, it will go to OfficerA class.

Is there a way to tweak it to the desired logic as mentioned? If so, how is it done?

5
  • Of course it is possible to have the code work as you describe. But it seems to me that you no longer really have a "chain of responsibility" implementation when you do that. So if your question is "is it possible to do this while still using 'chain of responsibility'?", I'd say the answer is "no". But of course, that's an opinionated answer, exactly the type a question like this is likely to solicit. Commented Mar 17, 2015 at 7:21
  • @PeterDuniho Why would you say that you no longer really have a "chain of responsibility" ? What OP violates in chain of responsibility pattern? Pattern doesn't mandates the wiring to be at compile time. Commented Mar 17, 2015 at 7:25
  • @SriramSakthivel: frankly, this is exactly what I'm talking about. Design patterns are themselves inherently subjective, and you and I clearly have a basic disagreement as to where the fuzzy threshold of "part of the design pattern" and "not part of the design pattern" is located. You define the pattern more broadly than I might. This is not the right forum for debating such questions. Commented Mar 17, 2015 at 9:09
  • @PeterDuniho My point is formal definition doesn't mandates wiring to be at compile time . Yes, I agree this is not the place to debate, but if you have something good to strengthen your point, I'm happy to hear. Btw I'm not sure why this question is voted to close as "Opinion based". OP didn't asked whether doing so is right or wrong which is subjective, rather the question is how to make his code work. I can't understand how on earth it is "Opinion based". Commented Mar 17, 2015 at 9:14
  • @SriramSakthivel: there is no "formal definition" for any of the design patterns we use every day in programming. Used correctly they are fluid and adapted to immediate needs, without commitment to dogma. But at the same time, each person has their own subjective stance as to what the boundaries of a pattern are and whether certain changes push code outside those boundaries. The OP asks a vague "is it possible" question, which if interpreted to be asking whether it's possible to do what he says and still stay in the pattern is to me clearly a matter of opinion. Commented Mar 17, 2015 at 9:17

1 Answer 1

3

If I understand correctly, you need to configure your approvers via runtime value (string in this case).

With very little change in your code it is possible. Here is the required modification.

public ApproverChain() { List<string> approverList = Entity.ApproverList.Split(',').ToList(); Queue<string> approverQueue = new Queue<string>(); Approver = new StartApprover(); ApproverCategorizer currentApprover = Approver; foreach (string approver in approverList) { switch (approver) { case "OfficerA": currentApprover = currentApprover.RegisterNextApprover(new OfficerAApprover()); break; case "OfficerB": currentApprover = currentApprover.RegisterNextApprover(new OfficerBApprover()); break; } approverQueue.Enqueue(approver); } Entity.ApproverQueue = approverQueue; } 

Above code will work just fine. But for me the ApproverQueue looks fishy.

It seems like you're having ApproverQueue property just to find out whether the current approver is the last approver in the chain. You can find it by simply checking NextApprover to null and completely get rid of that approverQueue.

Then your code becomes

public class ApproverChain { public static TestEntity Entity { get; set; } public static ApproverCategorizer Approver { get; set; } public ApproverChain() { Approver = new StartApprover(); List<string> approverList = Entity.ApproverList.Split(',').ToList(); ApproverCategorizer currentApprover = Approver; foreach (string approver in approverList) { switch (approver) { case "OfficerA": currentApprover = currentApprover.RegisterNextApprover(new OfficerAApprover()); break; case "OfficerB": currentApprover = currentApprover.RegisterNextApprover(new OfficerBApprover()); break; } } } public string StartProcess() { Approver.ApproveAmount(Entity); return Entity.Status; } } public abstract class ApproverCategorizer { protected ApproverCategorizer NextApprover { get; private set; } public ApproverCategorizer RegisterNextApprover(ApproverCategorizer nextApprover) { NextApprover = nextApprover; return nextApprover; } public abstract void ApproveAmount(TestEntity entity); protected bool IsLast() { return NextApprover == null; } } public class OfficerAApprover : ApproverCategorizer { public override void ApproveAmount(TestEntity entity) { if (entity.Amount <= 300) { entity.Status = "Approved"; return; } if (!IsLast() && string.IsNullOrWhiteSpace(entity.Status)) { NextApprover.ApproveAmount(entity); return; } else { entity.Status = "Rejected"; } } } 

And your TestEntity class will not have ApproverQueue property.

Sign up to request clarification or add additional context in comments.

1 Comment

I actually revised my initial version to what I want, and surprisingly we almost have the same design. Thank you guys for your debate and clarification. :)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.