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{}
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 全部备份安卓app数据包括密码,可以复制到另一手机上运行
  • ¥15 Python3.5 相关代码写作
  • ¥20 测距传感器数据手册i2c
  • ¥15 RPA正常跑,cmd输入cookies跑不出来
  • ¥15 求帮我调试一下freefem代码
  • ¥15 matlab代码解决,怎么运行
  • ¥15 R语言Rstudio突然无法启动
  • ¥15 关于#matlab#的问题:提取2个图像的变量作为另外一个图像像元的移动量,计算新的位置创建新的图像并提取第二个图像的变量到新的图像
  • ¥15 改算法,照着压缩包里边,参考其他代码封装的格式 写到main函数里
  • ¥15 用windows做服务的同志有吗