5
\$\begingroup\$

I wanted to consolidate solutions to disparate problems into a single program. I am worried that there is a interface, command pattern, or dictionary solution to what will become a massive switch statement here. However, the main in this program is essentially a meta-program which would switch between the miniature programs that it links to.

static void Main(string[] args)
{
    int eulerNumber;

    Console.WriteLine("Please enter the number of the Problem you want to solve.");

    while (!int.TryParse(Console.ReadLine(), out eulerNumber))
    {
        Console.WriteLine("Please enter a valid number.");
        Console.WriteLine();
        Console.WriteLine("Please enter the number of the Problem you want to solve.");
    }
    switch (eulerNumber)
    {
        case 1:
        //solve problem 1.
        break;

        case 2:
        //solve problem 2.
        break;
        case 3:
        //solve problem 3.

        break;
        etc. etc. etc.
    }
}

This is not an elegant solution, but it seems to mirror the situation quite well, as I see it.

Should I scratch this approach? It seems to me to violate OCP but at the same time, it isn't chaotic, and isn't that much to keep track of, - just tack one more case onto the end, create a new class, and another problem is functional. Should I look into some other structure to make it easier to modify?

\$\endgroup\$
0

5 Answers 5

10
\$\begingroup\$

If I were to solve this, adding the new code to the switch seems to me like overkill.

I would try writing an application that uses plugins in a searchable directory. Then you solve each problem in its own plugin and the main program just consists of the logic to find and execute the plugins.

Of course to enhance finding the plugins there some possible designs:

Naming the plugin after the solved problem

Name the plugin file by a given structure so it is obvious which problem is solved:

ep-001.dll
ep-002.dll
...

That has the problem of forbidding user defined names, however it offers efficiency in that you can directly load only the needed plugin if you know the number.

Give the plugin class a problemNumber function

The other solution is to give the plugin class (that you will need anyway for a common interface to query for the solution) a function that returns the problem number. This allows for arbitrarily named plugins, but also for duplicates and has the disadvantage of loading all plugins that are there to see if the one containing the requested solution is there.

Since you are not that much efficiency bound on this problem I would suggest the second alternative because it has a much nicer OO interface (IMHO).

\$\endgroup\$
2
  • \$\begingroup\$ +1 this is definitively a great example where a plugin architecture works best. \$\endgroup\$ Commented Dec 24, 2013 at 16:03
  • \$\begingroup\$ Supposed to avoid thanks, but this is my first post so... I'll just revel in the interwebs. Yeah, it's overkill but student practicing for rl here. The thing that pulls these disparate problems together is the desire for accessibility. \$\endgroup\$ Commented Jan 1, 2014 at 6:29
8
\$\begingroup\$

Instead of making one big program to solve all the problems, I would just make one program per problem. There is no logical connection between the Euler problems, so it's best to keep the solutions separate, too.

\$\endgroup\$
3
\$\begingroup\$

I think a plugin architecture would be a little overkill unless you're interested in versioning or deploying the sub-modules separately. You haven't shown the sub-modules (the actual solvers) but I'd probably define an interface for them and then use a Dictionary instead of the switch statement. Really, it's pretty minor, but I see a couple of advantages of the Dictionary:

  • You go from three lines for a new definition to a single line; this will start to make a difference after 10 or so sub-modules since you'll fit on the same screen
  • It makes explicit that this main method is solely for determining the sub-module; the switch statement leaves room for other code, so it needs to be scanned when reading the code
  • Since the dictionary is explicitly data, it'd be easier to abstract out later if you wanted
  • You may want to inject other components into your sub-modules (such as a logger, or an abstracted input/output routine) and having a single init routine will make that easier.

So, a quick implementation would be something like:

interface ISolver {
    void Solve();
}

class EulerOne : ISolver {
}


static void Main(string[] args)
{
    // Using a Func to allow a new copy and/or some custom init if needed
    Dictionary<int, Func<ISolver>> solvers = new Dictionary<int, Func<ISolver>>() {
       { 1, () => new EulerOne() },
       { 2, () => new EulerTwo() },
       { 3, () => new EulerThree() }
    };

    int eulerNumber;

    Console.WriteLine("Please enter the number of the Problem you want to solve.");

    while (!int.TryParse(Console.ReadLine(), out eulerNumber))
    {
       Console.WriteLine("Please enter a valid number.");
       Console.WriteLine();
       Console.WriteLine("Please enter the number of the Problem you want to solve.");
    }

    var solverBuilder = solvers[eulerNumber];
    var solver = solverBuilder();
    solver.Solve();

    return 0;
}
\$\endgroup\$
1
  • \$\begingroup\$ solvers would be solverBuilders then, and the lost and lonely _solver, too. \$\endgroup\$
    – devio
    Commented Dec 29, 2013 at 11:55
1
\$\begingroup\$

I took advantage of the partial keyword.

For example, Problem 21 would go into Question021.cs.

    namespace ProjectEuler
    {
        partial class Questions
        {
            public void Question021()
            {
                Console.WriteLine("Question021: 42");
            }
        }
    }

This way all your answers can go into their own file. I also started .cs file called Common that has all of my helper functions in it (generating primes, triangle numbers, Fibonacci sequence, etc) that all the questions can take advantage of.

\$\endgroup\$
0
0
\$\begingroup\$

Not sure how to do it in C#, but if you don't want to change the names of the individual functions, I would recommend putting them in an indexed table.

If you don't want to create individual functions for each problem, your current solution is probably the way to go.

\$\endgroup\$

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.