Как сделать оператор switch более объектно-ориентированным?
Я пытаюсь реорганизовать оператор switch, который выглядит следующим образом:
private void Validate(DataTypeEnum dataType, string value, ...)
{
switch(dataType)
{
case DataTypeEnum.Number:
var typedValue = int.Parse(value);
//Validation of typedValue
break;
case DataTypeEnum.Decimal:
var typedValue = Decimal.Parse(value);
//Validation of typedValue
break;
case DataTypeEnum.DateTime:
var typedValue = DateTime.Parse(value);
//Validation of typedValue
break;
}
}
Я хотел бы избавиться от оператора switch и как-то заменить его более объектно-ориентированной конструкцией. Какие-либо предложения?
Моим идеалом было бы что-то вроде этого:
private void Validate(DataTypeEnum dataType, string value, ...)
{
Validate(value);
}
private void (Decimal value)
{
//Validate
}
private void (DateTime value)
{
//Validate
}
Есть ли какой-нибудь элегантный способ исправить это?
7 ответов
Прежде всего, я бы начал с реализации простого интерфейса для проверки:
public interface IValidator
{
bool Validate(object value);
}
Тогда валидатор номера может выглядеть так:
public class NumberValidator : IValidator
{
public bool Validate(object value)
{
return (int) value > 0;
}
}
И последний шаг - замена вашего переключателя на словарь:
var _validators = new Dictionary<DataTypeEnum, IValidator> // injected via DI
{
{ DataTypeEnum.Number, new NumberValidator() },
{ DataTypeEnum.DateTime, new DateTimeValidator() },
{ DataTypeEnum.String, new StringValidator() }
};
......
private bool Validate(DataTypeEnum dataType, object value, ...)
{
if (_validators.ContainsKey(dataType))
{
return _validators[dataType].Validate(value);
}
return false;
}
Используйте полиморфизм.
Пример:
public class DataType
{
public virtual void Validate()
{
Console.WriteLine("Performing base class validation tasks");
}
}
class Foo : DataType
{
public override void Validate()
{
// Code to validate a foo...
Console.WriteLine("Validating a foo");
}
}
class Bar : DataType
{
public override void Validate()
{
// Code to validate a bar...
Console.WriteLine("Validating a bar");
}
}
List<DataType> datatypes = new List<DataType>();
datatypes.Add(new Foo());
datatypes.Add(new Barr());
foreach (DataType s in datatypes)
{
s.Validate();
}
Чтобы добавить к этому, хотя некоторые могут считать это не ООП вообще, вы можете использовать dynamic
перегрузки для обработки этого:
public bool ValidateAny(dynamic val)
{
return Validate(val);
}
private bool Validate(string val) { ... }
private bool Validate(int val) { ... }
private bool Validate(decimal val) { ... }
private bool Validate(object val) { ... } // This is the fallback
По сути, это работает так же, как и обычное разрешение перегрузки, но выполняется во время выполнения, в зависимости от типа времени выполнения. val
в ValidateAny
,
Например:
ValidateAny(3); // Uses the int overload
ValidateAny((object)3); // Uses the int overload as well - dynamic handles the unboxing
ValidateAny(3M); // Uses the decimal overload
ValidateAny(3.ToString()); // Uses the string overload
ValidateAny(3f); // Uses the object overload, since there's no better match
Это невероятно мощно, поскольку вам нужно всего лишь иметь разные проверки для разных типов. Если у вас также есть другие соображения, это должно на каком-то уровне вернуться к if
/switch
, Даже тогда это может сэкономить вам много времени.
Создайте словарь с DataTypeEnum в качестве ключа и действием в качестве значения. Затем получите значение из словаря по ключу и вызовите действие.
private readonly Dictionary<DataTypeEnum , Action > Validator = new Dictionary<DataTypeEnum , Action >
{
{ DataTypeEnum.Number, () => Validate(Convert.ToDouble((string)value)) },
{ DataTypeEnum.Decimal, () => Validate(Convert.ToDecimal((string)value)) },
{ DataTypeEnum.DateTime, () => Validate(Convert.ToDateTime((string)value)) },
// ...
}
private void Validate(DataTypeEnum dataType, object value, ...)
{
Validator[dataType](); // Gets the Action and Invokes it
}
private void Validate (Decimal value)
{
//Validate
}
private void Validate (DateTime value)
{
//Validate
}
//...
Здесь действие - делегат Void, который не принимает параметров. Он преобразует значение в подходящий формат и вызывает метод проверки.
public interface IValidator
{
void Validate(object value);
}
public class NumberValidator : IValidator
{
public void Validate(object value)
{
//implementation
}
}
public class DecimalValidator : IValidator
{
public void Validate(object value)
{
//implementation
}
}
public class DatetimeValidator : IValidator
{
public void Validate(object value)
{
//implementation
}
}
private void Validate(IValidator validator, object value)
{
validator.Validate(value);
}
private void Validate<T>(T value) where T : IComparable
{
if(value is Number)
{
}
if(value is Decimal)
{
}
if(value is DateTime)
{
}
}
Как насчет чего-то вроде этого:
private void Validate(object value, ...)
{
if(Reference.Equals(null, value)
return;
if(value is Number)
{
}
else if(value is Decimal)
{
}
else if (value is DateTime)
{
}
else return;
}
Если переданное значение равно Number, Decimal или DateTime, он запустит соответствующий метод, в противном случае он просто вернется.