我目前正在学习围棋,我很感激大家对如何最好地减少重复代码数量的见解.

相关部件的文件夹 struct 如下:

.
├── http
│   ├── handlers
│   └── routes
├── models
│   └── dto
├── specifications
└── store
    └── postgres

在我的specifications文件夹中,我有两个‘store’界面:

type TaskStore interface {
    CreateTask(ctx context.Context, input dto.TaskCreate) error
    UpdateTask(ctx context.Context, id int, input dto.TaskUpdate) error
    GetTask(ctx context.Context, id int) (dto.TaskResult, error)
    ListTasks(ctx context.Context) ([]dto.TaskResult, error)
    DeleteTask(ctx context.Context, id int) error
}

type TagStore interface {
    CreateTag(ctx context.Context, input dto.TagCreate) error
    RenameTag(ctx context.Context, id int, input dto.TagUpdate) error
    ListTags(ctx context.Context) ([]dto.TagResult, error)
    GetTag(ctx context.Context, id int) (dto.TagResult, error)
    DeleteTag(ctx context.Context, id int) error
}

store/postgres文件夹包含任务和标记的实现(存储库模式).

我看到的问题是:

在我的handlers文件夹中,我有一个 struct ,它接受一个store 接口的输入:

type TaskHandler struct {
    store specifications.TaskStore
}

func NewTaskHandler(store specifications.TaskStore) TaskHandler {
    return TaskHandler{
        store: store,
    }
}
type TagHandler struct {
    store specifications.TagStore
}

func NewTagHandler(store specifications.TagStore) TagHandler {
    return TagHandler{
        store: store,
    }
}

这些处理程序包含将映射到api路径的方法:

func (h TaskHandler) List() http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        tasks, err := h.store.ListTasks(r.Context())
        if err != nil {
            log.Err(err).Msg("failed to retrieve tasks")
            w.WriteHeader(http.StatusInternalServerError)
            return
        }
        render.JSON(w, r, tasks)
    }
}
func (h TagHandler) List() http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        tags, err := h.store.ListTags(r.Context())
        if err != nil {
            log.Err(err).Msg("failed to retrieve tags")
            w.WriteHeader(http.StatusInternalServerError)
            return
        }
        render.JSON(w, r, tags)
    }
}

您将注意到,每个处理程序上的List方法基本相同,只是每个store 使用的接口不同.

我怎样才能改变这一点,以减少重复的代码?

我最初认为我可以使用泛型来解决这个问题,比如:

type EntityStore[CreateDto any, UpdateDto any, ResultDto any] interface {
    Create(ctx context.Context, input CreateDto) error
    Update(ctx context.Context, id int, input UpdateDto) error
    List(ctx context.Context) ([]ResultDto, error)
    Get(ctx context.Context, id int) (ResultDto, error)
    Delete(ctx context.Context, id int) error
}

但这将意味着将每种类型映射到处理程序中,我认为这不是一个实际的解决方案.

对于如何更好地映射我的DTO和接口,有什么建议吗?

推荐答案

你可以有一个帮手功能

func[T any] ListHandler(name string, lister func(ctx context.Context) ([]T, error)) func(w http.ResponseWriter, r *http.Request) {
    return func(w http.ResponseWriter, r *http.Request) {
        list, err := lister(r.Context())
        if err != nil {
            log.Err(err).Msg("failed to retrieve " + name)
            w.WriteHeader(http.StatusInternalServerError)
            return
        }
        render.JSON(w, r, list)
    }
}

然后你就会有

func (h TaskHandler) List() http.HandlerFunc {
    return ListHandler("tasks", h.store.ListTasks)
}

func (h TagHandler) List() http.HandlerFunc {
    return ListHandler("tags", h.store.ListTags)
}

Go相关问答推荐

带有条件的for循环中缺少RETURN语句

显示GUI时后台处理功能

文件路径.Abs()未在结果中提供子目录

为什么 `go mod` 占用了另一个磁盘上的空间而不是我的 GOPATH?

Golang Fiber Render - 将数据发送到多个布局

这种合并排序的实现有什么问题?

用 fork 替换 Go 依赖:...用于两个不同的模块路径

如何为ANTLR4目标Go调试监听器

如何测试光纤参数

没有任务角色的 AWS CDK ECS 任务定义

golang 中的可变参数函数

获取切片元素的地址是否意味着 Go 中元素的副本?

Golang Gin 绑定请求正文 XML 到 Slice

Golang并发写入多个文件

如何使用 math/big 对 bigInt 进行取模?

如何在循环中旋转图像以便在 golang 中创建 GIF?

将 CSVExport 函数传递给处理程序 Gin

如何扩充 ResponseWriter 的 Header() 返回的 map

Scanner.Buffer - 最大值对自定义拆分没有影响?

Golang LinkedList 删除第一个元素