2014-09-02 3 views
3

Предположим, что у меня есть тип type T int, и я хочу определить логику для работы с этим типом.Проектирование пакетов Go: когда я должен определять методы по типам?

Какую абстракцию следует использовать и когда?

  • Определение метода по этому типу:

    func (T t) someLogic() { 
    // ... 
    } 
    
  • Определение функции:

    func somelogic(T t) { 
    // ... 
    } 
    
+0

Вторая версия (функция) в основном бессмысленна без возвращаемого значения. В противном случае: это просто зависит. – Volker

+0

@ Volker: это не распространено, но эта форма иногда полезна с элементами-указателями, срезами или картами; например 'func (p IntSlice) Sort()' в пакете сортировки – JimB

ответ

5

Некоторые ситуации, в которых вы склонны использовать методы:

  • мутирует приемник: Вещи, которые изменяют поля объектов, часто являются методами. Для ваших пользователей менее удивительно, что x.Foo изменит X, чем Foo(x).
  • Побочные эффекты через приемник: Вещи часто являются методами типа, если у них есть побочные эффекты в/через объект более тонко, например, запись в сетевое соединение, входящее в состав struct, или запись через указатели или фрагменты или так далее в структуре.
  • Доступ к закрытым полям: Теоретически, что-либо внутри одного и того же пакета может видеть неэкспортируемые поля объекта, но чаще всего это только конструктор и методы объекта. Другие взгляды на невыполненные поля похожи на C++ friend s.
  • Обязательно для удовлетворения интерфейса: Только методы могут быть частью интерфейсов, поэтому вам может понадобиться сделать что-то вроде метода, чтобы просто удовлетворить интерфейс. Например, Peter Bourgon's Go intro определяет type openWeatherMap как пустую структуру с помощью метода, а не как функцию, чтобы удовлетворить тот же интерфейс weatherProvider, что и другие реализации, которые не являются пустых структур.
    • Тест раскорчевка: В частном случае выше, иногда интерфейсы помогают stub out objects for testing, поэтому ваши реализации заглушек, возможно, придется быть методами, даже если они не имеют никакого состояния.

Некоторые где вы склонны использовать функции:

  • Конструкторы:func NewFoo(...) (*Foo) является функцией, а не метод. У Go нет понятия конструктора, так вот как это должно быть.
  • Выполнение на интерфейсах или базовых типов: Вы не можете добавлять методы на interface s или базовые типы (если вы не используете type, чтобы сделать их новым типом). Итак, strings.Split и reflect.DeepEqual должны быть функциями. Кроме того, io.Copy должен быть функцией, потому что он не может просто определить метод на Reader или Writer. Обратите внимание, что они не объявляют новый тип (например, strings.MyString), чтобы обойти невозможность делать методы для базовых типов.
  • Перемещение функциональности негабаритов типов или пакеты: Иногда один тип (думают User или Page в некоторых веб-приложениях) скапливается много функциональности, и что вредит читаемость или организация, или даже вызывает структурные проблемы (например, если он становится сложнее избегать cyclic imports). Выполнение не-метода вне метода, который не является, мутирующий приемник, доступ к нераспределенным полям и т. Д. Может быть этапом рефакторинга для перемещения своего кода «вверх» на более высокий уровень приложения или «над» на другой тип/пакет или автономная функция - это самое естественное долгосрочное место для него. (Hat наконечник Steve Francia для включения такого примера из hugo в разговоре о своих Их ошибках.)
  • удобства «использовать значения по умолчанию» функция: Если ваши пользователям могли бы хотеть быстрый способ использовать значение объекта «по умолчанию» без явного создания объекта, вы можете выставлять функции, которые делают это, часто с тем же именем, что и объектный метод. Например, http.ListenAndServe() - это функция уровня пакета, которая делает тривиальный http.Server и вызывает на нем ListenAndServe.
  • Функция для прохождения поведения вокруг: Иногда вам не нужно определить тип и интерфейс просто передать функции вокруг и голая функции достаточно, как и в http.HandleFunc() или template.Funcs() или для регистрации go vet проверки и так далее. Не заставляйте его.
  • функция, если объект ориентация будет вынуждена: Произнесите main() или init() чисты, если они взывают к некоторым хелперам, или у вас есть частные функции, которые не похожи на каком-либо полей объект и никогда не будут. Опять же, не чувствуйте, что вам нужно заставить OO (à la type Application struct{...}), если в вашей ситуации вы ничего не получите.

Если у вас есть сомнения, если что-то является частью вашего экспортированного API и существует естественный выбор того, к какому типу присоединяться, сделайте его методом. Однако, не деформируйте свой дизайн (вытаскивая проблемы в свой тип или пакет, которые могут быть раздельными), так что что-то может быть методом. Writer s не WriteJSON; это было бы сложно реализовать, если бы они это сделали. Вместо этого у вас есть функция JSON, добавленная к Writer с помощью функции в другом месте, json.NewEncoder(w io.Writer).

Если вы все еще не уверены, первые записи, так что документация читает четко, то так, что код читает естественно (o.Verb() или o.Attrib()), затем идут с тем, что чувствует себя хорошо, не корпел над ней слишком много, так как часто вы можете изменить это позже.

3

Используйте метод, если вы манипулируете внутренней secrets о е ваш объект

(T *t) func someLogic() { 
     t.mu.Lock() 
     ... 
    } 

Используйте функцию, если вы используете public interface объекта

func somelogic(T *t) { 
     t.DoThis() 
     t.DoThat() 
    } 
0

, если вы хотите изменить T объект, используйте

func (t *T) someLogic() { 
// ... 
} 

если вы donn't изменить T объект и хотел бы origined-объектный способ, использовать

func (t T) someLogic() { 
// ... 
} 

но Remeber, что это будет генерировать temporay объект T для вызова someLogic

если ваш язык как способ с делает, используйте

func somelogic(t T) { 
     t.DoThis() 
     t.DoThat() 
} 

или

func somelogic(t T) { 
     t.DoThis() 
     t.DoThat() 
    } 

еще одна вещь, тип behide вар в golang.

Смежные вопросы