-2

Using C#, I got a class called BaseConfigurations which handles CRUD operations. This class also contains a protected method which is used inside the CRUD handling methods:

public class BaseConfigurations : ICRUDHandler
{
    public virtual bool CreateSetting(string someData)
    {
        //some logic
        ProtectedMethod();
        //more logic
    }
    
    protected virtual void ProtectedMethod() { ... }
}

Now there's a different version which can handle the CRUD operation a bit different:

public class BaseConfigurationsV2 : BaseConfigurations { ... }

There are 2 instances of BaseConfigurations and BaseConfigurationsV2 living at the same time and should be invoked based on some logic I have.

Now there's another class which handles specifically secret data:

public class SecretConfigurations : BaseConfigurations
{
   public ICRUDHandler _crudHandler;
   public SecretConfigurations(ICRUDHandler handler) 
   { 
      _crudHandler = handler; //this handler gets either the instance of `BaseConfigurations` or `BaseConfigurationsV2` 
   }

   public override bool CreateSetting(string data)
   {
      _crudHandler.CreateSetting(data);
      //adds additional logic
   }

   public override void ProtectedMethod() { ... }
}

My problem here is that because the ICRUDHandler which SecretConfigurations receives is either BaseConfigurations or BaseConfigurationsV2, it doesn't know the protected method which was overriden in SecretConfigurations and doesn't invoke it.

The easy way to fix it is to probably just make another class of SecretConfigurationsV2 which will derive from BaseConfigurationsV2 and then have the handler be initialized to either SecretConfigurations or SecretConfigurationsV2, but that involves code duplication.

How would you design a fix for this issue?

8
  • 1
    Your question is unclear. How does "ICRUDHandler" look like? What is the reason for deriving BaseConfigurationsV2 from BaseConfigurations? What is the reason for deriving SecretConfigurations from BaseConfigurations? The whole purpose of why you are using inheritance here is a mystery to me.
    – Doc Brown
    Feb 8 at 15:19
  • research "template method". That plus an abstract class works well in an inheritance situation where a base class method contains common behavior and other code in that method is overridden by subclasses.
    – radarbob
    Feb 8 at 18:29
  • ICRUDHandler is an interface with general signatures of CRUD operations - like the CreateSetting example, it also have read, update, delete. About BaseConfigurationsV2 - the service itself has 2 versions and each should handle the CRUD operations in a slightly different way in a way such that BaseConfigurationsV2 adds some more logic to the logic in BaseConfigurations. About SecretConfigurations, it was there before BaseConfigurationsV2 and then it derived from BaseConfiugrations so I tried not changing alot, but I can change it if needed.
    – CodeMonkey
    Feb 8 at 19:59
  • @radarbob a template method does not solve the problem. SecretConfigurations should handle the settings either as a BaseConfigurations or as a BaseConfigurationsV2. Both options are of 2 different versions of the same service which lives at the same time
    – CodeMonkey
    Feb 8 at 21:06
  • Comments are not a good place to clarify questions. Use the "edit" button for adding expressive examples to your question, then we can see what we can do for your.
    – Doc Brown
    Feb 8 at 22:41

3 Answers 3

2

If you run into a problem with inheritance, switch to composition instead.

Its hard to say more without knowing what the methods and classes do.

public class Configurations : ICRUDHandler
{
    public Configurations(IProtectedMethod protMethod)
    public virtual bool CreateSetting(string someData)
    {
        //some logic
        protMethod.ProtectedMethod(this.whateverData);
        //more logic
    }
}
   

public class ProtV1 : IProtectedMethod 
{
...
}
public class ProtV2 : IProtectedMethod 
{
...
}
public class ProtSecret : IProtectedMethod 
{
...
}

etc
12
  • What about all the other classes I mentioned? Other types of configurations handling
    – CodeMonkey
    Feb 8 at 12:09
  • you only mention the three and the only method that changes is protectedMethod. So I dont have much to go on.
    – Ewan
    Feb 8 at 12:19
  • But each class has entirely different logic for each virtual method. Here you only describe using composition for the implementation of the protected method, but not how to get the correct logic of the other methods
    – CodeMonkey
    Feb 8 at 12:38
  • The same pattern would apply, refactor the logic into injected sub classes. difficult to say without examples
    – Ewan
    Feb 8 at 15:05
  • I am sure you are right that the OP can solve their problems by favoring composition over inheritance. Where I am not so sure is whether "ProtectedMethod" needs to become subject of the strategy pattern, as shown in your answer, or "CreateSetting".
    – Doc Brown
    Feb 8 at 15:23
1

So there's a lot of questions/concerns with your design, which the comments have already adequately addressed. I would suggest going back to the drawing board with this rather than trying to make your current design work.

But I do want to answer your specific question as a point of learning.

My problem here is that because the ICRUDHandler which SecretConfigurations receives is either BaseConfigurations or BaseConfigurationsV2, it doesn't know the protected method which was overriden in SecretConfigurations.

This is not a problem, it is the language working by design. When you design a contract to do something, e.g.:

public interface IStringGenerator
{
    string Generate();
}

and then you write code that accepts a parameter of this interface type:

public class MyService
{
    private readonly IStringGenerator generator;

    public MyService(IStringGenerator generator)
    {
        this.generator = generator;
    }

    public void DoAThing()
    {
        string result = this.generator.Generate();

        // do something with result
    }
}

Then you are by design stating that you accept any implementation of IStringGenerator. The logical conclusion there being that you intentionally don't want to have to be actively aware of the specific implementations of IStringGenerator that could possibly be passed into your MyService.

If you needed to know for a fact that MyService only wishes to accept MyFancyGenerator : IStringGenerator and not MyBasicGenerator : IStringGenerator, then you should explicitly ask for that type:

public class MyService
{
    private readonly MyFancyGenerator fancyGenerator;

    public MyService(MyFancyGenerator fancyGenerator)
    {
        this.fancyGenerator = fancyGenerator;
    }

    public void DoAThing()
    {
        string result = this.fancyGenerator.Generate();

        // do something with result
    }
}

The short answer here is "don't downcast into a base type when you care about the specific derived type". Doing so means you are writing code that does not match how you expect it to behave, which is going to always be a source of problems and confusion.

2
  • I edited the question with a more detailed explanation. Hopefully it clears up the mess
    – CodeMonkey
    Feb 9 at 7:34
  • @CodeMonkey None of the information you added invalidates the core of any of the posted answers. It's not that we don't understand your idea, it's that we're suggesting you to approach this differently from the ground up.
    – Flater
    Feb 11 at 9:09
1

Though your question is still somewhat unclear to me, after thinking a while about it, I guess what you want is to exchange the the ProtectedMethod called inside BaseConfigurations.CreateSetting when called in the context of SecretConfigurations.CreateSetting. For this purpose, I would use a run time mechanics to decide between the calls, not a compile time mechanics.

This can be achieved by making ProtectedMethod a run time parameter of CreateSetting, something along the line of this:

public class BaseConfigurations : ICRUDHandler
{
    public virtual bool CreateSetting(string someData)
    {
         CreateSetting(someData,ProtectedMethod1);
    }

    // no need to make this virtual here
    public bool CreateSetting(string someData, Action protectedMethod)
    {
        //some logic
        protectedMethod();
        //more logic
    }
    
    protected void ProtectedMethod1() { ... }
}

And in SecretConfigurations:

public class SecretConfigurations : ICRUDHandler   
{
   // not you wrote _crudHandler is always of type BaseConfigurations
   // or a subtype of it!
   public BaseConfigurations _crudHandler;

   // ...
   public override bool CreateSetting(string data)
   {
      _crudHandler.CreateSetting(data,ProtectedMethod2);
      //...
   }

   protected void ProtectedMethod2() { ... }
}

I renamed your ProtectedMethod to ProtectedMethod1 and ProtectedMethod2 to make the distinction more clearer. If your ProtectedMethod has parameters or return types, you may have to replace Action by Action<T1,T2,...>, or Func<T1, T2, ...>. And if it becomes more complicated, you may introduce an interface IProtectedMethod for it and pass functor objects instead of simple delegates. But I guess you get idea.

Note this is somewhat similar to what Ewan's answer says, with the difference that the specific CRUD handler can be constructed first, and the decision which protected method is called is made later, depending on the context.

6
  • I edited the question with a more detailed explanation. Hopefully it clears up the mess
    – CodeMonkey
    Feb 9 at 7:33
  • Did you see the edit?
    – CodeMonkey
    Feb 9 at 17:32
  • @CodeMonkey: yes, but did you read my answer? Nothing in your extra explanation tells me my answer cannot be applied to it.
    – Doc Brown
    Feb 9 at 20:31
  • Yeah I initially thought of maybe adding another handler member just for the action but passing it as a parameter is cleaner. Even though it does seem that the answer can be applied, I believe you did it based on my initial thoughts of how to solve the issue I display in my edit. But let's say you only saw the edit itself as the question without the first part, you might have a better idea of how to design the system to answer that need displayed there?
    – CodeMonkey
    Feb 9 at 21:32
  • 1
    @CodeMonkey: I still think your code examples are too contrived, and nothing in your extra explanation has changed this So it is up to you to transfer the suggestions I made to your real code, the parts of which I could only make blind guesses how it looks like . The gist of my answer is, when you want to change certain behaviour in a context-dependend manner, consider to use a more functional approach and make the actions which are different functional parameters.
    – Doc Brown
    Feb 10 at 7:25

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.