2010-04-23 5 views
16

Если у меня есть конструктор с 2 обязательными параметрами и 4 необязательными параметрами, как я могу избежать написания 16 конструкторов или даже 10 конструкторов, которые мне пришлось бы писать, если бы я использовал параметры по умолчанию (что мне не нравится, потому что это плохая самодокументация)? Существуют ли какие-либо идиомы или методы, использующие шаблоны, которые я могу использовать, чтобы сделать его менее утомительным? (И проще в обслуживании?)избегая скуки необязательных параметров

+1

Вы уверены, что ваш класс не пытается сделать слишком много? –

+0

@Johannes: Он хочет, чтобы пропустить say d, не заставляя вызывающего абонента узнать его значение по умолчанию. – Danvil

+2

@John: Я вижу, что вы получаете, но параметры могут быть свойствами. Например, у меня есть класс GUI Button, который имеет множество свойств, таких как изображение, текст, размер, цвет и т. Д., Но я не чувствую, что это «слишком много». IMHO, Кнопки служат очень конкретной цели и легко вписываются в один класс, где пользователь сразу узнает, что делает класс. – Kyle

ответ

34

Возможно, вас заинтересует Named Parameter Idiom.

Чтобы подвести итог, создайте класс, который содержит значения, которые вы хотите передать своим конструкторам. Добавьте метод для установки каждого из этих значений, и каждый из них должен сделать return *this; в конце. У вас есть конструктор в своем классе, который ссылается на этот новый класс. Это можно использовать так:

class Person; 

class PersonOptions 
{ 
    friend class Person; 
    string name_; 
    int age_; 
    char gender_; 

public: 
    PersonOptions() : 
    age_(0), 
    gender_('U') 
    {} 

    PersonOptions& name(const string& n) { name_ = n; return *this; } 
    PersonOptions& age(int a) { age_ = a; return *this; } 
    PersonOptions& gender(char g) { gender_ = g; return *this; } 
}; 

class Person 
{ 
    string name_; 
    int age_; 
    char gender_; 

public: 
    Person(const PersonOptions& opts) : 
    name_(opts.name_), 
    age_(opts.age_), 
    gender_(opts.gender_) 
    {} 
}; 
Person p = PersonOptions().name("George").age(57).gender('M'); 
+0

Как бы вы сделали эту работу в конструкторе? Используя 'return this' в методе конструктора? –

+1

Очевидно, вы ничего не можете вернуть из конструктора. Но вы можете использовать копию, например 'Foo f = Foo(). Option1(). Option2();' например. В примере в ссылке используется отдельный класс для фактического создания. –

+2

@Robert важно заметить, что правая сторона начинается с 'PersonOptions', а не' Person'. Это создает предположительно легкий объект, а затем имеет ряд вызовов методов для установки свойств на значения, отличные от значений по умолчанию. Тогда есть ctor с именем 'Person (const PersonOptions &)' и ta da. Создание экземпляра объекта (например, 'PersonOptions()') оставляет «* this» в текущем контексте, так что вы можете сразу его отключить .method(). Однако каждый метод все равно должен «возвращать» это, чтобы иметь возможность цепочки после него. –

9

Что делать, если вы создали объект параметра, содержащий все поля? Тогда вы можете просто передать это и просто установить любые поля, в которых вы нуждаетесь. Там, вероятно, название для этой модели, не уверен, что это такое, хотя ...

UPDATE:

код может выглядеть несколько так:

paramObj.x=1; 
paramObj.y=2; 
paramObj.z=3; 
paramObj.magic=true; 
... //set many other "parameters here" 

someObject myObject = new someObject(paramObj); 

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

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

+2

... и этот объект параметра задает значения по умолчанию в своем конструкторе по умолчанию. – Danvil

+1

Почему это лучше, чем просто создавать исходные объекты и свойства настройки? –

+0

@Johannes: Разве это не Идиома Имени, которую Фред Ларсон упоминает в своем ответе? –

4

И теперь для «Boost, есть кое-что для него» ответ:

Boost Parameter Library, кажется, хорошо подходит для вашего использования.

1

Все новые для C++ 17

#include <optional> 

using optional_int = std::optional<int>; 

class foo { 
    int arg0, arg1; // required 
    int arg2, arg3; // optional 
    const int default_2 = -2; 
    const int default_3 = -3; 
public: 
    foo(int arg0, int arg1, optional_int opt0 = {}, optional_int opt1 = {}) 
     : arg0(arg0), arg1(arg1) 
     , arg2(opt0.value_or(default_2)) 
     , arg3(opt1.value_or(default_3)) 
    { } 

}; 

int main() { 
    foo bar(42, 43, {}, 45); // Take default for opt0 (arg2) 
    return 0; 
} 

У меня есть кубический сплайн реализации, что позволяет пользователю необязательно указывать первую производную либо на левом конце, правый конец, или обоих. Если производная не указана, то действующий код вычисляет один, считая, что вторая производная равна нулю (так называемый «естественный сплайн»). Вот фрагмент для левого конца.

// Calculate the second derivative at the left end point 
    if (!left_deriv.has_value()) { 
     ddy[0]=u[0]=0.0; // "Natural spline" 
    } else { 
     const real yP0 = left_deriv.value(); 
     ddy[0] = -0.5; 
     u[0]=(3.0/(x[1]-x[0]))*((y[1]-y[0])/(x[1]-x[0])-yP0); 
    } 
Смежные вопросы