找到你要的答案

Q:Implement abstract class when target already implements one

Q:当目标已经实现时,实现抽象类

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.

我的一些类有相同的错误存储代码。作为两个错误属性(数字和消息)和设置错误状态的几种方法。此代码重复每一节课,所以今天我决定重构公共代码提取可重复使用。

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 = "")
}

但后来我遇到了一个问题,在我的课上已经有一个继承。

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

如果我继承了接口,它希望我重新实现所有的功能,这对我来说似乎适得其反。

我如何构造我的错误存储代码,以便它可以被重用在我的类,即使他们已经有一个继承,但避免重写任何代码?

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

同一性质和方法。

答案是超出范围的建议的解决方案隔离属性的来电者A和B的真正使用它们正是传递给来电者一些信息。不做一些外部可重用的动作。

answer1: 回答1:

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!");
    }
}

有一个共同的做法称为“组成超过继承”(或“复合重用原则”)。这意味着,而不是一个“is-a”关系,你搬到“has-a”的关系。

这意味着在这种情况下,你将有一个专门的类,处理常见的错误,像这样:

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

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

现在你的每一个类,以此为通过它的构造函数,可以在内部处理错误使用它的参数,这意味着你的工作委托给它的需要:

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

    public void DoStuff()
    {
        // do stuff
        errorHandler.HandleError("Everything went wrong!");
    }
}
answer2: 回答2:

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.

使用合成而不是继承。将错误管理代码移到单独的类中,并在需要的类中保留该类的实例。

answer3: 回答3:

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.

你确定你的定义都是一样的DBEngine DBEngine中所定义的错误,或者你能想到的,将来你想创建DBEngine,使用不同的经理一类的可能性,甚至是一个类,在每一个领域都是DBEngine除中所定义的错误吗?

人们倾向于继承而不是写作,因为它较少打字。他们忘记了未来的变化将更加困难。

以上继承的优点是,你可以决定让你的类使用不同的错误处理程序,而你所有的百万用户的类(嘿,你完全可用的类!)不必看到任何从这个变化。

组成的另一个优点是,您可以控制您的类的用户使用错误处理程序的函数。你肯定你的类的对象可以不干扰你的任何功能的使用DBEngine类中所定义的错误吗?

作文的缺点就是你得打字。但通常这只是调用相应的函数中所定义的错误。

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();
    }

注意,你不需要重新编码所有困难的代码,只是调用处理程序。

还要注意,如果你想改变你的引擎类,使用类notsogooderrorhandler变化最小的:IErrorHanlder?

注意,你没有大的变化,你可以让你的错误处理程序的创造者DBEngine使用:

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();
    }
}

看到变化是最小的。如果你的类不是真正的另一类,不要使用继承。作文可能会采取一些额外的打字现在,但它大大提高了变化和可重用性的可能性。

answer4: 回答4:

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.

如果您的错误处理是静态的,则可以通过扩展方法来执行:

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!");
    }
}

如果你想的话可以省略这个。

answer5: 回答5:

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!");
    }
}

在你的类创建程序:

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!");
    }
}
c#  interface  abstract-class