#c# #generics #interface #refactoring
#c# #обобщения #интерфейс #рефакторинг
Вопрос:
В настоящее время я занимаюсь рефакторингом своего кода, чтобы все важные классы реализовали интерфейс (для возможности модульного тестирования). Я наткнулся на класс, который реализует IComparable (без шаблонов); что-то вроде:
public MyClass : IComparable
{
public int CompareTo(object obj)
{
MyClass cObj = obj as MyClass;
if (cObj == null) { throw new ArgumentException(); }
// etc.
}
}
Я хочу вывести его из интерфейса и использовать обобщения, пока я этим занимаюсь; что-то вроде этого:
public IMyClass : IComparable<IMyClass>
{
// Other methods here
}
public MyClass : IMyClass
{
public CompareTo<IMyClass>(IMyClass other)
{
...
}
// Other methods here
}
Но тогда, в идеале, MyClass
должны быть реализованы IComparable<MyClass>
(а затем MyClass
должны быть реализованы IComparable<MySubClass>
подклассы).
Все это для того, чтобы задать несколько вопросов:
Что вы думаете о подходе, который я описал? Есть ли лучший способ выполнить этот рефакторинг? Есть ли смысл в том, чтобы MyClass
также реализовать IComparable<MyClass>
, или это бессмысленно, поскольку мы уже реализуем IComparable<IMyClass>
? Какие-либо рекомендации или «лучшие» практики, о которых я мог бы знать?
Ответ №1:
Действительно ли имеет смысл иметь несколько объектов разных типов, которые все сопоставимы друг с другом? Язык позволяет это, но я могу сосчитать на 0 руках количество раз, когда мне приходилось его использовать.
Я бы рекомендовал использовать IClass
without being IComparable
и просто реализовать производные классы IComparable
.
P.S. Я также против добавления интерфейсов «для модульной тестируемости». Если дизайн вашей программы требует заводского шаблона с привязкой только к интерфейсу, то обязательно кодируйте до этого уровня сложности. Но не злоупотребляйте дизайном только для того, чтобы упростить свои тесты; вместо этого используйте Moles.
Комментарии:
1. Moles выглядит очень интересно. Обязательно изучу это; спасибо!
Ответ №2:
Краткий ответ: это зависит.
В вашем конкретном примере я бы сказал, что почти всегда неправильно создавать ненужный интерфейс ( IMyClass
в данном случае), потому что это просто создает работу для вас. Эмпирическое правило: используйте интерфейсы только тогда, когда их реализует более одного класса. И, как вы указываете, этот конкретный интерфейс даже не достигает цели сделать ваш класс напрямую сопоставимым.
Что касается того, какие классы должны реализовывать IComparable
, общие или иные, это полностью зависит от того, каковы ваши потребности в сравнении. Если сравнение всегда выполняется между ссылками на базовый класс, производному классу не нужно реализовывать интерфейс, поскольку он никогда не будет вызван.
Комментарии:
1. Я не уверен, что согласен с вашим эмпирическим правилом — причина, по которой я подключаю это, заключается в том, что я могу проводить модульное тестирование классов, которые его используют. Это означает, что я могу издеваться над этим классом (хотя, возможно, это означает, что я действительно согласен с вашим эмпирическим правилом, поскольку я буду реализовывать его в модульных тестах).
2. @Smashery: В вашем примере использования это не прояснилось. Это совершенно допустимое использование интерфейсов, которое «следует правилу». 🙂