Комментарии 7
Паттерн крутой, но так он меняет cостояние объекта, применяя его в методах структуры стреляете себе в ногу, например необходимо сбрасывать значения полей при повторных вызовах.
Есть 2 мнения на этот счет. Однако мой подход приведен для наглядности примера с паттерном.
Можно сделать функцию, которая будет сбрасывать значение или возвращать null по умолчанию.
Не вижу почему это выстрел себе в ногу. Если возможно, буду благодрен за пример
В том и проблема что можно написать любую функцию, которая будет менять состояние ScannedItem, и будет очень сложно контролировать все возможные варианты, например так:
func (item *ScannedItem) ChangeGS1(value bool) ScanFunc {
return func(item *ScannedItem) {
item.GS1 = value
}
}
и тогда в зависимости от порядка опций в вызове ScannedItem айтем с одними и теме же Datamatrix может быть и валидным и не валидным
Option pattern удобно и более правильно использовать для установки параметров структуры отличных от дефолтных по умолчанию, например так:
func NewScannedItem(opts ...ScanFunc) *ScannedItem {
item := &ScannedItem{}
for _, fn := range opts {
fn(item)
}
return item
}
func WithGS1(value bool) ScanFunc {
return func(item *ScannedItem) {
item.GS1 = value
}
}
func main() {
NewScannedItem(WithGS1(true))
}
В вашем случае, если в методе Scan надо научиться запускать кастомные валидаторы, то можно в структуру добавить validators []ScanFunc, задавать их список в конструкторе с так же с помощью opts, а в методе Scan вызывать
Спасибо за пример - асбсолютно согласен!
type OptFunc func(*Opts)
type Opts struct {
maxConn int
tls bool
}
func WithTLS() OptFunc {
return func(o *Opts) {
o.tls = true
}
}
func MaxConn(con_number int) OptFunc {
return func(o *Opts) {
o.maxConn = con_number
}
}
func DefaultCon() Opts {
return Opts{
10, false,
}
}
type Server struct {
Opts
}
func NewServer(opts ...OptFunc) *Server {
o := DefaultCon()
for _, fn := range opts {
fn(&o)
}
return &Server{o}
}
Можно будет даже дополнить статью им.
Выглядит как сложное решение непонятно какой даже проблемы. Чем это лучше последовательного вызова Check-функций? Их все равно надо написать и передать в Scan
a.Scan()
a.CheckValidWithReason()
// a.CropDatamatrixOption()
a.CheckGS1Option()
Я ещё понимаю если бы было
a, err := ScanWithOptions(CheckValidWithReason(), CheckGS1Option())
обычно так это и используют
Паттерны Go — Паттерн «Опции» — ключ к простому рефакторингу в будущем