doww38701 2017-04-08 21:51
浏览 38
已采纳

Go中不允许克服导入周期

I understand the problem, as per the answer here, however, I could really use help or a more detailed code explanation of how it's overcome.

My situation is this: I used to have models and controllers separated, and in my models package I had a datastore.go file containing an interface of all the model functions:

package models

type DSDatabase interface {
    CreateUser(ctx context.Context, username string, password []byte) (*datastore.Key, error)
    // More model functions
}

type datastoreDB struct {
    client *datastore.Client
}

var (
    DB DSDatabase
    _  DSDatabase = &datastoreDB{}
)

func init() {
    // init datastore
}

This was all fine because the model functions were also located within the models package, so my functions in the controller package could freely call models.DB.CreateUser(ctx, "username", []byte("password")).

Now, I have decided to move all the above code to a datastore package, whereas the model for CreateUser is located in a user package. In other words, package user now contains both controller and model functions, for which the controller related functions rely on datastore package, while the DSDatabase interface rely on the user model functions.

I would really appreciate help figuring out how to overcome the import cycle while keeping the DSDatastore interface separate from all the other packages such as home and user.


in case the above is not clear enough, the above code has changed to:

package datastore

import (
    "github.com/username/projectname/user"
)

type DSDatabase interface {
    user.CreateUser(ctx context.Context, username string, passwoUserRegister(ctx context.Context, username string, password []byte) (*datastore.Key, error)
}

...

while in my user package I have this in a controller-related file:

package user

import (
    "github.com/username/projectname/datastore"
)

func CreateUserPOST(w http.ResponseWriter, r *http.Request) {
    // get formdata and such
    datastore.DB.CreateUser(ctx, "username", []byte("password"))
}

and in another model-related file I have:

package user

import (
    "github.com/username/projectname/datastore"
)

func (db *datastore.datastoreDB) CreateUser(ctx context.Context, username string) (*User, error) {
    key := datastore.NameKey("User", username, nil)
    var user User
    err := db.client.Get(ctx, key, &user)
    if err != nil {
        return nil, err
    }
    return &user, nil
}

Which of course results in an import cycle, that I sadly can't figure out how to overcome..

  • 写回答

1条回答 默认 最新

  • douguaidian8021 2017-04-09 00:21
    关注

    First things first, you cannot define a method, in pacakge A, on a type declared in package B.

    So this...

    package user
    
    import (
        "github.com/username/projectname/datastore"
    )
    
    func (db *datastore.datastoreDB) CreateUser(ctx context.Context, username string) (*User, error) {
        key := datastore.NameKey("User", username, nil)
        var user User
        err := db.client.Get(ctx, key, &user)
        if err != nil {
            return nil, err
        }
        return &user, nil
    }
    

    ...should not even compile.

    This here...

    package datastore
    
    import (
        "github.com/username/projectname/user"
    )
    
    type DSDatabase interface {
        user.CreateUser(ctx context.Context, username string, passwoUserRegister(ctx context.Context, username string, password []byte) (*datastore.Key, error)
    }
    

    ...this is also invalid Go code.


    As to your question... one thing you could do is to define the Datastore interface inside the user package and have the implementation live in another package, this lends itself nicely for when you need different implementations of one interface. If you do this your user package does not need to know about the datastore package anymore, the datastore package still has to know about the user package though, which is a OK.

    An example:

    package user
    
    import (
        "context"
    )
    
    type DSDatabase interface {
        CreateUser(ctx context.Context, username string, password []byte) (*User, error)
        // ...
    }
    
    // This can be set by the package that implements the interface
    // or by any other package that imports the user package and
    // a package that defines an implementation of the interface.
    var DB DSDatabase
    
    type User struct {
        // ...
    }
    
    func CreateUserPOST(w http.ResponseWriter, r *http.Request) {
        // get formdata and such
        DB.CreateUser(ctx, "username", []byte("password"))
    }
    

    The package with the implementation of the interface:

    package datastore
    
    import (
        "context"
        "github.com/username/projectname/user"
    )
    
    // DB implements the user.DSDatabase interface.
    type DB struct { /* ... */ }
    
    func (db *DB) CreateUser(ctx context.Context, username string) (*user.User, error) {
        key := datastore.NameKey("User", username, nil)
        var user user.User
        err := db.client.Get(ctx, key, &user)
        if err != nil {
            return nil, err
        }
        return &user, nil
    }
    
    func init() {
        // make sure to initialize the user.DB variable that
        // is accessed by the CreateUserPOST func or else you'll
        // get nil reference panic.
        user.DB = &DB{}
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥100 set_link_state
  • ¥15 虚幻5 UE美术毛发渲染
  • ¥15 CVRP 图论 物流运输优化
  • ¥15 Tableau online 嵌入ppt失败
  • ¥100 支付宝网页转账系统不识别账号
  • ¥15 基于单片机的靶位控制系统
  • ¥15 真我手机蓝牙传输进度消息被关闭了,怎么打开?(关键词-消息通知)
  • ¥15 装 pytorch 的时候出了好多问题,遇到这种情况怎么处理?
  • ¥20 IOS游览器某宝手机网页版自动立即购买JavaScript脚本
  • ¥15 手机接入宽带网线,如何释放宽带全部速度