Ответ 1
Стандартная библиотека решает эту проблему несколькими способами:
1) Без "Центрального" реестра
Пример этого - разные хэш-алгоритмы. Пакет crypto
определяет интерфейс Hash
(тип и методы). Конкретные реализации находятся в разных пакетах (фактически вложенных папок, но не обязательно), например crypto/md5
и crypto/sha256
.
Когда вам нужен "хэшер", вы явно указываете, какой из них вы хотите, и создайте экземпляр этого файла, например.
h1 := md5.New()
h2 := sha256.New()
Это самое простое решение, и оно также дает хорошее разделение: пакет Hash
не должен знать или беспокоиться о реализации.
Это предпочтительное решение, если вы знаете, или вы можете решить, какую реализацию вы хотите ранее.
2) С "Центральным" реестром
Это в основном ваше предлагаемое решение. Реализации должны каким-то образом регистрироваться (обычно в пакете init()
).
Примером этого является image
. Пакет определяет image
интерфейс и несколько его реализаций. Различные форматы изображений определяются в разных пакетах, таких как image/gif
, image/jpeg
и image/png
.
В пакете image
есть функция Decode()
, которая декодирует и возвращает image
из указанного io.Reader
. Часто неизвестно, какой тип изображения поступает от читателя, и поэтому вы не можете использовать алгоритм декодирования определенного формата изображения.
В этом случае, если мы хотим, чтобы механизм декодирования изображения был расширяемым, регистрация неизбежна. Самый чистый, чтобы сделать это, - это функции пакета init()
, которые запускаются путем указания пустого идентификатора имени пакета при импорте.
Обратите внимание, что это решение также дает вам возможность использовать конкретную реализацию для декодирования изображения, конкретные реализации также предоставляют функцию Decode()
, например png.Decode()
.
Итак, лучший способ?
В зависимости от ваших требований. Если вы знаете или можете решить, какая реализация вам нужна, перейдите к № 1. Если вы не можете решить или не знаете, и вам нужна расширяемость, перейдите к № 2.
... Или перейдите к # 3, представленному ниже.
3) Предложение 3-го решения: "Пользовательский" реестр
У вас все еще может быть удобство "центрального" реестра с интерфейсом и реализациями, разделенными за счет "автоматической расширяемости".
Идея заключается в том, что у вас есть интерфейс в пакете pi
. У вас есть реализации в пакете pa
, pb
и т.д.
И вы создадите пакет pf
, который будет иметь нужные вам методы "factory", например. pf.NewClient()
. Пакет pf
может ссылаться на пакеты pa
, pb
, pi
без создания циклической зависимости.