亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

如何使這個對象映射在 Go 中更加干燥和可重用?

如何使這個對象映射在 Go 中更加干燥和可重用?

Go
RISEBY 2021-12-07 19:31:37
我在 Go 中創建了一個非關系的對象映射,它非常簡單。我有幾個看起來像這樣的結構:type Message struct {    Id       int64    Message  string    ReplyTo  sql.NullInt64 `db:"reply_to"`    FromId   int64         `db:"from_id"`    ToId     int64         `db:"to_id"`    IsActive bool          `db:"is_active"`    SentTime int64         `db:"sent_time"`    IsViewed bool          `db:"is_viewed"`    Method   string `db:"-"`    AppendTo int64  `db:"-"`}要創建新消息,我只需運行此函數:func New() *Message {    return &Message{        IsActive: true,        SentTime: time.Now().Unix(),        Method:   "new",    }}然后我有一個用于這個結構的 message_crud.go 文件,如下所示:要按唯一列(例如按 id)查找消息,我運行此函數:func ByUnique(column string, value interface{}) (*Message, error) {    query := fmt.Sprintf(`        SELECT *        FROM message        WHERE %s = ?        LIMIT 1;    `, column)    message := &Message{}    err := sql.DB.QueryRowx(query, value).StructScan(message)    if err != nil {        return nil, err    }    return message, nil}為了保存消息(在數據庫中插入或更新),我運行了這個方法:func (this *Message) save() error {    s := ""    if this.Id == 0 {        s = "INSERT INTO message SET %s;"    } else {        s = "UPDATE message SET %s WHERE id=:id;"    }    query := fmt.Sprintf(s, sql.PlaceholderPairs(this))    nstmt, err := sql.DB.PrepareNamed(query)    if err != nil {        return err    }    res, err := nstmt.Exec(*this)    if err != nil {        return err    }    if this.Id == 0 {        lastId, err := res.LastInsertId()        if err != nil {            return err        }        this.Id = lastId    }    return nil}但是每次我創建一個新的結構,例如一個用戶結構時,我必須復制粘貼上面的“crud 部分”并創建一個 user_crud.go 文件并將“消息”替換為“用戶”,并將“消息”替換為“消息”與“用戶”。我重復了很多代碼,它不是很枯燥。有什么我可以做的事情,我可以不為我會重用的東西重復這段代碼嗎?我總是有一個 save() 方法,并且總是有一個 ByUnique() 函數,我可以在其中返回一個結構并按唯一的列進行搜索。在 PHP 中這很容易,因為 PHP 不是靜態類型的。這可以在 Go 中做到嗎?
查看完整描述

3 回答

?
拉風的咖菲貓

TA貢獻1995條經驗 獲得超2個贊

你的ByUnique已經幾乎是通用的了。只需拉出變化的部分(表格和目的地):


func ByUnique(table string, column string, value interface{}, dest interface{}) error {

    query := fmt.Sprintf(`

            SELECT *

            FROM %s

            WHERE %s = ?

            LIMIT 1;

        `, table, column)


    return sql.DB.QueryRowx(query, value).StructScan(dest)

}


func ByUniqueMessage(column string, value interface{}) (*Message, error) {

    message := &Message{}

    if err := ByUnique("message", column, value, &message); err != nil {

        return nil, err

    }

    return message, error

}

你save的非常相似。您只需要按照以下方式創建一個通用的保存功能:


func Save(table string, identifier int64, source interface{}) { ... }

然后在 內部(*Message)save,您只需調用通用Save()函數即可??雌饋砗芎唵?。


旁注:不要this在方法內用作對象的名稱。有關更多信息,請參閱@OneOfOne 的鏈接。不要沉迷于 DRY。它本身并不是一個目標。Go 專注于代碼簡單、清晰和可靠。不要為了避免輸入簡單的錯誤處理行而創建復雜和脆弱的東西。這并不意味著您不應該提取重復的代碼。這只是意味著在 Go 中,通常最好重復一點簡單的代碼,而不是創建復雜的代碼來避免它。


編輯:如果你想Save使用接口來實現,那沒問題。只需創建一個Identifier接口。


type Ider interface {

    Id() int64

    SetId(newId int64)

}


func (msg *Message) Id() int64 {

    return msg.Id

}


func (msg *Message) SetId(newId int64) {

    msg.Id = newId

}


func Save(table string, source Ider) error {

    s := ""

    if source.Id() == 0 {

        s = fmt.Sprintf("INSERT INTO %s SET %%s;", table)

    } else {

        s = fmt.Sprintf("UPDATE %s SET %%s WHERE id=:id;", table)

    }

    query := fmt.Sprintf(s, sql.PlaceholderPairs(source))


    nstmt, err := sql.DB.PrepareNamed(query)

    if err != nil {

        return err

    }


    res, err := nstmt.Exec(source)

    if err != nil {

        return err

    }


    if source.Id() == 0 {

        lastId, err := res.LastInsertId()

        if err != nil {

            return err

        }

        source.SetId(lastId)

    }


    return nil

}


func (msg *Message) save() error {

    return Save("message", msg)

}

可能會因此而爆炸的一個部分是調用Exec. 我不知道你使用的是什么包,Exec如果你傳遞一個接口而不是實際的結構,它可能無法正常工作,但它可能會工作。也就是說,我可能只是傳遞標識符而不是添加這個開銷。


查看完整回答
反對 回復 2021-12-07
?
慕絲7291255

TA貢獻1859條經驗 獲得超6個贊

您可能想要使用 ORM。它們消除了您描述的許多樣板代碼。

這個問題對于“什么是ORM?”

以下是 go 的 ORM 列表:https : //github.com/avelino/awesome-go#orm

我自己從來沒有用過一個,所以我不能推薦任何一個。主要原因是 ORM 需要開發人員的大量控制并引入不可忽略的性能開銷。您需要親眼看看它們是否適合您的用例和/或您是否對這些庫中正在發生的“魔法”感到滿意。


查看完整回答
反對 回復 2021-12-07
?
HUX布斯

TA貢獻1876條經驗 獲得超6個贊

我不建議這樣做,我個人更喜歡明確地掃描結構和創建查詢。


但如果你真的想堅持反思,你可以這樣做:


func ByUnique(obj interface{}, column string, value interface{}) error {

    // ...

    return sql.DB.QueryRowx(query, value).StructScan(obj)

}


// Call with

message := &Message{}

ByUnique(message, ...)

為了您的保存:


type Identifiable interface {

    Id() int64

}


// Implement Identifiable for message, etc.


func Save(obj Identifiable) error {

    // ...

}


// Call with

Save(message)

我使用并推薦給您的方法:


type Redirect struct {

    ID        string

    URL       string

    CreatedAt time.Time

}


func FindByID(db *sql.DB, id string) (*Redirect, error) {

    var redirect Redirect


    err := db.QueryRow(

        `SELECT "id", "url", "created_at" FROM "redirect" WHERE "id" = $1`, id).

        Scan(&redirect.ID, &redirect.URL, &redirect.CreatedAt)


    switch {

    case err == sql.ErrNoRows:

        return nil, nil

    case err != nil:

        return nil, err

    }


    return &redirect, nil

}


func Save(db *sql.DB, redirect *Redirect) error {

    redirect.CreatedAt = time.Now()


    _, err := db.Exec(

        `INSERT INTO "redirect" ("id", "url", "created_at") VALUES ($1, $2, $3)`,

        redirect.ID, redirect.URL, redirect.CreatedAt)


    return err

}

這具有使用類型系統并僅映射它應該實際映射的內容的優點。


查看完整回答
反對 回復 2021-12-07
  • 3 回答
  • 0 關注
  • 234 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號