
Комментарии 9
Шо, опять?! Или снова?
просто Go на столько прост, что не о чем больше рассказывать)))
обе ссылки есть в статье, как предвосхищающие материалы. И она содержит новый контент, кроме того ещё и генератор для бойлерплейта.
Стоило всё таки прочитать статью, до того как писать язвительный комментарий.
Не совсем корректно сравнивать этот шаблон с "строителем" (чейнинг опций в тексте). У них разные назначения, в определенном смысле не пересекающиеся.
"Функциональные опции", это замещение отсутствующего в го keys variadic arguments. Если бы в сигнатуре функции можно было бы задавать, значения по-умолчанию и язык это поддерживал, потребности в этом паттерне не было бы.
"Строитель" - предназначен для конструированния сложной сущности и это DSL всё же больше. Испрользование "чейнинга" (fluid interface - распостранённое кстати название для этого дизайна) как строителя в формате статьи вообще ошибка (о чём в статье и написано, о несогласованности полученного объекта). Именно поэтому для строителя используют отдельный неизменяемый тип данных и по-итогам формируют состояние.
Мне кажется - чейнинг это инструмент, который применяется в строителе. Например, HouseBuilder после вызова нескольких методов не превратится в House - будет нужно вызывать условный Build(). Так что да, согласен - чейнинг не строитель и строитель не чейнинг. Потому не стал упоминать строителя и в целом - паттерны. Как будто ничего из перечисленного выше не реализуют какой-то конкретный паттерн.
Собственно, функц.опции чем-то напоминает и "строителя" и "стратегию" и что-то еще, что не могу вспомнить; Но все же они - что-то уникальное.
Функциональные опции - это действительно от недостатка kva. И интересно то, как сообщество, в условиях ограничений, создало новый подход.
Про строителя и сложный объект тоже согласен, но конкретно в конструкторах я его никогда не видел в го приложениях. Самое похожее, что вспомнил - это github.com/go-resty/resty. Но тут тоже не строитель?
Начнем с базы - высказывать свои мысли сложно, мне удалось в предыдущем комментарии, попробую исправится.
Я фокусировал внимание бы вот на чём - на том, что вы в тексте статьи называете "чейнинг". И то как (и для чего) предлагается использовать его - это ошибка и существенная. Сам этот дизайн имеет конкретное название - "fluid interface".
Теперь, отвечу на вопрос "почему использовать плохо использовать такой подход":
SetAddress(address string) *Server"
В тексте статьи вы пишете правильно - можно получить не согласованный объект. Однако ситуация куда хуже. Вы даже получив согласованный объект, нарушите его инкапсуляцию. Конструирование объекта (любым способом) предназначено для того, чтобы изолировать его состояние от окружающего мира. Если бы вы этого не делали - вам не нужен был бы setter.
Любой метод - это некое поведение. И ваш условный Server, экспонируя методы SetAddress, SetPort / etc - декларирует, что при установке этих значений, даже у запущенного в работу экземпляра - произойдут внутренние изменения. Более того он декларирует своей сигнатурой, что вернёт *Server тот который уже учёл все изменения.
Но у вас ни с опциями, ни с текучим интерфейсом этой цели не стоит -вы просто пытаетесь найти другой способ конструировать объект.
Если же вы не планируете поведения на изменения внутренных свойств - достаточно сделать их публичными - код выйдет даже короче, т.к. не надо ни чейнить, ни сеттеры писать.
Чтобы получать сложные объекты и нужен шаблон "Строитель", на который я указывал. Где вам понадобится ещё одна структура, иммутабельность вызова при каждом chain методе и доступ к "телу" конструируемого объекта, для формирования согласованного инстанса.
Вся разница в том, что fn opts - это kvargs, а fluid interface - уже полноценный DSL.
Однако оба подхода позволяют достичь общего результата, но по разным причинам. Builder - вам нужен, когда вы конструируете объект по некой конфигурации (resty замечательный, хоть и грязно имплементированный пример). А fn opts, это способ реализовать перегруженные конструкторы. Их можно использовать совместно или вместо друг друга.
Но повсеместное распостранение "грязного" строительства в го, где ленивый программист (в том числе и я, конечно-же) использует инстанс рабочей сущности для её же конструированние - пример анти-паттерна распостраненного в го. Так что его упоминание скорее требует порицания и куда большего внимания чем фразы "объект может получится не согласованный" - он может стать не согласованным в ходе исполнения.
Например вы сконструировали resty клиент. Передали его в несколько сервиса и в одном из них вызвали SetHost, в итоге оба используют - новый хост адрес, в то время как первый, считает, что всё ещё ходит на старый. В реальности - ситуация иная. Если бы передавался в обоих случаях ClientBuilder, по-значению - такого бы side-effect просто не могло случится.
Попытка дать анти-паттерну название, вряд ли может быть хорошей идеей :)
Спасибо, по описанию полезный будет инструмент, и надо попробовать. Сам уже некоторое время про такое думаю, чтобы болерейт автоматизировать, но руки никак не доходили до воплощения.
Functional options in Go