Sergio
Sergio

Reputation: 1423

Implement abstract class when target already implements one

Some of my classes has the same code for error storage. Being two error properties (number and message) and several methods for setting the error state. This code repeats on every class, so today I decided to refactor the common code and extract it so it can be reused.

First I tried to create it as an interface, but I could make it work. On my VB.net programmer mind I thought that it was simple matter of moving the code and referencing it. But interfaces could not handle the code. So I created an abstract class and started to inherit on my other classes.

public class DBEngine : ErrorContainer, IDisposable {
  protected DBEngine(string connectionString, string providerName)
  public DBEngine(string connectionString = "")
}

But then I came to a problem when on of my classes already has an inheritance.

public abstract class TypedTable<TRow> : TypedTableBase<TRow> where TRow : TypedRow {
  protected TypedTable(SerializationInfo info, StreamingContext context) : base(info, context)
  protected TypedTable()
}

If I inherit the interface, it wants me to re implement all the functions, that seems to me counter-productive.

How I can structure my error storage code, so it can be reused on my classes, even when they already have an inheritance, but avoiding rewriting any code?

NOTE: Please don't confuse what I'm talking there with some Error Handling. For Error handling I use NLog and have my own interface layer and static implementation. What I'm talking there it's to reuse some properties that several classes have in common, that happens to store the error code and message. Nothing more.

   ClassA
     L public Property1
     L public Property2
     L private MethodToSet1And2

   ClassB : Something (problem there)
     L public Property1
     L public Property2
     L private MethodToSet1And2

Same two properties and and method.

The answers are out of scope as the proposed solutions isolate the properties from the caller of A and B. The real use of them is precisely pass to the caller some info. Not do some external reusable action.

Upvotes: 1

Views: 111

Answers (5)

Tim Pohlmann
Tim Pohlmann

Reputation: 4440

Create the ErrorHandler inside your class:

public inteface IErrorHandler
{
    void HandleError(string errorMessage);
}

public class ErrorHandler : IErrorHandler
{
    public void HandleError(string errorMessage)
    {
        Console.WriteLine(errorMessage);
    }
}

public class YourClass: YourSuperClass
{
    public IErrorHandler ErrorHandler { get; } = new ErrorHandler();

    public void DoSomething()
    {
        this.ErrorHandler.HandleError("Error, I did something!");
    }
}

Upvotes: 0

Tim Pohlmann
Tim Pohlmann

Reputation: 4440

If your error handling is static you can do it via an extension method:

public interface IErrorHandling { }

public static void HandleError(this IErrorHandling errorHandler, string errorMessage)
{
    Console.WriteLine(errorMessage);
}

public class YourClass : YourSuperClass, IErrorHandling
{
    public void DoSomething()
    {
        this.HandleError("Error, I did something!");
    }
}

You can omit the this if you want.

Upvotes: 0

Harald Coppoolse
Harald Coppoolse

Reputation: 30512

Are you sure that in your definition of DBEngine every DBEngine IS the same ErrorManager, or can you think of the possibility that in future you want to create a subclass of DBEngine that uses a different manager, or even a class that is a DBEngine in every way except for the ErrorManager?

People tend to inherit instead of compose because it is less typing. They forget that future changes will be more problematic.

The advantage of composition above inheritance, is that you can decide to let your class use a different error handler while all your million users of your class (hey, you make perfectly usable classes!) don't have to see anything from this change.

Another advantage of composition is that you control what functions of the error handler can be used by users of your class. Are you sure that users of your class can use any function of an ErrorManager without interfering with your DBEngine class?

Indeed the disadvantage of composition is that you'll have to do some typing. But usually this is only a call to the corresponding function of the errormanager.

interface IErrorHandler
{
    void Warning();
    void Error();
}

class VeryGoodErrorHandler : IErrorHandler
{
    public void Warning()
    {
        // a lot of difficult code
    }
    public void Error()
    {
        // even more difficult code
    }
}

class DBEngine : IErrorHandler
{
    private IErorHandler myErrorHandler = new VeryGoodErrorHandler()

    public void Warning()
    {
        this.myErrorHandler.Warning();
    }
    public void Error()
    {
        this.myErrorHandler.Error();
    }

Notice that you don't have to recode all the difficult code, just call the handler.

Also notice that the changes are minimal if you want to change your engine class such that is uses the class NotSoGoodErrorHandler : IErrorHanlder?

Notice that you without big changes you can let the creator of your DBEngine which error handler to use:

class DBEngine : IErrorHandler
{
    private IErorHandler myErrorHandler = null;

    public DBEngine()
    {   // use default error handler:
        myErrorHandler = new VeryGoodErrorHandler();
    }
    public DBEngine(IErrorHandler userProvidedErrorHandler)
    {   // use default error handler:
        myErrorHandler = userProvidedErrorHandler
    }

            public void Warning()
    {
        this.myErrorHandler.Warning();
    }
    public void Error()
    {
        this.myErrorHandler.Error();
    }
}

See the changes are minimal. If your class isn't really a special kind of another class, don't use inheritance. Composition may take some extra typing now, but it greatly enhances the possibilities for change and reusability.

Upvotes: 0

Yuval Itzchakov
Yuval Itzchakov

Reputation: 149646

There's a common practice called "Composition over inheritance" (or "Composite Reuse Principle"). This means that instead of an "is-a" relationship, you move to a "has-a" relationship.

What this means in this case is that you'll have a dedicated class which handles common errors, like this:

public inteface IErrorHandler
{
    void HandleError(string errorMessage);
}

public class ErrorHandler : IErrorHandler
{
    public void HandleError(string errorMessage)
    {
        Console.WriteLine(errorMessage);
    }
}

Now each of your classes takes this as a parameter via it's constructor and can use it internally for error handling, meaning you delegate the work to it whereever needed:

public class Foo
{
    private readonly IErrorHandler errorHandler;
    public Foo(IErrorHandler errorHandler)
    {
        this.errorHandler = errorHandler;
    }

    public void DoStuff()
    {
        // do stuff
        errorHandler.HandleError("Everything went wrong!");
    }
}

Upvotes: 2

Jakub Lortz
Jakub Lortz

Reputation: 14904

Use composition instead of inheritance. Move your error management code to a separate class and keep an instance of that class in the classes that need it.

Upvotes: 1

Related Questions