Проблема циклического импорта в маршрутизации мультиплексора gorilla с несколькими подпакетами

Вот структура моего проекта

--main package
--|--child_package1
--|--child_package2
--|--child_package3

У меня есть все маршруты и управление вызовами методов для вызовов API, перечисленных в main_package.

Обработчик маршрутизатора из main_package.go выглядит так:

func Handlers(db *sql.DB, customeruploadFile string) *mux.Router {
  router := mux.NewRouter()
  router.HandleFunc("/api1", child_package1.method )
  router.HandleFunc("/api2", child_package2.method)
  router.HandleFunc("/api3", child_package3.mehtod)

  fileHandler := http.FileServer(http.Dir("./client/compiled"))
  router.PathPrefix("/").Handler(http.StripPrefix("/", fileHandler))
  return router
}

Проблема в том, что когда я пишу тестовые примеры для дочерних пакетов, мне нужен этот метод Handlers для создания тестового сервера, для этого мне нужно импортировать main_package в child_packages, тогда, как очевидно, происходит циклический импорт, так как child_packages импортируются в main_package. Может ли кто-нибудь предложить мне лучший подход к решению этой проблемы?


person Mahesh Haldar    schedule 11.12.2015    source источник
comment
Это говорит о том, что структура вашего пакета плохо продумана. Рассматривали ли вы перенос функции Handlers в отдельный пакет?   -  person elithrar    schedule 11.12.2015
comment
Да, я сделал, но снова происходит импорт цикла. Причина, по которой новый пакет будет вызывать child_package, а child_package снова будет вызывать handler package. Проблема не решена с новым пакетом обработчика.   -  person Mahesh Haldar    schedule 11.12.2015
comment
Пакеты не должны нуждаться друг в друге для такого тестирования. Не зная больше о вашей структуре, «основной пакет», вероятно, должен быть «основным пакетом» и импортировать то, что ему нужно для настройки, и ничем другим не импортироваться.   -  person elithrar    schedule 11.12.2015
comment
Только вежливо указать, когда вы делаете перекрестную публикацию, чтобы люди не тратили время на дублирование ответов: groups.google.com/forum/?fromgroups#!topic/golang-nuts/   -  person JimB    schedule 11.12.2015


Ответы (1)


Я предполагаю, что ваш main_package не является пакетом main в Go. Я думаю, что child_packages не должны находиться под main_package, так как наша цель — отделить каждый пакет друг от друга.

Это шаблон, который я сейчас использую в своем проекте, чтобы избежать конфликтов зависимостей:

project/
├── main_package
│   └── main_package.go
├── brokers
│   └── brokers.go
├── child_package1
│   └── child_package1.go
├── child_package2
│   └── child_package2.go
└── child_package3
    └── child_package3.go

По сути, каждый пакет никогда не должен иметь дело ни с чем вне себя (или, по крайней мере, делать это как можно реже). broker будет единственной стороной, которая «договаривается» между любыми двумя пакетами.

// main_package.go
package main_package

import (
    "path/to/sql"
    "path/to/mux"
    "path/to/brokers"
)

// Never use selectors from packages directly
// but create a `Broker` object for each endpoint
var bk1 = brokers.New("/api1")
var bk2 = brokers.New("/api2")
var bk3 = brokers.New("/api3")

func Handlers(db *sql.DB, customeruploadFile string) *mux.Router {
    router := mux.NewRouter()

    // each broker has its own `MyHandler` function
    router.HandleFunc("/api1", bk1.MyHandler)
    router.HandleFunc("/api2", bk2.MyHandler)
    router.HandleFunc("/api3", bk3.MyHandler)

    fileHandler := http.FileServer(http.Dir("./client/compiled"))
    router.PathPrefix("/").Handler(http.StripPrefix("/", fileHandler))
    return router
}

Пакет brokers является центральным интерфейсом для связи

// brokers.go
package brokers

import (
    "path/to/child_package1"
    "path/to/child_package2"
    "path/to/child_package3"
    "net/http"
)

type Broker interface {
    MyHandler(http.ResponseWriter, *http.Request) 
}

// Factory function to create a `Broker` instance
func New(uri string) Broker {
    if uri == "/api1" {
        return Broker( new(child_package1.Delegate) )
    } else if uri == "/api2" {
        return Broker( new(child_package2.Delegate) )
    } else if uri == "/api3" {
        return Broker( new(child_package3.Delegate) )
    }
    return nil
}

Теперь child_packageX больше не привязан к какой-либо внутренней зависимости, при условии, что он предоставляет «представителя» или Delegate объект для общения с брокером.

// child_package1.go
package child_package1

import "net/http"

type Delegate struct {
   // Optional parameters can be carried by the Delegate
   // to be used in the created Broker anywhere
}

func (d *Delegate) MyHandler(w http.ResponseWriter, r *http.Request) {
  // Maybe return a JSON here
}

У каждого потомка может быть свой собственный MyHandler, который выполняет разные действия для разных вызовов API, не зная, какие конечные точки они обслуживают.

// child_package2
package child_package2

import "net/http"

type Delegate struct {}

func (d *Delegate) MyHandler(w http.ResponseWriter, r *http.Request) {
    // Maybe return an XML here
}

main_package импортирует не весь пакет child_packageX, а только пакет broker. Вы можете написать тест, который импортирует пакет broker вместо реальных пакетов, или вы даже можете написать другой брокер для тестирования.

package test

import (
    "testing"
    "path/to/main_package"
)

func TestMain(*testing.T) {
    // test the routing in `main_package`
}

Вы больше не тестируете функциональность функции-обработчика, а одну из конечных точек, предоставляемых брокером. Это побуждает вас писать универсальные функции-обработчики и фокусироваться на конечных точках более высокого уровня.

package test

import (
    "testing"
    "path/to/broker"

)

func TestGetJSONAlright(*testing.T) {
     bk1 := brokers.New("/api1")
     // test if I get JSON here
}

func TestGetXMLAlright(*testing.T) {
    bk1 := brokers.New("/api2")
    // test if I get XML here
}

На мой взгляд, это мощный шаблон, поскольку вы можете написать более «универсальные» обработчики и просто подключить их к нужным вам маршрутам.

person Pie 'Oh' Pah    schedule 11.12.2015
comment
Спасибо, что поделились этим подходом к брокерскому пакету. Это действительно отличное решение для предотвращения циклического импорта. Но моя проблема заключалась в основном в совместном использовании обработчиков маршрутов мультиплексирования горилл с другими дочерними пакетами, чтобы обработчики маршрутов могли совместно использоваться пакетами и тестовыми утверждениями. Вы можете поделиться своими мыслями по этому поводу?? - person Mahesh Haldar; 14.12.2015
comment
@MaheshHaldar Я полностью переписал ответ для вашего случая. Надеюсь, это сработает. - person Pie 'Oh' Pah; 14.12.2015
comment
@MaheshHaldar Пожалуйста, проголосуйте за лучший ответ, если считаете его полезным. - person Pie 'Oh' Pah; 18.01.2016