Go 如何收缩此代码并合并if和else条件

Go 如何收缩此代码并合并if和else条件,go,refactoring,go-fiber,sqlc,Go,Refactoring,Go Fiber,Sqlc,if和else条件遵循相同的代码过程,只是函数和结构不同,如何合并它们,使代码更小。我的意思是: if category == 0 { rows, err := h.Repo.GetAllLatestProducts(c.Context()) if err != nil { return c.Status(fiber.StatusInternalServerError).SendString(err.Error())

if和else条件遵循相同的代码过程,只是函数和结构不同,如何合并它们,使代码更小。我的意思是:

    if category == 0 {
        rows, err := h.Repo.GetAllLatestProducts(c.Context())
        if err != nil {
            return c.Status(fiber.StatusInternalServerError).SendString(err.Error())
        }
        result := make([]interface{}, len(rows))
        for i, product := range rows {
            result[i] = dbrow.ConvertToAllLatestProducts(product)
        }
    } else {
        rows, err := h.Repo.GetLatestProductsByCategory(c.Context(), int16(category))
        if err != nil {
            return c.Status(fiber.StatusInternalServerError).SendString(err.Error())
        }
        result := make([]interface{}, len(rows))
        for i, product := range rows {
            result[i] = dbrow.ConvertToCategoryLatestProducts(product)
        }
    }

无法触摸h.Repo.GetAllLatestProducts或h.Repo.GetLatestProductsByCategory,因为它们是外部函数。类型安全也很重要

可以有多个函数,如featuredproducts、newproducts,我想创建一个通用函数,根据动态选择的sql函数将产品作为json返回


你有问题了,几十个具有相同代码结构的函数都是不好的,至少对我来说,它的可读性与否无关紧要,它只是毫无意义的复制/粘贴, 大多数情况下,复制/粘贴仅与函数名/SQLC生成的函数名和结构名发生更改,其余代码流相同

SQLC基于SQL查询生成自动代码,现在编写重复代码只是为了将结果转换为JSON并返回给客户端,这是浪费时间。 可能有几十个SQL函数返回最新产品、特色产品、类别中的产品、愿望列表产品等等

网站只了解产品结构,但SQLC返回不同的结构,因此没有单一的dbResult类型。无论如何,映射不是一件大事,使用反射我们可以检查同名字段,并在映射函数中将SQL.NullString转换为string等

对我来说,真正的问题是if/else语句。您已经在不同的函数中移动了代码,但对我来说,在这种情况下,它没有意义。因为web处理程序无论如何都必须进行检查 请求是否有效,是否定义了类别,然后检查类别是否为0,然后调用不同的函数,得到结果并返回给客户端。 对于单个函数,它可能看起来更好,但对于实际生产,它会使事情变得更糟,并且不再是单个函数和if/else块,现在每个API有3个函数

我希望将SQLC结果映射到路由处理程序。代码流总是一样的, 只有函数名和结构名更改。如何使其动态化,以便在我的http处理程序中,我可以简单地编写:

    var rows []postgres.GetAllLatestProductsRow
    var rows []postgres.GetLatestProductsByCategoryRow
    if category == 0 {
        rows, err = h.Repo.GetAllLatestProducts(c.Context())
    } else {
        rows, err = h.Repo.GetLatestProductsByCategory(c.Context(), int16(category))
    }
    //Rest of the code ...
然后根据c.Query中的category值(“category”),SQLCResult将自动调用GetAllFeaturedProducts或GetFeaturedProductsByCategory。 类似于回调函数,但函数签名不同,这是一个问题

return SQLCResult(c.Query("category"), GetAllFeaturedProducts, GetFeaturedProductsByCategory)
映射函数不是必需的,因为在SQLCResult中,我们可以执行以下操作:

func (q *Queries) GetAllFeaturedProducts(ctx context.Context) ([]GetAllFeaturedProductsRow, error)
func (q *Queries) GetFeaturedProductsByCategory(ctx context.Context, idCategory int16)
这将创建字段名和索引的映射,并传递dbresult行,它将使用反射将其转换为产品结构并返回相同的结果,即在修改其字段后返回第一个参数作为结果

我仍在寻找如何编写SQLCResult函数,将SQLC函数名作为输入,然后返回结果,或者将产品{}结构本身放入SQLCResult函数中,使其更通用,如:

MapDBStructToRestAPIStruct(&Product{}, &row, MapFields(&row))
其中,SQLCResult将基于布尔条件调用GetAllFeaturedProducts或GetFeaturedProductsByCategory 创建并将函数结果映射到作为第一个参数传递的结构,然后返回该结构

或者这是最终的目标:

var result := SQLCResult(&Product{}, c.Query("category") == 0, GetAllFeaturedProducts, GetFeaturedProductsByCategory)
return c.Status(fiber.StatusOK).JSON(result)

<> P>有很多要考虑的,代码确实没有问题,但它可能难以维护,它可能更难,与预期的情况类似,它规模很差,并且在长期内,可能会出现更多的规则,使它很容易成为意大利面。 我们想要的是关注点的分离、可重用的相似部分和清晰性。我们不需要太多的复杂

考虑到我们不能更改存储库API——这可能是一种直接的方法——我们必须包装存储库,更像是一个装饰器,或者用围棋术语来说,是隐藏

func (h *Handlers) GetLatestProducts(c *fiber.Ctx) error {
  if c.Query("category") == 0
    return c.JSON(SQLCResult(&Product{}, GetAllLatestProducts)
  else
    return c.JSON(SQLCResult(&Product{}, GetLatestProductsByCategory, c.Query("category"))
}

func (h *Handlers) GetFeaturedProducts(c *fiber.Ctx) error {
  if c.Query("category") == 0
    return c.JSON(SQLCResult(&Product{}, GetAllFeaturedProducts)
  else
    return c.JSON(SQLCResult(&Product{}, GetFeaturedProductsByCategory, c.Query("category"))
}
这使我们能够更好地封装每个调用的兴趣

// ProductHandler shadows product repository
type ProductHandler struct {
    *Repo
}
这样,我们就可以通过自己的方法来委托检索或不检索类别的职责,并自动将结果集包装到自己的类型中,相应地分离映射职责

func (ph ProductHandler) GetLatestProductsByCategory(ctx context.Context, cat int) ([]interface{}, error) {
    if cat == 0 {
        return nil, nil
    }

    l, err := ph.Repo.GetLatestProductsByCategory(ctx, cat)

    return ResultSet(&ProductsByCategory{}, l), err
}

func (ph ProductHandler) GetAllLatestProducts(ctx context.Context) ([]interface{}, error) {
    l, err := ph.Repo.GetAllLatestProducts(ctx)

    return ResultSet(&Products{}, l), err
}
为了实现db结果集到特定类型的转换,我们必须公开一个公共接口,因此任何实现该接口的类型都可以转换(translate、map、hydrome都是同义的)

现在,每个类型都可以有自己的从->到->的转换

type Transformer interface {
    Transform(interface{}) interface{}
}
有一个功能可以帮助我们转换数据列表,我们可以随时重用

func (p Products) Transform(i interface{}) interface{} {
    v, _ := i.(*dbresult)

    p.Name = v.RawName
    p.Id = v.RawId

    return p
}

func (p ProductsByCategory) Transform(i interface{}) interface{} {
    v, _ := i.(*dbresult)

    p.Category = v.CategoryName

    return p
}
到目前为止,我们的实现可以简单地像这样,并且所有这些部分都可以重用

func ResultSet(t Transformer, d []interface{}) []interface{} {
    result := make([]interface{}, len(d))
    for i, p := range d {
        result[i] = t.Transform(p)
    }

    return result
}
虽然代码使用
接口{}
这并没有什么不好的地方,但最终您的数据已经像那样来自数据库,我们只是将它们传递过去。如果做得不好,断言它们的类型可能代价高昂,在调用json封送处理之前,这里没有一个是这样的

你可以找到一个 为了支持这些情况,有一个可能的db响应调用的模拟。
尝试为
类别
指定一个值,看看会发生什么

它是可读的,并且清楚它的书写方式。为什么要更改它?嗨,Burak,有两件事,首先是代码重复,if/else中还有很多其他代码可以对结果起作用。第二,正如前面提到的,有很多类似的函数,比如featuredproduct,newproduct,我想最后把所有函数合并成一些通用的函数。忽略dbrow.ConvertToAllLatestProducts中的部分。关于GetAllLatestProductsRow使变量类型动态的部分是问题。结果是[]接口{}。convert函数什么都不是,只是将一个结构映射到另一个结构。像结构a{b:int}到结构c{b:int}。
func ResultSet(t Transformer, d []interface{}) []interface{} {
    result := make([]interface{}, len(d))
    for i, p := range d {
        result[i] = t.Transform(p)
    }

    return result
}
func main() {
    var category int
    // h.Repo
    repo := Repo{}
    ph := ProductHandler{&repo}

    pcat, _ := ph.GetLatestProductsByCategory(context.Background(), category)
    products, _ := ph.GetAllLatestProducts(context.Background())
    products = append(pcat, products...)

    for _, product := range products {
        fmt.Printf("%v\n", product)
    }
}