У меня есть эта функция API:
public ResultEnum DoSomeAction(string a, string b, DateTime c, OtherEnum d,
string e, string f, out Guid code)
Мне это не нравится. Потому что порядок параметров становится излишне значительным. Сложнее добавлять новые поля. Труднее понять, что происходит. Сложнее реорганизовать метод на более мелкие части, потому что он создает дополнительные накладные расходы для передачи всех параметров в подфункциях. Код сложнее читать.
Я придумал самую очевидную идею: у меня есть объект, инкапсулирующий данные и передающий его, вместо того, чтобы передавать каждый параметр по одному. Вот что я придумал:
public class DoSomeActionParameters
{
public string A;
public string B;
public DateTime C;
public OtherEnum D;
public string E;
public string F;
}
Это уменьшило мою декларацию API до:
public ResultEnum DoSomeAction(DoSomeActionParameters parameters, out Guid code)
Ницца. Выглядит очень невинно, но мы фактически внесли огромные изменения: мы ввели изменчивость. Потому что то, что мы ранее делали, состояло в том, чтобы передать анонимный неизменяемый объект: параметры функции в стеке. Теперь мы создали новый класс, который очень изменчив. Мы создали возможность управлять состоянием вызывающего. Это отстой. Теперь я хочу, чтобы мой объект был неизменным, что мне делать?
public class DoSomeActionParameters
{
public string A { get; private set; }
public string B { get; private set; }
public DateTime C { get; private set; }
public OtherEnum D { get; private set; }
public string E { get; private set; }
public string F { get; private set; }
public DoSomeActionParameters(string a, string b, DateTime c, OtherEnum d,
string e, string f)
{
this.A = a;
this.B = b;
// ... tears erased the text here
}
}
Как вы можете видеть, я действительно заново создал свою оригинальную проблему: слишком много параметров. Очевидно, что это не путь. Что я собираюсь делать? Последним вариантом достижения такой неизменности является использование структуры "readonly" следующим образом:
public struct DoSomeActionParameters
{
public readonly string A;
public readonly string B;
public readonly DateTime C;
public readonly OtherEnum D;
public readonly string E;
public readonly string F;
}
Это позволяет нам избегать конструкторов со слишком большим количеством параметров и достигать неизменности. Фактически он исправляет все проблемы (порядок параметров и т.д.). Тем не менее:
- Все (включая FXCop и Jon Skeet) согласны с тем, что публикация открытых полей плохая.
- Эрик Липперт и др. полагаясь на поля readonly для неизменности - это ложь.
Что, когда я запутался и решил написать этот вопрос: Какой самый простой способ в С# избежать проблемы с "слишком многими параметрами" без введения изменчивости? Можно ли использовать для этой цели структуру readonly и все же не иметь плохой дизайн API?
ПОЯСНЕНИЯ:
- Пожалуйста, примите во внимание, что нет нарушения единого принципа ответственности. В моем исходном случае функция просто записывает заданные параметры в одну запись БД.
- Я не ищу конкретного решения данной функции. Я ищу обобщенный подход к таким проблемам. Меня особенно интересует решение проблемы "слишком много параметров" без введения изменчивости или ужасного дизайна.
UPDATE
Представленные здесь ответы имеют разные преимущества/недостатки. Поэтому я хотел бы преобразовать это в вики сообщества. Я думаю, что каждый ответ с образцом кода и "Плюсами/минусами" станет хорошим руководством для подобных проблем в будущем. Теперь я пытаюсь выяснить, как это сделать.