сущности, запрещающий обход конструктора? Или это полностью забота разработчика использующего пакет что он получит панику и частичное выполнение кода если создаст публичную структуру руками? Или нужно в каждый метод пихать проверку? Или сделать структуру приватной чтоб ее нельзя было создать руками а конструктор чтоб возвращал preemptive interface описанный в пакете или приватную структуру для сохранения которой консюмер должен описать ее интерфейс?
Пока что самым правильным кажется решение с валидацией в каждом метод, хоть и с оверхедной проверкой и бойлерплейтом
https://go.dev/play/p/VlbkmJpl4ZQ
Возвращай интерфейс. А сама структура пусть приватная будет
Вначале я так и делал) Но потом узнал про антипаттерн preemptive interface и линтер ругался на ireturn, так что ему нужно было в правила добавлять исключения) https://bryanftan.medium.com/accept-interfaces-return-structs-in-go-d4cab29a301b
линтер не просто так ругается на возвращение интерфейса, это же фу-фу-фу (не всегда, но почти) попробуйте отказаться от конструктора вообще, если он обязательный: инициализироваться в init, или неявно при первом использовании
Интерфейс фу-фу-фу, но init - топчик) Ну и init не решит проблему. Чем неявная инициализация лучше ошибки явной? Проверка что так что так в каждом методе нужна
Проверки иногда можно избежать: https://dave.cheney.net/2013/01/19/what-is-the-zero-value-and-why-is-it-useful Ну вы же наверное либу пишите. Разве не приятнее, когда либа прощает, а не принуждает нас? А чем принуждение к конструктору лучше непринуждения к нему?
Вот за что Go нельзя не любить: статье 10 лет, а она не устарела.
А если речь про обязательньіе поля? К примеру разраб заполнил не все, потом неявно мержить с остальньіми обязательньіми которьіе по дефолту при первом вьізове заполнять?
preemptive interface это когда конструктор интерфейс возвращает?
У тебя же сама структура может быть приватной а конструктор нет и ты можешь вернуть не интерфейс а приватную структуру
Да. Нужно только немного угомонить линтер и IDE а то они с ума сходят от unexported type). И положить рядом интерфейс в либу, если кому в консюмере нужно будет сохранить инстанс, чтоб не описьівать руками 100500 методов // Modulator a list of methods to store in the consumer as an interface. type Modulator interface { Alice() error Bob() error } // New instance of module //goland:noinspection GoExportedFuncWithUnexportedType func New() *module { //nolint:revive,golint m := &module{} return m } // New instance of module as interface func NewI() Modulator { //nolint:ireturn return New() } type module struct {} func (m *module) Alice() error { return nil } func (m *module) Bob() error { return nil } Хотя я до конца так и не проникся в чем боль ireturn по сравнению с возвращением приватной структурьі, что вьіигрьіваем? Можно даже сделать два конструктора один возвращает приватную структуру, второй интерфейс. Чтоб гарантированно подгорело у всех)
да, этот рул про запрет unexported structs from exported functions, самый разражающий, его везде и всюду приходится отключать. кто вообще придумал считать антипаттерном то что применяют повсюду без каких-либо проблем
типа type foo struct { ... } func ExportedFunc() foo { } ``` так?
да, линтеры издревле жалуются на это и игнорируют всех недовольных в багтрекерах, и отказываются отключать этот рул по умолчанию
Обсуждают сегодня