LINUX.ORG.RU

Передача набора параметров, вопрос проектирвоания

 


0

1

Собственно вопрос может породить холивары, но я в данном случае не стремлюсь докопаться до истины. Допустим есть класс работающий с БД, ему нужно получить настройки, можно передать двумя способами Первый вариант

DbConnector(ip, host, port);
В данном случае имеем преимущества 1. Видим какие параметры использует класс и что ему нужно. Класс обладает относительной «независимостью» от других классов 2. Проще тестировать, т.к. ip, host, port - простые типы и можно проверить его работу без всяких заглушек. Недостатки 1. Если вдруг появится потребность в новом параметре, например имя БД, нужно переписывать интерфейсы. 2. Опять же, необходимо помнить последовательность передачи параметров. 4. Увеличение кол-ва передаваемых параметров, что усложняет восприятие класса. Второй вариант Создаем класс DbSettings и передаем экземляр классу, DbConnector
DbConnector(DbSettings settings_);
Преимущества 1. Код получается более лаконичным 2. Опять же, проще модифицировать Недостатки 1. Сложней тестировать, т.к. надо для тестирования создавать объект DbSettings; 2. Сложней для анализа, т.к. для того что бы понять откуда берутся настройки, придется вообще пробираться в класс DbSettings.

Недавно читал книгу по проектированию на Java так там первый вариант описывался как более предпочтительный, сегодня уточню авторство


по мне первый вариант лучше. Обычно стараюсь не переусердствовать сущностями в виде гномиков классов

r0ck3r ★★★★★
()

если настроек много, то второе, если меньше 4, то первое

вообще, старайся не передавать больше двух-трех параметров

anonymous
()

1. Если вдруг появится потребность в новом параметре, например имя БД, нужно переписывать интерфейсы

язык не допускает перегрузку? нельзя одновременно иметь методы DbConnector(ip, host, port), DbConnector(ip, host, port, dbname), DbConnector(settings) ?

2. Опять же, необходимо помнить последовательность передачи параметров

для этого IDE существует. Так-то для любой функции нужно «помнить последовательность», или - языкозависимо - писать нечто вроде DbConnector(ip => «127.0.0.1», host => «localhost», port =>«5432»)

4. Увеличение кол-ва передаваемых параметров, что усложняет восприятие класса

в разумных пределах - нет. Главное, не доводить до уровня winapi, который «у функции 16 параметров, 10 обязаны быть NULL, три зарезервированы, один - структура с 21 полем»

arkhnchul ★★★
()
Ответ на: комментарий от arkhnchul

Ок, в общем понял. Оба варианта имеют право на жизнь, в зависимости от ситуации, что касается перегрузки ф-ий, как и параметров по умолчанию, тут на мой субъективный взгляд код проще писать, но сложней затем читать и поддерживать, т.к. логика работы у перегруженных ф-ий часто имеет серьезные отличие, но опять же, все это мое субъективное мнене. C get норм, но опять же, если нужно установить к примеру 3 параметра, получится

db.setIp(ip);
db.setPort(port);
db.setLogin(login);
можно и забыть какой-нибудь set вызывать, а тут
db.setSettings(ip, port, login);
или
db.setSettings(dbSettings);
компилятор напомнит, что что-то забыл

da17
() автор топика

Как-то рефакторил древнючий код, и из первого варианта переделывал во второй. Количество аргументов у некоторых функций было 50+.

orm-i-auga ★★★★★
()

Сложней тестировать, т.к. надо для тестирования создавать объект DbSettings

А в чём сложность?

no-such-file ★★★★★
()

Недостатки 1. Сложней тестировать, т.к. надо для тестирования создавать объект DbSettings;

Его не нужно тестировать, это пассивный объект-контейнер без логики.

Сложней для анализа, т.к. для того что бы понять откуда берутся настройки, придется вообще пробираться в класс DbSettings.

Наоборот, плюс, т.к. есть одно место где можно описать как пользоваться настройками и мб даже нариовать pretty-printer содержимого. Это же можно сделать и рядом с кодом метода, но там обычно можно рассказать о чём-то другом.

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

DbConnector({ ip, host, port });
Нужно делать отдельный объект или нет в основном зависит от кол-ва настроек: если их много или планируется увеличение, то нужно делать объект. Также, если планируется хранить настройки и передать из модуля в модуль, то тоже лучше засовывать в объект чтобы не разводить бесполезных copy + paste из передачи мн-ва аргументов через ф-ии.

anonymous
()

DbConnector.toHost('127.0.0.1').byPort('5432').connect()

system-root ★★★★★
()

В общем случае, естественно, первый вариант + перегрузки и аргументы по умолчанию. Но если аргументов овердофига, и/или из них овердофига опциональных то имеет смысл сделать второе, причём обычно так:

DbConnector(DbSettings().SetIp().SetHost().SetPort())
slovazap ★★★★★
()
Ответ на: комментарий от slovazap
DbConnector.toHost('127.0.0.1').byPort('5432').connect()
DbConnector(DbSettings().SetIp().SetHost().SetPort())

Это два самых худших гогнокодо-варианта из всех возможных.

Первый заставляет держать состояние приготовления настроек внутри коннектора тем самым в одном объекте скрещиввая две сущности (коннектору настройки могут быть не нужны вне конструктора).

И оба варианта это один из классических примеров ООП головного мозга, а именно нагромождение сеттеров/геттеров на пустом месте.

anonymous
()
Ответ на: комментарий от anonymous

Насчёт первого согласен. По второму аргументов не вижу, и жду твой не-гогнокодо-вариант для жавы, коли уж о ней речь.

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

slovazap ★★★★★
()
Ответ на: комментарий от anonymous

анон, ты офигел builder ООП головного мозга называть когда он тут решает все проблемы ТС, от именования и последовательности, до неопределенности количества параметров?

system-root ★★★★★
()

а вариант DbConnector(const string &url) как основной уже совсем не в моде? :-)

DbConnector(ip,host,port), DbConnector(DbSettings &) навязывают лишние сущности. Первый подразумевает и требует сетевые соединения. Второй требует от пользователя уметь оперировать ещё одним классом.

MKuznetsov ★★★★★
()

Никаких плюсов во втором варианте не вижу. Допустим, ты читаешь код. Если написано, как в первом варианте, то все сразу понятно. Если как во втором - то лезешь, ищешь определение этих DbSettings, а когда находишь, то понимаешь, что имелся в виду собственно первый вариант. Кроме того, я не понимаю, как во втором случае код может быть «более лаконичным», когда добавляется плюс один класс.

hotpil ★★★★
()

на практике и первый и второй и многие другие варианты встречаются, лично мне пофиг - лишь бы работу выполняли

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от no-such-file

вот я тоже не вижу сложности, наборот - сколько проблем уходит, когда куча дефолтных параметров есть

I-Love-Microsoft ★★★★★
()

И то и другое в интерфейсе. Внутри - хранить структуру, дабы параметры не смешивались с другими членами класса.

Но вообще на первый вариант можно забить ибо:

DbConnection(DbSettings(host, IP, port));
Settings отвечает за валидность параметров. Connection никогда не получит мусор.

invy ★★★★★
()
Последнее исправление: invy (всего исправлений: 1)

В случае C++ никакой разницы не будет (если, кончено, ты не нагородишь виртуальных методов).

И в том и в другом случае ты будешь ломать ABI в случае изменений.

Если посмотреть с позиций абстрактного проектирования, то тоже никакой разницы: ну получишь ты сильную межмодульную связь между DbConnector и DbSettings... Оно тебе будет легче?

С клиентской точки зрения — вообще похрен. Тебе нужен будет пул или хотя бы фабрика уровня ШайтанМашнина.ДайМнеСоединениеСБД(), а сигнатура конкретных конструкторов будет где-то очень глубоко, если не «по приглашениям».

Macil ★★★★★
()
Ответ на: комментарий от anonymous

Это два самых худших гогнокодо-варианта из всех возможных.

Монады без do-нотации и ленивой семантики — извращение!

Macil ★★★★★
()
Ответ на: комментарий от invy

Settings отвечает за валидность параметров. Connection никогда не получит мусор.

Угу. Вот только *как*? Эксцепшены кидать? Конструктор, чай!

Macil ★★★★★
()
Ответ на: комментарий от Macil

Если посмотреть с позиций абстрактного проектирования, то тоже никакой разницы: ну получишь ты сильную межмодульную связь между DbConnector и DbSettings... Оно тебе будет легче?

О как, т.е. вариант с DbSettings это сильное связывание, что-то долго размышлял над этим термином, а сейчас вдруг понял, что в виду имеют.

da17
() автор топика
Ответ на: комментарий от system-root

анон, ты офигел builder ООП головного мозга называть когда он тут решает все проблемы ТС, от именования и последовательности, до неопределенности количества параметров?

Глаза открой и посмотри что за чушь советуешь. Его DbConnector это не фабрика соединений, а объект с состоянием соединения и API для работы с базой. В твоём варианте нужно сделать ещё один класс который после connect() вернёт DbConnector и это в данном случае overengineering.

anonymous
()
Ответ на: комментарий от slovazap

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

Разве в яве нельзя сделать поля класса public и работать с ними как со структурой? В совокупности с final полями и перегрузкой конструктора можно отлично обойтись без геттеров/сеттеров. Мой вариант выглядит так

DbConnector(Settings(ip, host, port));

Settings settings = new Settings(...);

settings.Parm = argh;
...

DbConnector(settings);


или первый вариант из топика.
anonymous
()

Например так:

my $conf = {
  type => 'mysql',
  name => 'test',
  user => 'user',
  pass => 'pass',
  host => '127.0.0.1', # из дефолта
  port => 3306, # из дефолта
  opts => { # из дефолта
    mysql_enable_utf8 => 1,
    AutoCommit => 1,
  },
};
# ^^^ берётся из конфига (сериализованный yaml), мержится с дефолтом
# хэш выше - не обычный, не bless()ed
my $conf->{dsn} = sprintf "dbi:%s:dbname=%s;host=%s;port=%d",
  $conf->{type}, $conf->{name}, $conf->{host}, $conf->{port};
my $dbh = DBI->connect($conf->{dsn}, $conf->{user}, $conf->{pass}, $conf->{opts});
# ... и далее работаем с $dbh
anonymous
()
Ответ на: комментарий от anonymous

Вообще я на си++ пишу, просто книгу по проектированию встретил на яве. Вот мне понравилась идея с проверкой валидности параметров объектом Settings, т.к. всю логику проверок можно инкапсулировать в него и скрыть от пользователя. Но тут опять возникает проблема, значительное количество проверок это увеличение скорости отладки т.к. проблемы либо не возникают вообще, либо выявляются на ранних стадиях. Остается найти компромис между увеличением объема кода и как следствие сложностью.

da17
() автор топика
Ответ на: комментарий от da17

Подводя итог, в принципе не вижу смысла медитировать над этим вопросм дизайна. Оба варианта получается приемлемы, зачем я все это спрашиваю, мне достался крупный проект на доработку, который уже довел до ручки пару программистов, подобного рода детали для меня очень важны и во всех случаях планирую придерживаться правила - «чем код проще читать и анализировать, тем лучше»

da17
() автор топика
Ответ на: комментарий от da17

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

Логика проверок и так скрыта от пользователя - её делает либо Settings, либо коннектор. Всю логику проверок засунуть в Settings нельзя т.к. существуют общие проверки, которые можно засунуть в Settings, и зависимые от поведения коннектора. Последние нельзя засунуть в Settings.

anonymous
()
Ответ на: комментарий от da17

pdf в интернете скачай да сделай вывод самостоятельно.

fluorite ★★★★★
()
Ответ на: комментарий от anonymous

Разве в яве нельзя сделать поля класса public и работать с ними как со структурой? В совокупности с final полями и перегрузкой конструктора можно отлично обойтись без геттеров/сеттеров. Мой вариант выглядит так

Многословное уродство. Chainable setters намного лучше.

slovazap ★★★★★
()
Ответ на: комментарий от slovazap

Это вообще смысла никакого не имеет.

Отнюдь это позволяет работать с изменяемым кол-вом папаметров,которые можно структурировать и проверять как вам хочется без необходимости лезть в основной класс... Ну это мое видение

jo_b1ack ★★★★★
()
Ответ на: комментарий от jo_b1ack

DbSettings здесь вообще не нужен - всё что ты делаешь в его конструкторе можно и нужно делать в конструкторе DbConnector.

slovazap ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.