在非本地包

时间:2018-04-23 05:02:37

标签: go interface network-programming microservices

尝试在Go中创建微服务,我有一个包网络来处理获取字节并转换为特定请求:

package network

type Request interface {
}

type RequestA struct {
a int
}

type RequestB struct {
b string
}

func GetRequestFromBytes(conn net.Conn) Request {
buf := make([]byte, 100)
_, _ := conn.Read(buf)
switch buf[0] {
case 0:
    // convert bytes into RequestA
    requestA = RequestAFromBytes(buf[1:])
    return requestA
case 1:
    requestB = RequestBFromBytes(buf[1:])
    return requestB
}}

现在主要是,我想处理请求。

package main
import (
    "network"
)

(ra *RequestA) Handle() {
// handle here
}

(rb *RequestB) Handle() {
// handle
}

func main() {
    // get conn here
    req = GetRequestFromBytes(conn)
    req.Handle()
}

但是,Golang不允许在主程序包中扩展RequestA / RequestB类。我在网上找到的两个解决方案是类型别名/嵌入,但在两者中看起来我们必须通过在运行时查找特定类型的请求来处理。这需要在网络包和主包中都使用switch / if语句,这两个语句都会复制代码。

最简单的解决方案是将switch语句移动到package main中,在那里我们确定请求的类型然后处理它,但是package main必须处理原始字节,我想要避免。查找请求类型并将其发送到“向上”的责任。应该放在网络包中。

是否有一种简单的惯用Go解决方法?

2 个答案:

答案 0 :(得分:2)

在主程序包中使用type switch

switch req := network.GetRequestFromBytes(conn).(type) {
case *network.RequestA:
   // handle here, req has type *RequestA in this branch
case *network.RequestB:
   // handle here, req has type *RequestB in this branch
default:
   // handle unknown request type
}

这避免了编码主包中协议字节的编码知识。

因为问题指定解析代码在网络包中并且处理程序代码在主包中,所以当添加新请求类型时,不可能避免更新两个包中的代码。即使语言支持问题中提出的功能,在添加新请求类型时仍然需要更新两个包。

答案 1 :(得分:1)

如果在接口中包装返回值,则必须在其上键入switch以根据其在此类场景中的类型对其进行处理。

您接近的方式的替代方法是通过向public class reciever extends AppCompatActivity { Button rec; String tmp=""; ArrayList<String> object; int i=0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_reciever); Intent intent = getIntent(); Bundle args = intent.getBundleExtra("BUNDLE"); object = (ArrayList<String>) args.getSerializable("ARRAYLIST"); rec = (Button) findViewById(R.id.btnrec); rec.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { for (i=0;i<object.size();i++) { tmp+=object.get(i); } Toast.makeText(getApplicationContext(),tmp, Toast.LENGTH_LONG).show(); } }); } 接口添加一个允许您注册处理程序的方法和另一个方法Request来动态地将处理函数插入到Request对象中。您可以通过调用已注册的处理程序来调用处理每条消息。经验表明,您最终会在每个Request类型定义中找到一堆样板文件。

在go中,我可能会采用不同的方法,而不是使用OO类型的设计,您可以在每个请求中键入它自己的“类”。我可能会在下面的代码行中更多地定义网络包。 (警告:这没有经过测试,可能会有一些拼写错误,还有一些需要填写的错误内容)

Handle()

在主程序包中,您将初始化type RequestType byte const ( ARequest RequestType = iota BRequest CRequest EndRequestRange ) type HandlerFunc func([]byte) error type MessageInterface struct { handlers map[RequestType]HandlerFunc // other stuff } func NewMessageInterface() *MessageInterface { m := &MessageInterface { handlers: make(map[RequestType]HandlerFunc), } // Do other stuff return m } func (m *MessageInterface) AddHandler(rt RequestType, h HandlerFunc) error { if rt >= EndRequestRange { // return an error } m.handlers[rt]=h return nil } func (m *MessageInterface) ProcessNextMessage() error { buf := make([]byte, 100) _, _ := conn.Read(buf) h, ok := m.handlers[ReqestType(buff[0])] if !ok { // probably return error } return h(buff[1:]) } ,调用MessageInterface以在初始化期间添加每个处理程序,并在主要处理过程中重复调用AddHandler()

这是一种与你进入的设计方向不同的方法,但对我来说更多的是一种方法。

(请注意 - 我没有做任何事情来确保代码对于并发是安全的,这种方法通常以每个处理程序在其自己的goroutine中调用的方式使用。如果你做了那个更改你可能需要在地图周围进行一些互斥保护以及检查其他一些方面)