2

I have X classes with different information and calculation methods that should be shared but could be overwritten, so:

class Rule1 {
    int type = 1;
    string name = "Rule";
    public float Calc()
    {
        return 1 + 2 + type; // SAME
    }
}

class Rule2 {
    int type = 2;
    string name = "Rule2";
    public float Calc()
    {
        return 1 + 2 + type; // SAME
    }
}

class Rule3 {
    int type = 3;
    string name = "Rule3";
    public float Calc()
    {
        return 3 + 4 + type; // DIFFERENT
    }
}

What I want to write in the calling methods are like this:

class Calculator
{
    public void Do(List<IRules> ruleList)
    {
        foreach(var rule in ruleList)
        {
            rule.Calc();
        }
    }
}

So how would my interface should have to look like and how to abstract the calc method as default implementation but overwriteable?

PassionateDeveloper
  • 14,558
  • 34
  • 107
  • 176

4 Answers4

6

If you have an implementation that's correct for most inheritors but not all, mark it virtual and override it in a derived class:

public class BaseCalculation
{
    public virtual float Calculate()
    {
        return 42;
    }
}

public class HalfCalculation : BaseCalculation
{
    public override float Calculate()
    {
        return 21;
    }
}

You can now use the base class, BaseCalculation, instead of an interface. If you insist on still using an interface, then you can still define the Calculate() method in an interface and apply that to your base class:

public interface IRules
{
    float Calculate();
}

public class BaseCalculation : IRules
{
    // same as above

With the added benefit that you can apply this interface to other classes that also calculate something, but without any of the logic that's in BaseCalculation.

CodeCaster
  • 147,647
  • 23
  • 218
  • 272
  • I don't have to repeat all of the OP's code to explain a principle that they (and future visitors) can apply. – CodeCaster Oct 08 '19 at 14:52
  • 1
    Yeah we've ran across each other more than once the last couple of days and let's say our approaches in explaining stuff to people don't match. – CodeCaster Oct 08 '19 at 14:54
2

You can try this using an asbtract base class and polymorphism on Calc.

What is polymorphism

No need to use interface unless you have a real and good reason to do that.

What is the difference between an interface and a class

We use a protected constructor to propagate the parameters.

class Calculator
{
  public void Do(List<RuleBase> ruleList)
  {
    foreach ( var rule in ruleList )
    {
      // do what you want with the result of rule.Calc();
    }
  }
}
public abstract class RuleBase
{
  public int Type { get; private set; }
  public string Name { get; private set; }
  public abstract float Calc();
  protected RuleBase(int type, string name)
  {
    Type = type;
    Name = name;
  }
}
public class Rule1 : RuleBase
{
  public override float Calc()
  {
    return 1 + 2 + Type;
  }
  public Rule1()
    : base(1, "Rule1")
  {
  }
  protected Rule1(int type, string name)
    : base(type, name)
  {
  }
}
public class Rule2 : Rule1
{
  public Rule2()
    : base(2, "Rule2")
  {
  }
  protected Rule2(int type, string name)
    : base(type, name)
  {
  }
}
public class Rule3 : RuleBase
{
  public override float Calc()
  {
    return 3 + 4 + Type;
  }
  public Rule3()
    : base(3, "Rule3")
  {
  }
  protected Rule3(int type, string name)
    : base(type, name)
  {
  }
}

If you want an interface create it and add it to RuleBase:

public interface IRule
{
  float Calc();
}

public abstract class RuleBase : IRule
1

You are searching for inherited class and virtual method (wich allows override) :

class GenericRule {
    int type = 1;
    string name = "Rule";
    public virtual float Calc()
    {
        return 1 + 2 + type; // SAME
    }
}

class Rule3 : GenericRule
{
    int type = 3;
    string name = "Rule3";
    public override float Calc()
    {
        return 3 + 4 + type; // DIFFERENT
    }
}

class Calculator
{
    public void Do(List<GenericRule> ruleList)
    {
        foreach(var rule in ruleList)
        {
            rule.Calc();
        }
    }
}
nellowl
  • 339
  • 3
  • 16
0

Every class must support the interface. The implementation of method Calc in each class is not important. They can be the same or different.

If you want to have a standard implementation (virtual implementation), you could use a base class and overwrite the method in some classes (in your example Rule3).

If you do not want a standard implmentation (virtual implementation), you could use an abstract base class and overwrite the method in all classes (in your example Rule1, Rule2 and Rule3).

But that has nothing to do with the interface you want to use.

Complete working example (just using the interface):

using System;
using System.Collections.Generic;

namespace Temp
{
    class Program
    {
        static void Main(string[] args)
        {
            var calc = new Calculator();
            var rules = new List<IRule>() { new Rule1(), new Rule2(), new Rule3() };
            calc.Do(rules);
            Console.WriteLine(calc.GetTotal());
            Console.ReadKey();
        }
    }

    public interface IRule
    {
        float Calc();
    }

    public class Rule1 : IRule
    {
        int type = 1;
        string name = "Rule";
        public float Calc()
        {
            return 1 + 2 + type; // SAME
        }
    }

    public class Rule2 : IRule
    {
        int type = 2;
        string name = "Rule2";
        public float Calc()
        {
            return 1 + 2 + type; // SAME
        }
    }

    public class Rule3 : IRule
    {
        int type = 3;
        string name = "Rule3";
        public float Calc()
        {
            return 3 + 4 + type; // DIFFERENT
        }
    }

    public class Calculator
    {
        private float _total = 0;

        public void Do(List<IRule> ruleList)
        {
            foreach (var rule in ruleList)
            {
                _total += rule.Calc();
            }
        }

        public float GetTotal()
        {
            return _total;
        }
    }
}
RWC
  • 4,697
  • 2
  • 22
  • 29
  • Okay, fair. He wrote "that should be shared". In that case he should use a virtual/ abstract class, which I explained. Like I said, it has nothing to do with the interface. – RWC Oct 08 '19 at 15:00