0

I have an abstract class "A" that has a property of an enum type "OutputType", and an abstract method calculation() that needs to do a certin calculation and to output the result in a double[] **according to the OutputType value.

I also have a classes defined as D1,D2,D3...D20, derive from A, Where each Di class implements A's calculation() method differently.

The problem is that not all of the OutputType values (the calculation() output types) are supported in every Di

For example:

If OutputType enum values are "Scaled", "NonScaled" and "ConstantLength",

A certin Di can support "Scaled" and "NonScaled" but not support "ConstantLength" and another Di can support all of the operation types,

My question is:

What is the right design pattern to implement this kind of behaiver?

Please avoid the obvious "Throw if the OutputType property recieves a value of a non supported operation" answers.



Edit:

Expanding the question:

Is there a way to notify the user with intellisense of all the supported operation so the user wont have to take the trial and error approach? (besides a proper documentation)

drKnows
  • 53
  • 6
  • It seems somewhat of a codesmell to have `OutputType` dictate the behaviour of `classification` *especially if* it's possible to provide an invalid `OutputType`. Why not have one method per type, and properly implement them via interfaces, with the common between all inside the abstract class? – Rob Dec 04 '16 at 12:17
  • There is no "common" type value , interface for each type is not a good idea , the type values can eventually change / extend, also the method is same , just the output type is different and i dont think you should define a method according to the output but according to the operation before outputing it. – drKnows Dec 04 '16 at 12:28

1 Answers1

0

Note that I've edited my answer completely dropping my previous approach using a precondition with code contracts.

I've been thinking about a solution that should be more powerful.

What about an abstract class which defines a protected virtual method for each possible operation type, and a public, non-overridable Calculate method could call the overriden protected virtual method?

public abstract class Calculation
{
    public Calculation()
    {
        // I store all overridable methods in a dictionary where keys
        // are the method names
        OverridableMethods = new Dictionary<string, Func<double[]>>()
        {
            { nameof(CalculateScaled), CalculateScaled },
            { nameof(CalculateNonScaled), CalculateNonScaled },
            { nameof(CalculateConstantLength), CalculateConstantLength }
        };


        // Then we get the first overriden method. If there're more
        // than one, it will pick the first occurence and others will be
        // discarded. It's up to the developer to override just one.
        MethodInfo overridenMethod = OverridableMethods.Keys
                .Select(methodName => GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic))
                .FirstOrDefault(method => method.DeclaringType != typeof(Calculation));

        Contract.Assert(overridenMethod != null, "No calculation operation implementation has been provided");

        // So some method was overriden and I store the method name
        OverridenMethodName = overridenMethod.Name;
    }

    private string OverridenMethodName { get; }
    private Dictionary<string, Func<double[]>> OverridableMethods { get; }

    protected virtual double[] CalculateScaled() 
    {
        throw new NotImplementedException();
    }

    protected virtual double[] CalculateNonScaled() 
    {
        throw new NotImplementedException();
    }

    protected virtual double[] CalculateConstantLength()
    {
        throw new NotImplementedException();
    }

    // Calculate just access the overridable method dictionary
    // to get the overriden method encapsulated by a Func<double[]>
    // and I immediately call it!
    public double[] Calculate() => OverridableMethods[OverridenMethodName]();
}

public class SomeCalculation : Calculation
{
    protected override double[] CalculateScaled()
    {
        return new double[] { 22 };
    }
}

So it really works!

    Calculation calculation = new SomeCalculation();

    // Prints out 22!
    Console.WriteLine(calculation.Calculate()[0]);
Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206
  • acctualy this is what my current approach is, did not know if its a good qay to go or not. – drKnows Dec 04 '16 at 10:58
  • @drKnows It's a good a approach. See reference source for `ICollection`: https://referencesource.microsoft.com/#mscorlib/system/collections/generic/icollection.cs,a9bf1395d3addc77. You'll see that it defines some constraints using a contract class – Matías Fidemraizer Dec 04 '16 at 11:01
  • @drKnows Design by contract is a very powerful paradigm. You're defining how others should use a portion of code. – Matías Fidemraizer Dec 04 '16 at 11:02
  • @drKnows Now I need to go with I'll post an alternate approach using a design pattern too... – Matías Fidemraizer Dec 04 '16 at 11:15
  • @drKnows check again my answer tonight ;) – Matías Fidemraizer Dec 04 '16 at 11:15
  • Yes it's just that i thought that if one finds himself in a situation where he needs to use contracts maybe there was something wrong with his design in the first place. – drKnows Dec 04 '16 at 11:27
  • Overrides cannot strengthen a base contract. That violates the LSP. – usr Dec 04 '16 at 12:15
  • @usr I've opened a new Q&A to expose if this would violate LSP or not. I believe that you're right, BTW the discussion is interesting enough to discuss it in another Q&A. Check it out here: http://stackoverflow.com/questions/40963811/liskov-substitution-principle-preconditions-and-abstract-methods – Matías Fidemraizer Dec 04 '16 at 21:25
  • @drKnows I've completely replaced my other approach. Now I believe I got to the definitive answer to your problem :D – Matías Fidemraizer Dec 04 '16 at 23:20
  • @Matías Fidemraizer Its a very nice thought but your solution does not handle a state where there are multiple operations in one class.. – drKnows Dec 06 '16 at 00:28
  • @drKnows So I understand that different operations should be sequentially executed... would it be some predefined order when executing them? Tonight I'll give you a small modification on my current approach to solve your issue to support more than one operation override. – Matías Fidemraizer Dec 06 '16 at 09:29
  • @drKnows I don't understand at all your return value (i.e. `double[]`). Does each index in that array corresponds to a result of each possible operation type? Index 0: ConstantLength, Index 1: Scaled, Index 2: NonScaled? Please elaborate more on how it really works... it's hard to provide a good solution without that information... – Matías Fidemraizer Dec 06 '16 at 10:28
  • No , the whole array corresponds to a single operation type, just like in your answer, the only thing is that if a certin class implement calculation class it can implement more then one operation not only one as in your answer, and then you get when calling calculation() depends on the argument sent to calculation constructor the corresponding operation output. – drKnows Dec 06 '16 at 14:15
  • @drKnows Would you add some sample code of how you would implement a `Calculate` supporting more than an operation type, please? – Matías Fidemraizer Dec 06 '16 at 20:47
  • Same as yours only with a minor change: Calculation calculation = new SomeCalculation(OutputType.Scaled); // Prints a calculation as a scaled output Console.WriteLine(calculation.Calculate()[0]); – drKnows Dec 06 '16 at 21:37
  • @drKnows Maybe we can simplify this a lot. Are you really against calling different methods per operation type? `CalculateScaled()`, `CalculateNonScaled()` or `CalculateConstantLength()`. So we throw away the enumeration, and the method that's not overriden the one that will throw a `NotSupportedException`. It's a common pattern in a lot of classes of .NET and third-party libraries. – Matías Fidemraizer Dec 06 '16 at 21:48