Как создать метод интерфейса с несколькими типами возврата - PullRequest
0 голосов
/ 11 марта 2020

Это продолжение этого вопроса: Метод интерфейса с несколькими типами возврата

У меня есть две структуры, которые немного отличаются. Один о структуре торговли, другой о структуре передачи. Цель состоит в том, чтобы вычислить количество в конце. Также торговая структура должна реализовывать некоторую специфическую c функцию, не общую для структуры передачи, и наоборот. В конце все они вызывают функцию get() и в конечном итоге возвращают количество (тип string). Я не могу сделать что-то подобное qtyGetService(trade{}).calculateA().get(), где qtyGetService() и get() могут быть вызваны из обеих структур, но calculateA() - это метод только для торговой структуры. Интерфейсы с первого взгляда обещают решить такие проблемы, но я сталкиваюсь с проблемой, описанной в этом вопросе Метод интерфейса с несколькими типами возврата , что метод в интерфейсе должен возвращать указанный c тип. Возвращение interface{} также не будет возможным, так как я не смогу объединить функции, как показано в примере ниже (даже не упоминая об использовании reflect).

package main

import (
    "fmt"
)

type Trade struct {
    q   string
    // many other attributes
}

type Transfer struct {
    q   string
    // many other attributes
}

type TradeService struct {
    q       string
    // some other attributes
}

type TransferService struct {
    q       string
    // some other attributes
}

type GetQty struct {
    q       string
    // some other attributes
}

func qtyGetTradeService(str *Trade) *TradeService {
    // some processing on the initial struct
    return &TradeService{
        q: str.q + " TradeService ",
    }
}

func qtyGetTransferService(str *Transfer) *TransferService {
    // some processing on the initial struct
    return &TransferService{
        q: str.q + " TransferService ",
    }
}

func (ts *TradeService) calculateA() *GetQty {
    // some processing on ts
    return &GetQty{
        q: ts.q + " CalculateA ",
    }
}

func (ts *TradeService) calculateB() *GetQty {
    // some processing on ts
    return &GetQty{
        q: ts.q + " CalculateB ",
    }
}

func (ts *TransferService) calculateC() *GetQty {
    // some processing on ts
    return &GetQty{
        q: ts.q + " CalculateC ",
    }
}

func (gq *GetQty) get() string{
    // some processing on gq common to both trade and transfer
    return gq.q + " CommonGet "
}



func main() {
    // this works just fine
    fmt.Println(qtyGetTradeService(&Trade{q: "10"}).calculateA().get())
    fmt.Println(qtyGetTransferService(&Transfer{q: "10"}).calculateC().get())

    // But this would be "better" to do something like this:
    fmt.Println(qtyGetService(&Trade{q: "10"}).calculateA().get())
    fmt.Println(qtyGetService(&Transfer{q: "10"}).calculateC().get())
}

Ссылка на игровую площадку: https://play.golang.org/p/SBCs_O9SL0k

Ответы [ 2 ]

1 голос
/ 11 марта 2020

Спасибо за перепост вопроса. В комментариях к другому вопросу было бы сложно привести пример кода.

Я вижу здесь 2 варианта:

Вариант 1:

Создать функцию calculate на всех структурах с одинаковой подписью. В вашем примере нет входных параметров, только возвращаемое значение. Если это не так, все становится немного сложнее, и вариант 2 мог бы быть более подходящим.

Примечание: функции сцепления усложняют использование интерфейсов в Go. Возможно, вы захотите избавиться от этого.

Пример:

type qty interface{
    calculate() qty // chaining can be problematic. best no return value here.
    get() string // or whatever type get should return
}

func (ts *TradeService) calculate() qty {
    ts.calculateA()
    return ts
}

func (ts *TransferService) calculate() qty {
    ts.calculateC()
    return ts
}

func main() {
    // Now this should 
    fmt.Println(qtyGetService(&Trade{q: "10"}).calculate().get())
    fmt.Println(qtyGetTransferService(&Transfer{q: "10"}).calculate().get())

    // or without chaining (if the return value is removed from `calculate`):
    printVal(qtyGetService(&Trade{q: "10"}))
    printVal(qtyGetTransferService(&Transfer{q: "10"}))
}

func printVal(service qty) {
    service.calculate()
    fmt.Println(service.get())
}

Иногда имеет смысл реализовать функцию в структуре, даже если она не требуется для удовлетворения интерфейса. Если существует сервис, который не нужно вычислять перед вызовом get, просто создайте эту функцию:

func (ts *SomeService) calculate() {}

Теперь ее можно использовать как qty интерфейс.

Вариант 2 :

Может также случиться так, что случаи не настолько однородны, и их сложнее объединить в одном интерфейсе.

Затем вы можете работать с несколькими интерфейсами и приводить структуру, чтобы проверить, реализует ли она интерфейс. Только тогда вызовите метод. Это более или менее похоже на проверку, имеет ли структура определенный метод или нет.

type calculatorA interface {
    calculateA()
}

type calculatorB interface {
    calculateB()
}

type getter interface {
    get()
}

func main() {
    service := qtyGetService(&Trade{q: "10"})
    if ok, s := service.(calculatorA); ok {
        s.calculateA()
    }
    if ok, s := service.(calculatorB); ok {
        s.calculateB()
    }
    var val string
    if ok, s := service.(getter); ok {
        val = s.get()
    }
    fmt.Println(val)
}

Надеюсь, это применимо к вашему случаю и дает вам некоторые идеи.

0 голосов
/ 12 марта 2020

Если вы добавите GetService() рецептор к Trade и Transfer следующим образом:


func (t Trade) GetService() *TradeService {
    return &TradeService{
        q: t.q + " TradeService ",
    }
}

func (t Transfer) GetService() *TransferService {
    return &TransferService{
        q: t.q + " TransferService ",
    }
}

Вы сможете использовать его следующим образом:

    fmt.Println(Trade{q: "10"}.GetService().calculateA().get())
    fmt.Println(Transfer{q: "10"}.GetService().calculateC().get())

Кажется, что вы пытаетесь выполнить sh.

Определение GetService() в интерфейсе не будет работать, потому что тип возвращаемого значения в обеих реализациях отличается.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...