2015-10-08 4 views
1

Некоторые из моих классов имеют одинаковый код для хранения ошибок. Это два свойства ошибки (число и сообщение) и несколько методов для установки состояния ошибки. Этот код повторяется в каждом классе, поэтому сегодня я решил реорганизовать общий код и извлечь его, чтобы его можно было повторно использовать.Реализация абстрактного класса, когда цель уже реализует один

Сначала я попытался создать его как интерфейс, но я мог бы заставить его работать. На моем программистом 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 

Такие же свойства и способ.

Ответы выходят за рамки, поскольку предлагаемые решения изолируют свойства от вызывающего абонента A и B. Реальное использование их точно передает вызывающей информации некоторую информацию. Не выполняйте какое-либо внешнее многоразовое действие.

+0

Обработка ошибок, вероятно, следует извлечь из в класс, посвященный, что в одиночку, а затем друг с другом класс может иметь экземпляр этого. – jmcilhinney

+0

Но процессы, требующие обслуживания (DBEngine), также должны видеть свойства ErrorManager, так как они где-то в нем. Например, если (DbEngine.ErrorNumber! = 0) ProcessTheError (DbEngine.Error()); – Sergio

+0

В соответствии с принципами SOLID ни один из ваших подобных классов не выполняет управление ошибками. Как отмечали другие ребята, вы должны переместить код ошибки mgmt в совершенно новый класс. Вы можете создать * интерфейс *, который определяет, какую ошибку будет выполнять класс mgmt. Затем используйте конструктор или вложение свойств, чтобы предоставить экземпляр экземпляра ошибки mgmt из вызывающего кода. Более того, код обработки ошибок, похоже, не зависит от состояний объекта, поэтому вы также можете выбрать простой статический класс. – niksofteng

ответ

1

Используйте композицию вместо наследования. Переместите код управления ошибками в отдельный класс и сохраните экземпляр этого класса в классах, которые ему нужны.

2

Существует обычная практика, называемая "Composition over inheritance" (или "Композитный принцип повторного использования"). Это означает, что вместо отношения «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!"); 
    } 
} 
+0

Как Foo потребляется? Я должен передать ему новый errorHandler? У моих классов уже есть некоторые сложные конструкторы ... – Sergio

+0

'Foo' потребляется, как и любой другой класс, который у вас есть в вашем коде. Это просто пример. –

+0

OP - см. Мой комментарий к вашему оригинальному вопросу. @Yuval хорошо объяснил, как это сделать. Если вы не можете изменить конструкторы, вы можете иметь свойство типа IErrorHandler и установить его из вызывающего кода. – niksofteng

0

вы уверены, что в вашем определении DBEngine каждый DBEngine IS тот же ErrorManager, или вы можете думать о возможность того, что в будущем вы захотите создать подкласс DBEngine, который использует другой менеджер, или даже класс, который является DBEngine во всех отношениях, за исключением ErrorManager?

Люди склонны наследовать вместо составления, потому что это меньше печатает. Они забывают, что будущие изменения будут более проблематичными.

Преимущество композиции над наследованием заключается в том, что вы можете решить, чтобы ваш класс использовал другой обработчик ошибок, а все ваши миллионы пользователей вашего класса (эй, вы делаете отлично используемые классы!) Не должны ничего видеть от этого изменения.

Другим преимуществом композиции является то, что вы контролируете, какие функции обработчика ошибок могут использоваться пользователями вашего класса. Вы уверены, что пользователи вашего класса могут использовать любую функцию ErrorManager без вмешательства в ваш класс DBEngine?

Действительно, недостатком композиции является то, что вам придется набирать текст. Но обычно это только вызов соответствующей функции 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(); 
    } 

Обратите внимание, что вам не нужно перекодировать весь сложный код, просто вызовите обработчик.

Также обратите внимание, что изменения минимальны, если вы хотите изменить свой класс двигателя, который использует класс 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(); 
    } 
} 

Смотрите изменения минимальны. Если ваш класс не является особым видом другого класса, не используйте наследование. Теперь композиция может потребовать некоторого дополнительного набора текста, но это значительно расширяет возможности для изменения и повторного использования.

0

Если обработка ошибок статична вы можете сделать это с помощью метода расширения:

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, если вы хотите.

0

Создание 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!"); 
    } 
} 
Смежные вопросы