【问题标题】:How do I refactor this code with Interfaces, and if so, should I? [closed]我如何用接口重构这段代码,如果是这样,我应该吗? [关闭]
【发布时间】:2021-05-24 04:17:02
【问题描述】:

我正在编写一个软件,它使用来自不同服务的 API,方法相同但执行不同。我想知道代码是否可以在接口中组织,或者我只是继续这样工作:

type endPoint struct{
    serverName string
}

func (this *endPoint) getLastDateURL () string{
    if this.Name ="A"{
        return "api.someAWebsite.com/getLastDate"
    }
    if this.Name ="B"{
        return "api.someBWebsite.com/getLastDate"
    }
    return ""
}

func (this *endPoint) processData () output{
    if this.Name ="A"{
        //process in some way
        return
    }
    if this.Name ="B"{
                //process in some different way
                return
    }
    return
}

我正在考虑的界面替代方案是这样的:

struct endPoint interface{
    getLastDateURL()
    processData()
}

...
Do each method for each API provider
How would I use it then?

我的最终目标是拥有可维护且干净的代码。老实说,我很讨厌我必须为每个端点编写相同的方法来实现接口,但也许我还没有那么清楚的接口概念,或者在这种情况下再次使用接口几乎没有优势,可维护和干净的代码的最终目标。

谢谢。

【问题讨论】:

  • "真的很讨厌我必须为每个端点编写相同的方法来实现接口"。您提出的解决方案使用某种开关结构来执行特定代码。写func (e *typeX) method() 真的比if e.Name == "X" 这么麻烦吗?这就是我们在语言中具有多态性的原因:因此,您无需编写大的 switch 结构来为不同的值运行不同的代码,并且您可以将每组连贯的代码紧密地收集在一起,而不是将所有内容混杂在“主人”中过程。

标签: go interface coding-style


【解决方案1】:
// "struct endPoint interface" is invalid, and signatures are missing their return types:
type endPoint interface{
    getLastDateURL() string
    processData() output
}

那我该怎么用呢?

我不能给出一个完整的解决方案,因为你没有展示你如何使用你当前的代码,但一般来说你会有:

  • 实例化一个实现的东西——无论在当前实现中设置Name,都将创建一个实现endPoint的具体类型的实例
  • 调用getLastDateURL 和/或processData 的东西 - 不是接收*endPoint struct,而是接收endPoint 接口值,并且那么就不会关心底层实现是什么;它只会调用方法,就像现在一样

我的最终目标是拥有可维护且干净的代码。

这可能是实现这一目标的好方法,但这取决于问题中未显示的上下文。

真的很讨厌我必须为每个端点编写相同的方法来实现接口

已经为每个端点编写了一个实现,你只是在用相同的方法编写它们。您必须编写的代码量几乎相同,但生成的代码更简洁、更有条理——例如,每个具体实现都可以在自己的文件中,而不必全部使用相同的方法。对于许多/复杂的提供程序,如果您需要更改或添加提供程序,您当前的解决方案将变得越来越难以导航。

【讨论】:

    【解决方案2】:

    使用double dispatch pattern

    在 endPoint 上为它支持的每个类定义一个类型化的 func。

    定义一个接口,该接口具有一个接受端点对象的函数。

    让所有想要使用 endPoint 的类实现该接口的 func 并将自己传递给类型化的 endPoint 方法。

    要调用它,调用传递endPoint的接口方法。

    恕我直言,你应该这样做。

    【讨论】:

    • 为什么要使用双重调度和回调函数来过度复杂化呢?与更直观地使用接口和每个端点的实现相比,这似乎没有任何优势。
    • @Adrian 双重调度仅用于这种情况,即您有一个必须神奇维护的巨大 ifs 块,实际上代码和错误更少,因为 ifs 块在支持强类型和编译器完成所有工作。
    • “ifs 块被淘汰,有利于强类型,编译器完成所有工作”正是你使用接口得到的;添加带有回调的双重分派似乎没有必要、不习惯且过于复杂。
    • @Adrian 不是这样。您的方法要求每个参数对象都知道如何“以某种方式处理”,这与 OP 的范例有很大不同。双重调度将每个不同动作的责任和维护留在被调用的代码中,而不是参数的代码中。
    • 他们现在拥有的是一种包含所有实现的方法。这可以很容易地拆分为每个端点的类型,每个端点都只有该端点的实现。这是唯一的变化。最终结果是完全没有改变 OP 的范式;他们仍然以相同的方式调用相同的方法。除了将*endPoint 替换为endPoint 之外,调用代码可能完全不变。双重调度和回调在这里解决不了任何问题,只会增加复杂性。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多