2

I have two types, i.e AdditionCalc and AggregateCalc which have two methods:

  1. Calculate: Returns Model. No version code.
  2. Calculate: Doesn't return anything. Includes Versioning code. Stores the data in database.

Code :

public abstract class BaseCalculator 
{
     private readonly IVersion _version;
     protected BaseCalculator(IVersion version)
     {
            this._version = version;
     }
     public void Calculate(Request r)
     {
          CalculateInternal();
          //version code here
     }
     protected abstract void CalculateInternal(Request r);
     public abstract StatisticsModel Calculate(StatisticsRequest model);
}

public class AdditionCalc : BaseCalculator
{
    public AdditionCalc() : base(new Version(ConfigurationManager.ConnectionStrings["dbConnectionString"].ConnectionString)) { }
    public override StatisticsModel Calculate(StatisticsRequest model)
    {
         //no version code here
    }
}

public class CalculatorFactory
{
    public BaseCalculator GetCalculatorByType(int type)
    {
        switch (type)
        {
            case (int)Types.Addition:
                return new AdditionCalc();
            case (int)Types.Aggregate:
                return new AggregateCalc();
        }
    }
}

Now the problem is when I call Calculate method which returns StatisticsModel, I don't want to call base constructor because for this method I don't require version code, but every time when I call this method, the default constructor gets called and it initializes the version variable of base class.

This is how I am calling the appropriate Calculate method:

var calculatorFactory = new CalculatorFactory();
var type = calculatorFactory.GetCalculatorByType((int)Types.Addition);
type.Calculate(StatisticsRequest);

Note: I don't want to pass version dependency from calling code.

So how do I design the above code so that Version code doesn't get called for Calculate method which returns StatisticsModel?

Update :

public class AdditionCalc : BaseCalculator
{
    public AdditionCalc() : base(new Version(ConfigurationManager.ConnectionStrings["dbConnectionString"].ConnectionString)) { }
    public override StatisticsModel Calculate(StatisticsRequest model)
    {
         //no version code here
    }
    protected override void CalculateInternal(Request r)
    {
    }
}

public class AggregateCalc : BaseCalculator
{
    public AggregateCalc() : base(new Version(ConfigurationManager.ConnectionStrings["dbConnectionString"].ConnectionString)) { }
    public override StatisticsModel Calculate(StatisticsRequest model)
    {
         //no version code here
    }
    protected override void CalculateInternal(Request r)
    {
    }
}
I Love Stackoverflow
  • 6,738
  • 20
  • 97
  • 216
  • 1
    The version variable in the base class is being set via the constructor chain, starting at `new AdditionCalc();`. It has nothing to do the `Calculate()` method. – spodger Apr 30 '19 at 13:48
  • @spodger Agreed but then how do i inject Version dependency then and where? because i require it for my 2nd method – I Love Stackoverflow Apr 30 '19 at 13:51
  • So have another protected constructor in `BaseCalculator` that does not accept an `IVersion version`, and chain into that from the `AdditionCalc` constructor. – GSerg Apr 30 '19 at 13:51
  • @GSerg But then what will be the constructor in my 2 child classes ie AdditionCalc and AggregateCalc? – I Love Stackoverflow Apr 30 '19 at 13:54
  • It will be `: base()` instead of `: base(new Version(...))`. – GSerg Apr 30 '19 at 14:04
  • @GSerg But when I want to pass Version dependency then how should i do that? – I Love Stackoverflow Apr 30 '19 at 14:05
  • 1
    "but every time when I call this method, the default constructor gets called" - no, the constructor isn't called every time you call the method. I'm finding it hard to understand exactly what you're asking here, particularly as the constructor calls and method calls seem to be a bit confused in the question. – Jon Skeet Apr 30 '19 at 14:10
  • @JonSkeet Sorry for creating confusion in the question.I have 2 derive types(Addition and Aggregate ) and both the derive types have 2 methods.1 method just execute some code and return model and another executes code,uses algorithm to create version and store inside database.Now the problem is when i create object of my 2 derive types,Versioning variables gets initialized which i dont want because for my 1st method I simply dont require version. – I Love Stackoverflow Apr 30 '19 at 14:13
  • 2
    @ILoveStackoverflow: It sounds like you have a problem with your types trying to do too much then. Maybe each type should only have *one* method, so you can construct the calculator with only the amount of information it actually needs. – Jon Skeet Apr 30 '19 at 14:16
  • If your base class is doing things you don't want it to do with dependencies you don't want it to use, it sounds like the problem is that you shouldn't be inheriting from that base class. One of the problems with inheritance is that down the line we find ourselves wanting part of what we inherited but not wanting other parts. – Scott Hannen Apr 30 '19 at 14:19
  • @JonSkeet But Version is involved for both the derived types and both the derive types derives from BaseCalculator – I Love Stackoverflow Apr 30 '19 at 14:20
  • @ScottHannen But my base class contains some common code for my 2nd method which involves version and database store part.Versioning algorithm is same for both the derive types hence i dont want to duplicate that code for both the derive types – I Love Stackoverflow Apr 30 '19 at 14:21
  • 1
    Yes, I understand that - but if one of your calculators only *sometimes* needs Version, then maybe that should be two different types. – Jon Skeet Apr 30 '19 at 14:21
  • @JonSkeet 2 different types ? What different types ? I did not understand.Can you please elaborate a little bit ? – I Love Stackoverflow Apr 30 '19 at 14:40
  • 1
    I'm saying that you should possibly have AdditionCalc1 and AdditionCalc2 (with better names, obviously), each of which only does one thing. Does anything *need* a single calculator to be able to handle both of your methods? If not, don't force any single implementation to do both. – Jon Skeet Apr 30 '19 at 15:03
  • @JonSkeet Sample Design Cragin have showed in his answer,Is that what you are suggesting ? – I Love Stackoverflow May 01 '19 at 06:41
  • 1
    @ILoveStackoverflow: That sort of thing, yes. Unfortunately giving more concrete details than that would require more concrete context about what you're trying to achieve, including knowing why you've got two different calculation methods, and how they're used – Jon Skeet May 01 '19 at 06:45
  • @JonSkeet My 2nd method is consume by background service(windows service) and 1st method is consume by MVC application client to show model(StatisticsModel) result on UI that is why I have created 2 method. – I Love Stackoverflow May 01 '19 at 06:47
  • Well it sounds like it would be fine for those to be in two different classes then, perhaps implementing two different interfaces. – Jon Skeet May 01 '19 at 07:02
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/192659/discussion-between-ilovestackoverflow-and-jon-skeet). – I Love Stackoverflow May 01 '19 at 07:03

4 Answers4

3

Your two calculator classes should not be related to each other. They should implement a common interface, but share no common logic.

So you'd have one class that supports the versioning stuff, and one that doesn't know anything about versioning. Each is 100% focused on its own logic and neither have to be coupled/related to or interact with the other.

If you later do have some common behavior make sure it is indeed common behavior and put it in a base class that both of your classes inherit.

As you have it now, you're overriding the versioning behavior (and not even completely). When you are using inheritance, you want to add capabilities, not subtract them, which is kind of what your current model is doing.

Take a look at SOLID for some further guidance.

Consider,

interface ICalculator
{
    void Calculate() { ... }
}

abstract class CalculatorThatCaresAboutVersion : ICalculator
{
    void CalculatorThatCaresAboutVersion(IVersion version) { ... }
}

abstract class CalculatorThatDoesntCareAboutVersion : ICalculator
{
    void CalculatorThatDoesntCareAboutVersion() { ... }
}

You have two kinds of calculators that don't know or care about the other kinds. You can inherit from them if you need to, and the operation(s) associated with version don't have anything to do with the operation(s) in the other calculators.

That gives you the freedom to implement each as needed without affecting the other: no questions like "well how do I not initialize version if I don't care about it?" Because there's a natural separation of concerns, the question never even arises.

Kit
  • 20,354
  • 4
  • 60
  • 103
  • Thank you so much for the valuable advice.Appreciated but i am bit confused.Could you please include sample design code of what it looks like please ? – I Love Stackoverflow Apr 30 '19 at 14:27
  • Upvoted for more detail answer.Where should be my calculate method which only returns model(1st method ) ? – I Love Stackoverflow May 01 '19 at 06:44
  • Honestly, don't know. I don't know what these methods do. It seems like the two `Calc...` methods in each of your classes are not related, so those methods should be on separate interfaces with classes that separately implement those interfaces. But there's not enough context for me... for example, your factory returns a base calculator, but is there more than one callsite to this factory? Or is there one callsite but then both calculate methods then called. I don't think I can give you a great answer without knowing more. – Kit May 01 '19 at 07:20
  • I have updated question with both the derived type classes.There is 1 more place from where factory is being called. – I Love Stackoverflow May 01 '19 at 10:20
1

If I'm not wrong, you need version only for Calculate(Request r) and not for Calculate(StatisticsRequest model)

Also you are ok with hiding the dependency in the class you are using, i.e. you don't want the client code to pass the version (which may not be a good idea).

What about removing the IVersion parameter for BaseCalculator and retrieve the value you need in the Calculate method? Something like this:

public abstract class BaseCalculator 
{
    private readonly IVersion _version;
    protected BaseCalculator()
    {
    }

    public void Calculate(Request r)
    {
        this._version = GetVersion();
        CalculateInternal();
        //version code here
    }
    protected virtual void GetVersion() //protected virtual or abstract, pick the best for your scenario.
    {
        return new Version(ConfigurationManager.ConnectionStrings["dbConnectionString"].ConnectionString);
    }

    protected abstract void CalculateInternal(Request r);
    public abstract StatisticsModel Calculate(StatisticsRequest model);
}

The protected GetVersion method can be overridden in the derived classes (or can be an abstract method if your BaseCalculator doesn't know how to create the Version object, forcing derived classes to supply an implementation).

Gian Paolo
  • 4,161
  • 4
  • 16
  • 34
  • Thank you so much for answer and it looks good as well but tomorrow if I decide to remove version part then I have to change this class.Isnt this violating Open and Closed principle ? Please correct me if i am wrong – I Love Stackoverflow Apr 30 '19 at 14:29
  • @ILoveStackoverflow Just reading now one of your above comment: _and another executes code,uses algorithm to create version and store inside database_ You are worried about Open and Closed principle, here it seems you have a problem with the Single responsibility principle. If your BaseCalculator class does not save anything to db, does it magically get much easier? (and solve Open/Close problem?) Can the method `Calculate(Request r)` returns something, and a different class handle the saving on db for that result? Does `IVersion` disappear from BaseCalculator class in this case? – Gian Paolo Apr 30 '19 at 15:22
  • Calculate(Request r) doesnt returns anything as all statistics are being saved in database because its been called by windows service and I do have different class handling db save operation.I will still upvote your answer for your kind efforts towards helping me :) – I Love Stackoverflow May 01 '19 at 06:49
  • so let your windows service call something else. A class that first uses the right BaseCalculator (even better, the right implementation for an `ICalculatorInterface`), and then pass its result to another class which only responsability is to save on db the result. In this way you are decoupling the calculate responsability and the save responsability. Only the second one will need the Version – Gian Paolo May 01 '19 at 07:29
  • I have updated question with both the derived type classes. – I Love Stackoverflow May 01 '19 at 10:21
1

What you are explaining sounds like optional dependency.

One way to resolve it without significantly changing the design is to defer IVersion creation to the point when it's really needed by replacing the IVersion constructor argument with some sort of a factory - for instance Func<IVersion>, and eventually combine it with Lazy<T> field if it needs to create just once.

e.g. something like this:

public abstract class BaseCalculator 
{
    private readonly Func<IVersion> _versionFactory;
    protected BaseCalculator(Func<IVersion> versionFactory)
    {
        _versionFactory = versionFactory;
    }
}    

and inside the method(s) which needs IVersion

var version = _versionFactory();
// version code here

or

public abstract class BaseCalculator 
{
    private readonly Lazy<IVersion> _version;
    protected BaseCalculator(Func<IVersion> versionFactory)
    {
        _version = new Lazy<IVersion>(versionFactory);
    }
}    

and

var version = _version.Value;
// version code here

In both cases the derived class constructor code would be something like this (just add () => before the current base call code):

public AdditionCalc() : base(() => new Version(ConfigurationManager.ConnectionStrings["dbConnectionString"].ConnectionString)) { }
Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343
0

What you want to accomlish would require overriding the base constructor which cannot be done, see this post.

Edney Holder
  • 1,140
  • 8
  • 22