Reputation: 1691
I'm rewriting a beast of a program that does everything and it's kitchen sink. It's a phone IVR system (press 1 to blah blah, press 2 to...) I've got all of it's functions divvied up into their own projects, however my one biggest pain point comes right when we first answer the phone and have the user enter a code that lets us know what system to dispatch the caller to.
The whole code system is a mess TBH, but it can't be changed, and I've refactored about 800 lines of VB6 down to something that resembles the following code:
string code = foo.GetAccessCodeFromCaller();
if (DatabaseCheck1(code)
{
parse = ParseCode(code);
dbValue = GetSomethingFromDB(parse);
if (OtherCheck1(dbValue)
{
// Launch the Pay Taxes project.
}
else if (OtherCheck2(dbValue)
{
// Launch the Uploaded File project
}
else
{
// Schedule Something or other project
}
}
else if (LookForSomethingElseInDB(code)
{
parse2 = AltParseMethod(code)
if (Conditional(parse2))
{
if (LargeChunkOfCodeConditional(code))
{
// Report a something or other project.
}
else
{
// Talk to Tech Support.
}
}
else
{
// Invalid Input
}
}
else
{
if (YetAnotherChunkOfCode(code))
{
// Order products project
}
else
{
// Invalid Input.
}
}
I need a good architecture to get this system done right, where right equates to being highly adaptable to getting more crap shoe horned in to it. The original system was done in VB4/5, and lasted through over 16 years of near monthly changes. I want something that'll keep this mess orderly and make it easy to add crap to for the next 16 years.
So far I've tried a few patterns (Visitor, and Command), but nothing seems to be a good fit the way I tried to implement it. Any suggestions here would be very appreciated.
EDIT: To be a bit more clear my current architecture has a solution with the following projects: Dispatch, PayTaxes, UploadedFiles, ScheduleSomething, ReportSomething, TechSupportRedirect, OrderProducts ect... (along with HardwareAbstraction, SharedInterfaces, and DatabaseAccess projects). Dispatch uses the HardwareAbstraction project to answer the phone and ask for the callers code, then routes the call to one of the other 10 projects that perform wildly different tasks (and can then be rewritten in parallel by 10 different developers without toes getting stepped on).
I can figure out the architectures of the destination projects well enough, but the actual Dispatch project is tripping me up. Also if my whole solutions architecture is some sort of anti-pattern someone let me know before I get too far.
Upvotes: 1
Views: 2135
Reputation: 1691
Thanks to everyone that chimed in here, due to the suggestions I got past a few mental blocks and found that the chain of responsibility pattern will nicely solve my problem.Here's the MSDN article on how to implement it.
Upvotes: 1
Reputation: 49085
There's two common patterns for this type of problem:
1) subclassing/inheritance to achieve polymorphic dispatch
2) table-driven programming
The basic idea is that you place the information that allows you to make a decision in a table, then write code that traverses the table. If you hold your head at a funny angle, polymorphic methods are just table-driven programming that is built directly into the language. Table-driven techniques offer you these benefits: more declarative, smaller code size, easy to extend/add new cases, clearer.
As others have noted, you could implement this pattern with a Dictionary.
Upvotes: 0
Reputation: 13655
This sounds like a job for a Finite-State Machine! You could even get fancy and create an external DSL because state-machines are very ameniable to this. In fact I just found a project on codeplex that appears to use a phone system as their primary example.
Upvotes: 0
Reputation: 5246
I don't know anything about VB6, but how about mapping the codes to "delegates" (dunno if that concept exists in VB6). The idea is: the input-code is a "key" to a dictionary returning the method to invoke (or Empty/Null/Nothing if no such code found).
UPDATE: If this is written in C#, couldn't you just put the codes into a
Dictionary<string, Action> OpCodes;
Then do something like:
if(OpCodes.ContainsKey(code))
OpCodes[code]();
UPDATE 2: It seems you have "layers" of conditionals. I guess this would map to "Dictionaries of dictionaries". But thinking about the problem: User types a sequence of choices, which should wind up in a certain behaviour, sounds like: Define "delegates" for each system behavior, and map to codes:
Like:
OpCodes["123"] = new Action(TechSupport.StartConversation);
Upvotes: 1
Reputation: 25513
If you rewrote it to create different classes to handle the different codes it would probably make your codebase more maintainable.
Something like
var codeHandler = CodeHandlerDecider.GetCodeHandlerFor(
foo.GetAccessCodeFromCaller());
codeHandler.HandleCode();
Then your CodeHandlerDecider would do something like this:
public static ICodeHandler GetCodeHandlerFor(string code)
{
if (DatabaseCheck1(code)
{
return new FirstCodeHandlerClass(code);
}
else if (LookForSomethingElseInDB(code)
{
return new SecondCodeHandlerClass(code);
}
else
{
return new ThirdCodeHandlerClass(code);
}
}
and then an example class would be
public class FirstCodeHandlerClass: ICodeHandler
{
public void HandleCode(string code)
{
parse = ParseCode(code);
dbValue = GetSomethingFromDB(parse);
if (OtherCheck1(dbValue)
{
// Launch the Pay Taxes project.
}
else if (OtherCheck2(dbValue)
{
// Launch the Uploaded File project
}
else
{
// Schedule Something or other project
}
}
}
and the interface would look like
public interface ICodeHandler
{
void HandleCode();
}
Upvotes: 0
Reputation: 48230
Maybe what you need is just a simple ExtractMethod to exclude large inner bodies of conditionals to separate methods.
Upvotes: 1