How do I properly structure a package with sub packages in Go where a single type will be the receiver on most of the methods?

huangapple go评论76阅读模式
英文:

How do I properly structure a package with sub packages in Go where a single type will be the receiver on most of the methods?

问题

我目前正在设计一个实用程序库的设计阶段,它将使与x-go-binding的交互变得更容易一些。(我以前用Python和xpyb做过这个。)例如,它将帮助查询EWMH规范中定义的信息,并将键绑定到回调函数。(还有更多功能。)所以作为我对包布局的初始想法,请考虑:

  • xutil
    • ewmh
    • keybind

每个都是自己的包。(类似于标准库的图像包的设置。)

我情况独特的地方在于,几乎每个x-go-binding调用都需要xgb连接对象或根窗口标识符的某种组合。因此,我认为将这些信息存储在如下所示的结构体中是有意义的:

type XUtilConnection struct {
    conn xgb.Conn
    root xgb.Id
    // 还有其他一些东西,比如事件到回调的映射
}

这样,我可以有一个工厂可以这样使用:

xconn = xutil.NewXUtilConnection(blah blah)

它可以这样使用:

xconn.get_active_window()
xconn.bind_key("Shift-a", my_callback_fun)

可能还会有这样的函数:

keybind.get_keycode("a")
ewmh.get_atom("_NET_ACTIVE_WINDOW")

当然,我的问题是,据我所知,接收器只能是在同一个包中声明的类型。如果我将我的包分开,我就不能在任何子包中使用我的XUtilConnection类型作为接收器。

我怀疑我的答案将是将这个大包分成不同的逻辑文件,但我担心这可能会导致命名空间混乱。(例如,实现EWMH规范可能需要100多个函数。)

我也知道我可以在每个子包中为我的XUtilConnection对象定义一个新的容器类型。(我听说这应该是一个包含单个成员XUtilConnection的结构体,以避免强制转换。)但这对我来说似乎是一个非常混乱的情况,并且会阻止我想要的语义。(即,使用XUtilConnection结构体在几个不同的模块中调用方法。)

英文:

I'm currently in the design stage of writing a utility library that will make interaction with the x-go-binding a bit easier. (I've done this before with Python and xpyb.) For example, it will help with querying information defined in the EWMH spec and binding keys to callback functions. (And much more.) So as my initial idea for a package layout, consider:

  • xutil
    • ewmh
    • keybind

Where each is its own package. (Similar to how the standard library's image package is setup.)

What's unique about my situation is that almost every x-go-binding call requires some combination of an xgb connection object or a root window identifier. Therefore, it makes sense to me to store this information in a struct like so:

type XUtilConnection struct {
    conn xgb.Conn
    root xgb.Id
    // a few other things, like a mapping of events to callbacks
}

So that I'd have a factory that could be used like so:

xconn = xutil.NewXUtilConnection(blah blah)

And it could be used like:

xconn.get_active_window()
xconn.bind_key("Shift-a", my_callback_fun)

There may also be functions like:

keybind.get_keycode("a")
ewmh.get_atom("_NET_ACTIVE_WINDOW")

My problem of course is that receivers, to my knowledge, can only be of types that have been declared in the same package. If I separate my packages, I cannot use my XUtilConnection type as a receiver in any of my sub-packages.

I suspect that my answer is going to be making this one big package separated into different logical files, but I fear that this might lead to namespace clutter. (For example, implementing the EWMH spec is probably on the order of 100+ functions.)

I am also aware that I could define a new container type in each sub-package for my XUtilConnection object. (I've heard that this should be a struct containing a single member XUtilConnection to avoid casting.) But this seems like a really messy situation to me, and would prevent the kind of semantics I'd like. (i.e., using an XUtilConnection struct to call methods in several different modules.)

答案1

得分: 2

我建议使用embedding

在xutil包中:

type XUtilConnection struct {
    *ewmh.EWMH        // 嵌入*ewmh.EWMH的所有方法
    *keybind.KeyBind  // 嵌入*keybind.KeyBind的所有方法
}

在xutil/ewmh包中:

type EWMH struct {
    Conn xgb.Conn
    Root xgb.Id
    // 和其他需要的额外字段
}

// 一些EWMH方法:
func (e *EWMH) GetAtom(name string) { ... }
func (e *EWMH) ...

在xutil/keybind包中:

type KeyBind struct {
    Conn xgb.Conn
    Root xgb.Id
    // 和其他需要的额外字段
}

// 一些keybind方法:
func (k *KeyBind) GetKeyCode(s string) { ... }
func (k *KeyBind) ...

这将使您能够直接在类型为*XUtilConnection的值上调用EWMHKeyBind的方法:

var c *XUtilConnection = ...
c.GetAtom("_NET_ACTIVE_WINDOW")  // 调用(*emwh.EWMH).GetAtom(string)
c.GetKeyCode("a")                // 调用(*keybind.KeyBind).GetKeyCode(string)
英文:

I would suggest to use embedding.

In package xutil:

  type XUtilConnection struct {
      *ewmh.EWMH        // Embed all methods of *ewmh.EWMH
      *keybind.KeyBind  // Embed all methods of *keybind.KeyBind
  }

In package xutil/ewmh:

  type EWMH struct {
      Conn xgb.Conn
      Root xgb.Id
      // and any additional fields that are needed
  }

  // Some EWMH methods:
  func (e *EWMH) GetAtom(name string) { ... }
  func (e *EWMH) ...

In package xutil/keybind:

  type KeyBind struct {
      Conn xgb.Conn
      Root xgb.Id
      // and any additional fields that are needed
  }

  // Some keybind methods:
  func (k *KeyBind) GetKeyCode(s string) { ... }
  func (k *KeyBind) ...

This will enable you to directly call EWMH's and KeyBind's methods on values of type *XUtilConnection:

var c *XUtilConnection = ...
c.GetAtom("_NET_ACTIVE_WINDOW")  // Call (*emwh.EWMH).GetAtom(string)
c.GetKeyCode("a")                // Call (*keybind.KeyBind).GetKeyCode(string)

答案2

得分: 0

我不确定我是否正确理解了问题(我对在讨论方法接收者时看到的包名感到困惑),但我猜想传递一个接口而不是[*]结构体可以使其在任何包中满足。人们也可以始终像下面这样做(完全未经测试的代码):

package foo

type Foo interface { Bar(); Baz() }

----
package qux

import "foo"

type qux struct { foo.Foo; myStuff t }

func Foo(f foo.Foo) foo.Foo {
    return qux{f, &t{}}
}

func (q qux) Bar() {
    // smthg
}

例如,在包qux中“覆盖”f.Bar并“继承”未更改的f.Baz()

英文:

I'm not sure if I understood correctly the problem (I'm confused by seeing package names where they seem to be discussed as method receivers?), but I guess passing around an interface instead of the [*]struct would enable satisfying it from within any package. One can also always do something like (totally untested code):

package foo

type Foo interface { Bar(); Baz() }

----
package qux

import "foo"

type qux struct { foo.Foo; myStuff t }

func Foo(f foo.Foo) foo.Foo {
    return qux{f, &t{}}
}

func (q qux) Bar() {
    // smthg
}

To e.g. "override" f.Bar in package qux and "inherit" f.Baz() unaltered.

huangapple
  • 本文由 发表于 2012年2月24日 02:04:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/9418599.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定