Как мне обработать большое количество свойств, которые инициализируют класс?

#c

#c

Вопрос:

У меня есть класс, которому требуется большое количество свойств для инициализации:

 Hand hand = new Hand();
hand->skinColor(Color(0.5, 0.5, 0.2));
hand->indexFingerLength(0.5);
hand->middleFingerLength(0.6);
hand->ringFingerLength(0.55);
...
hand->init(); // Builds the hand model
  

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

Есть ли лучший способ реорганизовать это, кроме перемещения всех свойств в список аргументов для init ()?

Комментарии:

1. Класс со многими свойствами, на мой взгляд, плохо пахнет кодом.

2. @Federico Почему это так? В философии OO объекты нашего мышления должны быть сопоставлены классам. Если у меня есть сложная сущность, это приведет к созданию сложного класса.

3. @Hyperboreus Я думаю, что проблема заключается в согласованном состоянии класса, представленном множеством атрибутов и множеством несвязанных свойств. С первым все в порядке, а со вторым — неприятный запах.

4. @Отметить. Интересно. Не могли бы вы определить (или связать определение) «атрибут» и «свойство»? Свойство будет реализовано как переменная-член; как насчет атрибута?

5. @Hyperboreus в приведенном примере хорошим классом был бы Hand с пятью атрибутами » Finger класса», по одному для каждого пальца, каждый из которых имеет свое length свойство. Я говорю о хорошей абстракции, конечно, я действительно не вижу здесь необходимости в Finger классе, особенно если пальцы имеют только длину и ничего больше. Но все же я считаю хорошей практикой OO абстрагировать их в другом классе, поэтому, возможно, мы можем передать 5 Finger s в Hand конструкторе

Ответ №1:

 struct structHandParms
{
  param1;
  param2;
  param3;
  ...

};

structHandParms stParams;

Hand hand = new Hand(stParams)
  

Комментарии:

1. Можно улучшить, сделав параметры необязательными.

Ответ №2:

Вы могли бы добавить структуру «Hand_Init», которая была передана конструктору:

 Hand_Init hand_init;
hand_init.skinColor = Color(0.5, 0.5, 0.2);
hand_init.indexFingerLength = 0.5;
hand_init.middleFingerLength = 0.6;
hand_init.ringFingerLength = 0.55;
Hand hand = new Hand(hand_init);
  

Таким образом, как только вызов конструктора завершается, рука инициализируется, и у вас нет кучи анонимных аргументов, которые могли бы вызвать путаницу.

Ответ №3:

Если вы собираетесь создать более одного Hand , то, возможно, используйте что-то вроде шаблона Builder.

 // set properties
HandBuilder hb;
hb.skinColor(Color(0.5, 0.5, 0.2));
hb.indexFingerLength(0.5);
hb.middleFingerLength(0.6);
hb.ringFingerLength(0.55);
// create object
Hand hand = hb.buildHand();
  

Ответ №4:

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

Ответ №5:

«вызов init(), который использует их для построения класса» действительно создает класс или инициализирует экземпляр этого класса?

Почему бы вам не выполнить всю инициализацию в ctor и не передать требуемые значения в качестве параметров?

Комментарии:

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

2. Что ж, тогда вам нужно заполнить структуру вместо заполнения параметров ctor. Это просто отодвигает проблему и ничего не меняет.

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

4. Красота (и, следовательно, читаемость и понятность) в глазах смотрящего. Я нахожу стиль кодирования моих коллег просто уродливым и нечитаемым. То же самое, что он говорит о моем. Здесь должны вступить в игру рекомендации по корпоративному стилю кода.

Ответ №6:

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

Ответ №7:

Вы могли бы решить проблему модификации post init() путем создания исключения. Это помешало бы пользователю выполнить вызов одной из функций настройки и ожидать, что она что-то сделает.

В качестве альтернативы вы могли бы создать класс / структуру HandParameter. Этот объект будет отвечать за сбор всех данных, которые вам нужны об объекте Hand. Далее, вы могли бы заставить функцию init() вашего объекта Hand принимать HandParameter в качестве своего единственного аргумента.

 struct HandParam
{
    // Whatever settings you need
};

class Hand
{
    bool initialized;
    // other private data
    public:
        Hand();
        Hand( const HandParamamp; params ); // Calls the init() funciton with params
        void init( const HandParamamp; params ); // sets up hand and sets initialized to true
    // Whatever else you need in your class
};
  

Удачи!