Core question explained via code:
Say I have an interface
public interface IWorker
{
void DoWork();
}
Now, setup an abstract class implementing this interface. I want this abstract class's DoWork()
function logic to be the ONLY possible implementation of this function. Subclasses from this abstract class SHOULD NOT be able to override the functionality. Instead, they are forced to provide some "helper" functions to aid in the DoWork()
implementation:
public abstract class AbstractWorker : IWorker
{
// I cannot force this to be "sealed" - sealed keyword cannot be applied. All subclass's
// could override this logic if they wanted to
public void DoWork()
{
if (ShouldProcess()) Process();
}
public abstract bool ShouldProcess();
public abstract void Process();
}
public class Worker : AbstractWorker
{
// I DON'T want this to be possible - overriding this implementation.
// I want forced usage of the abstract class implementation.
public new void DoWork()
{
// Look at me, I'm going to decide to deviate away from my base class implementation
}
public override bool ShouldProcess() => true;
public override void Process() => Console.WriteLine("Processed");
}
// Consumer code
public void SomeFunction()
{
IWorker worker = GetWorkerSomehow();
// I want this to always use the abstract class implementation
// regardless of what concrete class I get back here.
worker.DoWork();
}
I want any call to an object with interface IWorker
to ALWAYS use the logic in the abstract class implementation and instead delegate actual processing via the abstract functions subclasses have to implement. It seems there is no way to cause compilation errors if a subclass decides to override the DoWork()
class. They are always able to do so via the new
operator on the function signature.
Real world example since I know people may say I'm trying to do something unintended and should look for a different implementation strategy (and maybe I am). I'm looking to implement a Chain of Responsibility design pattern. This pattern has an abstract class usage that concrete classes implement to make the pattern work. These concrete classes should either decide to handle a request, or if it can't, call the next request handler. But there is no enforcement of this idea. Someone could make a new concrete class and just forget to forward the call to the next handler breaking the chain. I want forced guarantee that the pattern is always followed in all concrete classes. Code:
public interface IHandler
{
void HandleRequest();
IHandler SetNextHandler(IHandler nextHandler);
}
public abstract class AbstractHandler : IHandler
{
private readonly IHandler _nextHandler;
public IHandler SetNextHandler(IHandler nextHandler);
{
_nextHandler = nextHandler;
return nextHandler;
}
// Forced calling of the _nextHandler if applicable
public void HandleRequest()
{
if (CanHandleRequest()) ProcessRequest();
else if (_nextHandler is not null) _nextHandler.HandleRequest();
}
// Forced function implementations on the concrete classes
// that aids in the HandleRequest() process
public abstract bool CanHandleRequest();
public abstract void ProcessRequest();
}
public class GoodHandler : AbstractHandler
{
public override bool CanHandleRequest() => true;
public override void ProcessRequest() { // Do stuff }
}
public class BadHandler : AbstractHandler
{
// Don't want this override via the 'new' keyword to be possible....should only use
// the abstract class implementation
public new void HandleRequest()
{
if (CanHandleRequest()) ProcessRequest();
// Look, I don't decide to forward the request to nextHandler, chain is broken
// I must rely on future class creators to fully understand this concept.
}
public override bool CanHandleRequest() => false;
public override void ProcessRequest() { // Do stuff }
}
// Consumer code
public void SomeFunction()
{
IHandler good = new GoodHandler();
IHandler bad = new BadHandler();
bad.SetNextHandler(good);
bad.HandleRequest(); // We will never call to the "good" handler
}
Get rid of IHandler.
public abstract class AbstractHandler
{
private AbstractHandler? _nextHandler;
public AbstractHandler SetNextHandler(AbstracHandler nextHandler);
{
_nextHandler = nextHandler;
return nextHandler;
}
// Forced calling of the _nextHandler if applicable
public void HandleRequest()
{
if (CanHandleRequest()) ProcessRequest();
else if (_nextHandler is not null) _nextHandler.HandleRequest();
}
// Forced function implementations on the concrete classes
// that aids in the HandleRequest() process
public abstract bool CanHandleRequest();
public abstract void ProcessRequest();
}
Now all handlers must conform and use the base class method.
The use of the interface allows for overriding. For instance in your example I could have:
public class BadHandler : IHandler
{
// ...
public void HandleRequest()
{
throw new Exception("Hell no, I ain't going to play nice!");
}
}
And your AddHandler on a class that extended AbstractHandler would have been none the wiser. The calls to HandleRequest would have handed off to the custom implementation and potentially broken the chain.
With the base abstract class only, even if I overload the HandleRequest method:
public class BadHandler : AbstractHandler
{
// ...
public new void HandleRequest()
{
throw new Exception("Hell no, I ain't going to play nice!");
}
}
... it doesn't matter, it won't get called as part of the chain of HandleRequest. The only way it can be called is with a concrete BadHandler reference.