1

I am constructing a new class which handles deciding which class should process the requests from a web service. I'm looking for a solution for a problem that I am having regarding extensibility with this new class. Each request will be processed differently, and the logic for processing them each are in their own class. The problem that I am hoping to solve is how to design the classes in such a way that adding a new type of request does not require any changes to existing code. Consider the code that I have below. Please pardon any typos in the following examples:

public interface IRequestData
{

}

public class OldRequestData : IRequestData
{
    // Some properties holding data.
}

public class NewRequestData : IRequestData
{
    // Some properties holding data.
}

public class ProcessOldRequestData
{
    public void Process(OldRequestData Data)
    {
        // Some code to process the request.
    }
}

public class ProcessNewRequestData
{
    public void Process(NewRequestData Data)
    {
        // Some code to process the request.
    }
}

public class RequestProcessor
{
    public void ProcessRequest(IRequestData Data)
    {
        // Get and load base type of Data

        // Based on if it's OldRequestData, NewRequestData, or *FutureRequestData*,
        // Instantiate the appropriate class to process the request.
    }
}

This is the model that I am currently working with. The problem is each new type of request requires changes to the RequestProcessor to call the new class. Is there a way to make this class "Know" which class it needs to call to process each type of request? If there is a better of handling this I would be very open for other suggestions.

Thank you in advance for your input.

3 Answers 3

0

There are two ways to do it.

  1. The OO way: Make your current OldRequestData and NewRequestData classes more active and let them implement a method that is executing the desired behaviour. This means your Process methods will be put into your IRequestData classes. The advantage is that you can now add classes implement IRequestData and let them implement have the corresponding Process methods. The drawback here kicks in as soon as you want to have differend kind of Process method for each IRequestData class (e.g. two different ways of processing OldRequestData and also two different ways of processing NewRequestData)

  2. Solving the drawbacks of the first solution means diving into the Expression Problem (even though we use a dynamically typed language here). There are different ways to solve this, but they lead to more complexity. You have to weight the benefits against the complexity. If you don't want IRequestData classes, you should stay with the first solution.

4
  • I like the solution in your first example. Would that mean the IRequestData classes are "Doing too much", as in having more than one responsibility now, though?
    – Tama198
    Jun 24, 2015 at 15:57
  • Yes, very well observed! It violates the SRP. It is the common OOP solution though (which is almost always violating SRP). Your current solution is the functional programming like approach which has the advantage of beeing able to add functionality but not new kind of objects. Using my 2nd solution you will be able to add functionality and new objects but it is kind of harder to understand and only beneficial if you really need to add functionality and objects later without wanting to touch any of the previous code.
    – valenterry
    Jun 24, 2015 at 16:23
  • I like your solution, and I think it could work well in my situation. I'm still hopeful of a solution without breaking SRP, though. Would there be some way that the request processor can dynamically know which class knows how to process the request? (Apologies, I may have just restated the original question). For example, would it be reasonable to have each base process class implement a field with the base IRequestData type? The RequestProcessor could then use reflection to compare the type of the field against the type of the parameter.
    – Tama198
    Jun 24, 2015 at 16:52
  • How would a type field help? The Processor still has to decide what to do - and unless you want to add a "default" behaviour (which would make the whole question needless) you need to change him everytime to make him decide what to do with a specific new type. Therefore the only way is passing in the type AND its behaviour either by putting it both into a class (solution 1) or using e.g. an object algebra (cs.utexas.edu/~wcook/Drafts/2012/ecoop2012.pdf) which does not violate SRP. I'm afraid that there is no easy solution to your problem as much as I'd want one to exist.
    – valenterry
    Jun 24, 2015 at 17:17
4

create a collection (map, dictionary, etc) of requests to objects. In ProcessRequest, you iterate through the collection to find an entry that matches the input request, and call the associated object.

Then you only have to populate the collection, but this can be done by reading configuration or by having each RequestData class register itself at startup (by adding itself to your collection along with the set of requests it can handle).

of course, if you have to create a new 'FutureRequestData' class with custom logic, then you will have to update your code. There's no getting round that unless you execute arbitrary code that is read from config (eg a scripting language) and you have some engine or compiler that can cause this code to execute.

1
  • This would mean any new request would need to be mapped so it can be registered by the class, it could be done by reading a config like you said, though.
    – Tama198
    Jun 24, 2015 at 16:00
0

Look at using the Abstract factory, Builder and Factory method patterns.

These will let you do something like the below (apologies for any syntax errors):

IRequestData rd = RequestDataBuilder.Build('old').GetData();
// not strictly necessary but follow the pattern throughout
IRequestProcessor rp = RequestProcessorBuilder.Build().GetProcessor();

on which you can now do

rp.ProcessRequest(rd);

without knowing - or caring - about which (old or new) set of data or processor you're working on.

The RequestProcessorBuilder can figure out which request processor to build based on arguments passed in to the Build() method or some other parameters used by the builder.

So the default might be a 'fast' ProcessSpeed but we can also specify a 'slow' speed, in which case a different object is returned. Importantly, the called doesn't care, since it just interacts with an interface.

IRequestProcessor rp = RequestProcessorBuilder.Build()
                         .ProcessSpeed('slow') 
                         .GetProcessor();

Alternatively, the decision as to which object the builder returns can come from config (file, db, wherever).

From those links (which have examples), a short description:

  • Abstract factory: Provide an interface for creating families of related or dependent objects without specifying their concrete classes
  • Builder: Separate the construction of a complex object from its representation, allowing the same construction process to create various representations
  • Factory method: Define an interface for creating a single object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses
1
  • This is an interesting solution - how would the RequestProcessorBuilder know which IRequestProcessor to create, though?
    – Tama198
    Jun 24, 2015 at 16:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.