В чем разница между фабрикой и обычным конструктором
Что проверяют
Знаете ли вы, что в Go нет конструкторов в привычном смысле, и понимаете ли, чем функция NewXxx отличается от фабрики, которая выбирает реализацию. Следом часто просят показать, где в стандартной библиотеке встречается каждый подход.
Как отвечать
Начните с того, что в Go объект создаётся литералом структуры, а «конструктор» это просто соглашение: функция NewUser(...), которая проверяет аргументы, заполняет поля и защищает инварианты. Язык её никак не выделяет.
- конструктор-функция: возвращает конкретный тип, обычно
*Userили(*User, error), если входные данные могут быть неверными. Примеры:bufio.NewReader,bytes.NewBuffer; - фабрика: возвращает интерфейс и сама решает, какую реализацию отдать. Вызывающий код про конкретный тип не знает. Примеры:
sha256.New()возвращаетhash.Hash,net.Dial("tcp", addr)возвращаетnet.Conn; - реестр драйверов:
sql.Open("postgres", dsn)выбирает реализацию по имени, которое драйвер зарегистрировал вinit(). Это фабрика с реестром; - functional options:
NewServer(addr, WithTimeout(5*time.Second))решает проблему длинных списков параметров без перегрузки, которой в Go нет; - нулевое значение: хороший тип часто вообще не требует конструктора,
sync.Mutexиbytes.Bufferготовы к работе сразу после объявления.
Дальше назовите правило выбора. Конструктор нужен, когда тип один и важно проверить входные данные. Фабрика оправдана, когда реализаций несколько и выбор зависит от конфигурации: хранилище в памяти для тестов и PostgreSQL в проде. Сошлитесь на принцип «принимайте интерфейсы, возвращайте структуры»: фабрика, которая возвращает интерфейс, это осознанное исключение.
Где спотыкаются
Переносят в Go джавовые AbstractFactory и заводят интерфейс на каждый тип. Код раздувается, а подменять реализацию всё равно нужно в одном месте.
Возвращают из фабрики nil на неизвестный тип вместо ошибки. Вызывающий получает панику при первом вызове метода, а не понятное сообщение.
Не знают ловушку с типизированным nil: если функция с результатом Shape вернёт var c *Circle = nil, проверка shape == nil даст false, потому что у интерфейса уже есть тип.