Должны ли конструкторы принимать параметры или я должен создавать сеттеры?
У меня есть два варианта. Либо создайте класс, который принимает много аргументов в своих конструкторах, либо создаст множество методов setter и метод init. Я не уверен, какой из них предпочтительнее, если некоторые аргументы будут приняты в конструкторах, а другие могут быть установлены вручную через setter? Или я слишком задумываюсь об этом?
Это тоже важный вопрос: Конфликты между именами участников и именами аргументов конструктора.
Ответы
Ответ 1
Если после создания объекта вы должны вызвать set
или init
, чтобы на самом деле его использовать... ну, это просто ужасный дизайн.
Если объект можно использовать без каких-либо элементов, инициализированных так, как вы хотите, они могут быть позже установлены.
Золотое правило здесь - , если вы создаете объект, вы сможете использовать его без какой-либо другой инициализации.
Расширение ответа:
Скажите, что у вас есть форма с 10 сторонами, 10 углами, цвет и имя, которые могут быть связаны с другой формой. Конструктор должен выглядеть так:
MyShape(Point c1, Point c2,...., Point c10, Color c, Name n)
Как вы можете видеть, я опустил связанную фигуру, потому что ее можно настроить на NULL
, если текущий объект не подключен. Однако при отсутствии каких-либо других параметров объект недействителен, поэтому они должны быть установлены в конструкторе.
Возможная перегрузка (альтернативно аргумент по умолчанию) может быть:
MyShape(Point c1, Point c2,...., Point c10, Color c, Name n,
MyShape* connectedShape /*=NULL*/)
Ответ 2
Вы должны предоставить аргументы конструктора для всех членов, которые необходимы для сохранения инварианта класса . Другими словами, объект должен находиться в правильном и согласованном состоянии с момента его создания до его уничтожения. Все остальное требует неприятностей.
Таким образом, иногда принимаются уступки, например. в случаях иерархии, где необходимо вызвать виртуальные методы для обеспечения инициализации типа. Зачастую этого можно избежать, используя шаблонные классы/методы (т.е. статический полиморфизм)
Если есть члены класса, которые не влияют на инвариант класса, их можно установить позже через сеттеры или другие методы.
Ответ 3
шаблон построителя поможет здесь также попытаться объединить параметры, чтобы они имели смысл при настройке компоновщика
Ответ 4
Это скорее зависит от того, что вы делаете. Обычно лучше всего устанавливать вещи в конструкторе, поскольку они помогают определить, как объект используется позже в его жизненном цикле. Также могут быть последствия изменения значения после создания объекта (например, коэффициента вычисления или имени файла), что может означать, что вы должны предоставить функциональность reset объекту - очень беспорядочно.
Иногда возникают аргументы для предоставления функции инициализации, которая вызывается после конструктора (при вызове чистого виртуального файла было бы трудно инициализировать непосредственно из конструктора), но тогда вам нужно сохранить запись состояния объекта, которая добавляет больше сложности.
Если объект представляет собой прямой контейнер данных без состояния, тогда аксессоры и мутаторы могут быть в порядке, но они добавляют много служебных накладных расходов и редко используются в любом случае.
Я бы склонен придерживаться настроек ваших значений в конструкторе, а затем добавлять аксессоров как и когда разрешить вам читать только доступ к аргументам, которые могут вам понадобиться.
Ответ 5
Это зависит от вашей архитектуры и инструментов:
Если вы планируете разработать/прототип большой OO-иерархии, я бы не захотел передавать много информации через конструкторы, если у вас нет хорошего IDE/редактора. В этом случае вы можете получить много работы на каждом этапе рефакторинга, что может привести к ошибкам, не улавливаемым компилятором.
Если вы планируете использовать хорошо интегрированный набор объектов (например, благодаря сильному использованию шаблонов проектирования), которые не охватывают одну большую иерархию, а имеют сильное итерационное действие, передача большего количества данных через конструкторы хорошо, так как смена одного конструктора объектов не разрушает все дочерние конструкторы.
Ответ 6
Если этот параметр необходим и ему нельзя присвоить значение по умолчанию, сделайте его обязательным в конструкторе. Таким образом, вы знаете, что это действительно будет установлено.
Если параметр не требуется и ему может быть присвоено значение по умолчанию, сделайте для него сеттер. Это делает конструктор намного проще.
например. Если у вас есть класс, который отправляет электронное письмо, в конструкторе может потребоваться поле "Кому", но все остальное может быть установлено в методе setter.
Ответ 7
Мой опыт указывает мне на наличие аргументов в конструкторе, а не на геттеры и сеттеры. Если у вас есть много параметров, это предполагает, что необязательные могут быть дефолтны, а обязательные/обязательные - параметры конструктора.
Ответ 8
Как правило, наличие большого количества параметров конструктора является признаком класса, который делает слишком много, поэтому сначала попробуйте разбить его на более мелкие классы.
Затем попробуйте сгруппировать некоторые параметры в более мелкие классы или структуры, имеющие свой собственный, более простой конструктор.
Если у вас есть разумные значения по умолчанию, вы можете использовать конструктор, который предоставляет параметры только для значений, которые абсолютно ДОЛЖНЫ быть заданы при построении нового объекта, а затем добавлять сеттеры или использовать статические функции, которые копируют объект "стартер", изменяя его часть в процессе. Таким образом, у вас всегда есть согласованные объекты (инварианты OK) и более короткие вызовы конструктора или функции.
Ответ 9
Я согласен с храповым уродцем предложением шаблона строителя, за исключением того, что есть компромисс в что типичный шаблон построителя не предлагает никаких проверок времени компиляции, чтобы обеспечить включение всех аргументов, и вы можете получить неполностью/неправильно построенный объект.
Для меня была достаточно проблема, что я делаю версию проверки времени компиляции, которая могла бы выполнить эту работу за вас, если вы сможете простить дополнительную технику. (Конечно, есть оптимизаторы)
#include <boost/shared_ptr.hpp>
class Thing
{
public:
Thing( int arg0, int arg1 )
{
std::cout << "Building Thing with \n";
std::cout << " arg0: " << arg0 << "\n";
std::cout << " arg1: " << arg1 << "\n";
}
template <typename CompleteArgsT>
static
Thing BuildThing( CompleteArgsT completeArgs )
{
return Thing( completeArgs.getArg0(),
completeArgs.getArg1() );
}
public:
class TheArgs
{
public:
int arg0;
int arg1;
};
class EmptyArgs
{
public:
EmptyArgs() : theArgs( new TheArgs ) {};
boost::shared_ptr<TheArgs> theArgs;
};
template <typename PartialArgsClassT>
class ArgsData : public PartialArgsClassT
{
public:
typedef ArgsData<PartialArgsClassT> OwnType;
ArgsData() {}
ArgsData( const PartialArgsClassT & parent ) : PartialArgsClassT( parent ) {}
class HasArg0 : public OwnType
{
public:
HasArg0( const OwnType & parent ) : OwnType( parent ) {}
int getArg0() { return EmptyArgs::theArgs->arg0; }
};
class HasArg1 : public OwnType
{
public:
HasArg1( const OwnType & parent ) : OwnType( parent ) {}
int getArg1() { return EmptyArgs::theArgs->arg1; }
};
ArgsData<HasArg0> arg0( int arg0 )
{
ArgsData<HasArg0> data( *this );
data.theArgs->arg0 = arg0;
return data;
}
ArgsData<HasArg1> arg1( int arg1 )
{
ArgsData<HasArg1> data( *this );
data.theArgs->arg1 = arg1;
return data;
}
};
typedef ArgsData<EmptyArgs> Args;
};
int main()
{
Thing thing = Thing::BuildThing( Thing::Args().arg0( 2 ).arg1( 5 ) );
return 0;
}