2014-04-03 5 views
3

У меня есть класс Client, который принимает объект с интерфейсом IConfiguration в конструкторе.Виртуальный вызов метода в конструкторе refactoring

Конфигурация должна быть подтверждена при создании объекта Client.

public interface IConfiguration 
{ 
    int ReconnectDelay { get; } 
} 

public class Client 
{ 
    public Client(IConfiguration configuration) 
    { 
     if (configuration.ReconnectDelay < 60000) 
     { 
      throw new ConfigurationErrorsException(); 
     } 
    } 
} 

Для тестирования я нужен клиент с ReconnectDelay собственности установлен на значение, которое меньше, чем действительный.

Вот мое текущее решение:

public class Client 
{ 
    public Client(IConfiguration configuration) 
    { 
     ValidateConfiguration(configuration); 
    } 

    protected virtual void ValidateConfiguration(IConfiguration configuration) 
    { 
     if (configuration.ReconnectDelay < 60000) 
     { 
      throw new ConfigurationErrorsException(); 
     } 
    } 
} 

public class TestClient : Client 
{ 
    public TestClient(IConfiguration configuration) 
     : base(configuration) 
    { 
    } 

    protected override void ValidateConfiguration(IConfiguration configuration) 
    { 
    } 
} 

Он работает, но вызывает вызов виртуального метода в конструкторе, что плохо (я знаю, что сейчас это не принесет никакого вреда, но я хочу, чтобы исправить это в любом случае).

Итак, есть ли элегантное решение для этого?

+0

Можете ли вы сделать класс «TestClient» «запечатанным»? – Dmitry

+0

@Dmitry Как это поможет? Честный вопрос. –

+0

@Dmitry, я могу сделать «TestClient» запечатанным, но проблема здесь в классе 'Client'. – bniwredyc

ответ

2

Вы можете создать интерфейс Validator с 2 реализациями, а затем делегировать на валидатор. Технически это по-прежнему виртуальный вызов, но его к другому объекту, поэтому вам не нужно беспокоиться о подклассах Client, перекрывающих вызов или доступ к частично построенному клиенту.

public interface IValidator 
{ 
    bool Validate (IConfiguration configuration); 
} 

Тогда ваша обычная usecase использует ReconnectionValidator.

public class ReconnectionValidator : IValidator 
{ 

    bool Validate (IConfiguration configuration) 
    { 
     return configuration.ReconnectDelay >= 60000; 
    } 
} 

Ваш тест Оценщик всегда может вернуть истинный

public class NullValidator : IValidator 
{ 

    bool Validate (IConfiguration configuration) 
    { 
     return true; 
    } 
} 

Ваш код клиента будет принимать как IValidator и IConfiguration в своем конструкторе и испытания, если валидатор проверяет конфигурацию.

public Client(IConfiguration configuration, IValidator validator) 
{ 
    if(!validator.Validate(configuration)) 
    { 
     throw new ConfigurationErrorsException(); 
    } 
} 

бонус этого метода является то вы можете изменить валидатор позже или иметь новую реализацию, цепочки несколько валидаторов вместе, чтобы поддержать «или» ING и «Андинг» валидаторы вместе.

+0

Спасибо, мне нравится ваше решение. – bniwredyc

+0

@bniwredyc большой. Я только что установил опечатку в моем коде ReconnectionValidator, который потерпел бы неудачу, поскольку у меня была логическая логика назад – dkatzel