1

I've got myself into a bit of a mess. I've got code coming from Main, through myMethod and down to OtherMethod. One of the parameters can be either an int or a string, which affects OtherMethod but none of the intervening methods.

public static void Main()
{
    myMethod(number: 10);
    myMethod(letters: "ten");
}

public static void myMethod(int number = 0, string letters = null)
{
    //long entirely shared code with calls to:
    OtherMethod(number,letters);
}

public static void OtherMethod(int number = 1, string letters = null)
{
    //Lots of shared code, embedded multiple times within this code is essentially:
    if (letters != null)
    {
        Console.WriteLine("letters!");
    }
    else
    {
        Console.WriteLine("numbers!");
    }
}

OtherMethod cares about when I'm passing it int or string, but none of the intervening methods care. I would also find splitting OtherMethod without unnecessary code reuse very hard.

The solution I have provided works, but is probably an anti-pattern. Because, for example, a call to myMethod(10,"ten") would silently throw 10 in the bin.

Would changing it to private mymethod(int number = 0, string = null) called by public (int number) and public (string letter) for each level be a terrible idea?

I though of instead passing a class with both int and string and instructions of which to use right from the top to bottom. I suspect this is the solution. But it feels as OTT as it does OO.

My real use case is that I'm randomly generating data at the bottom of this chain and I can either provide a seed (int), or cheat and provide the data to generate (string). It occurs to me that what I'm doing may be a procedural dependency injection bodge,

3 Answers 3

4

You are on the right track thinking you need to inject a class rather than the int and string parameters, but you actually want to inject one of two classes implementing a common interface: one with an int specialization and one with a string specialization.

To avoid the duplication in OtherMethod, you can make that common code that calls implementation-specific helper functions as necessary. In other words, wherever you have if (letters != null) put a call to HelperMethod().

I don't know C#, so consider this pseudocode, but it would look something like this:

public static void Main()
{
    myMethod(new IntGenerator(10));
    myMethod(new StringGenerator("ten"));
}

public static void myMethod(Generator gen)
{
    //long entirely shared code with calls to:
    gen.OtherMethod();
}

abstract class Generator {
    abstract void HelperMethod();

    public void OtherMethod()
    {
        //Lots of shared code, embedded multiple times within this code is:
        HelperMethod();
    }
}

class IntGenerator extends Generator
{
    IntGenerator(int value) {}

    void HelperMethod() 
    {
        Console.WriteLine("numbers!");
    }
}

class StringGenerator extends Generator
{
    StringGenerator(string value) {}

    void HelperMethod() 
    {
        Console.WriteLine("letters!");
    }
}

Yes, this creates a lot more functions, but if you divide it up properly, they are going to be much easier to maintain.

1

You could use Core.Choice or a delimited union of your own liking.

If you need to pass either one or the other value, just do not allow to pass both.

3
  • Isn't that just f#?
    – Nathan
    Commented Jul 2, 2014 at 8:01
  • @NathanCooper: I thought this type is reusable across CLR languages?
    – 9000
    Commented Jul 2, 2014 at 12:44
  • Can't see that we do. Looks sort of a like a c++ union, which we also do not have.
    – Nathan
    Commented Jul 2, 2014 at 12:52
1

I would go much deeper than Karl and convert the whole myMethod and OtherMethod into a class. Then you can use inheritance and abstract method to differentiate the cases.

public abstract class Generator
{
    public void Invoke()
    {
        //long entirely shared code with calls to:
        OtherMethod();
    }

    protected abstract void OtherMethod();
}

public class IntGenerator : Generator
{
    private int _number;
    public IntGenerator(int number)
    {
        _number = number;
    }

    protected override void OtherMethod()
    {
        Console.WriteLine("numbers!");
    }
}

public class StringGenerator : Generator
{
    private string _letters;
    public StringGenerator(string letters)
    {
        _letters = letters;
    }

    protected override void OtherMethod()
    {
        Console.WriteLine("letters!");
    }
}

class Program
{
    static void Main(string[] args)
    {
        new IntGenerator(10).Invoke();
        new StringGenerator("ten").Invoke();
    }
}

The major advantage is that in your code, you can just use Generator and swap around whenever you are using Int or String without need to change the code.

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.