当目标已经实现抽象类时实现抽象类

本文关键字:实现 抽象类 目标 | 更新日期: 2023-09-27 18:16:38

我的一些类有相同的错误存储代码。是两个错误属性(数字和消息)和几个设置错误状态的方法。这段代码在每个类上重复,所以今天我决定重构公共代码并提取它,以便它可以被重用。

首先我试着把它创建为一个接口,但我可以让它工作。在我的VB.net程序员头脑中,我认为这是移动代码和引用它的简单问题。但是接口不能处理代码。所以我创建了一个抽象类,并开始继承我的其他类。

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

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

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

注意:请不要混淆我在这里所说的错误处理。对于错误处理,我使用NLog,并有自己的接口层和静态实现。我在这里说的是重用几个类共有的一些属性,这些属性用来存储错误代码和消息。仅此而已。

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

相同的两个属性和and方法。

答案超出了范围,因为建议的解决方案将属性与A和b的调用者隔离开来。它们的真正用途是准确地向调用者传递一些信息。

当目标已经实现抽象类时实现抽象类

有一种常见的实践称为"组合优于继承"(或"组合重用原则")。这意味着,您将不再使用"是- - -"关系,而是使用"已- - -"关系。

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

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

现在,你的每个类都通过它的构造函数将this作为参数,并可以在内部使用它进行错误处理,这意味着你可以在需要的地方将委托给:

public class Foo
{
    private readonly IErrorHandler errorHandler;
    public Foo(IErrorHandler errorHandler)
    {
        this.errorHandler = errorHandler;
    }
    public void DoStuff()
    {
        // do stuff
        errorHandler.HandleError("Everything went wrong!");
    }
}

用组合代替继承。将错误管理代码移到一个单独的类中,并在需要它的类中保留该类的实例。

你确定在你的DBEngine定义中,每个DBEngine都是相同的ErrorManager吗?或者你能想到将来你想要创建一个使用不同管理器的DBEngine的子类,甚至是一个除了ErrorManager以外在所有方面都是DBEngine的类吗?

人们倾向于继承而不是撰写,因为它需要更少的输入。他们忘记了未来的变化会更成问题。

复合优于继承的优点是,您可以决定让您的类使用不同的错误处理程序,而您的类的所有百万用户(嘿,您创建了非常可用的类!)不必从这个更改中看到任何东西。

组合的另一个优点是,您可以控制错误处理程序的哪些函数可以被您的类的用户使用。您确定类的用户可以在不干扰DBEngine类的情况下使用ErrorManager的任何函数吗?

实际上,组合的缺点是你必须做一些打字工作。但通常这只是对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();
    }

请注意,您不必重新编写所有困难的代码,只需调用处理程序。

还请注意,如果要更改引擎类,则更改是最小的,例如使用类notsogooderorhandler: ierrorhandler ?

请注意,您可以让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();
    }
}

看到的变化是最小的。如果您的类实际上不是另一个类的特殊类型,则不要使用继承。组合现在可能需要一些额外的输入,但它大大增强了更改和可重用性的可能性。

如果你的错误处理是静态的,你可以通过一个扩展方法:

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

如果需要,可以省略this

在你的类中创建ErrorHandler:

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