измените базовую структуру для предоставленного интерфейса Golang, в котором базовая структура не экспортируется

# #go #pointers #interface

Вопрос:

У меня есть сценарий, в котором я должен написать метод, в котором я изменяю базовую структуру переданного интерфейса, я понял, что могу передать указатель на интерфейс, а затем использовать указатель, чтобы получить базовую структуру и манипулировать ею, но из того, что я узнал, использование указателей на интерфейс почти никогда не требуется. Может ли кто-нибудь предложить какой-либо лучший способ достижения требуемой функциональности ниже?

 package main

import (
    "fmt"
)

type person struct {
    name string
    age  int
}

type iperson interface {
    walk()
    changeName(string) *person
}

func (p person) walk() {

    fmt.Println("tip tap toe")
}

func (p person) changeName(name string) *person {

    return amp;person{name: name, age: p.age}
}

func modifyStr(s *string) {

    *s = "world"

}

func modifyStruct(p *person) {

    *p = person{name: "john", age: 10}

}

func modifyInterface(i iperson) {

    //i.walk()
    p := i.(*person)
    *p = person{name: "diff name", age: 111}

}

func modifyInterfaceWithUnexportedStruct(target interface{}) {

    // We can do so by type assertion on pointer to interface but this is not recommended
    // this works but is there any other way possible? i.e without having access to underlying struct but still manipulating it?

    p := target.(*iperson)
    *p = *(*p).changeName("some random name")

}

func main() {

    //modifying a string in a function call

    a := "hello"
    modifyStr(amp;a)
    fmt.Println(a)

    //modify a struct in a function call

    b := person{name: "sam", age: 2}
    modifyStruct(amp;b)
    fmt.Println(b)

    //modify a interface in a function call using the underlying struct

    c := person{name: "tom", age: 2}
    modifyInterface(amp;c)
    fmt.Println(c)

    //modify a passed interface in a function call without using the underlying struct

    d := iperson(person{name: "abc", age: 21})
    fmt.Println(d)
    modifyInterfaceWithUnexportedStruct(amp;d)
    fmt.Println(d)

}

 

Комментарии:

1. Что вы подразумеваете под личным? Вы имеете в виду неучтенный? Если это так, то вы НЕ можете изменять такую структуру вне пакета, в котором она объявлена. Если вы хотите изменить его только в рамках его собственного пакета, то то, что вы подразумеваете под «частным» и как это связано с вашим вопросом, становится еще менее ясным.

2. Этот вопрос не помешало бы прояснить… Независимо от того, экспортирована она или нет, вы не можете изменять структуру вне пакета, в котором она определена. Указатель на интерфейс почти никогда не бывает правильным или необходимым. Вы не можете манипулировать базовой структурой без утверждения типа, и если вы считаете, что вам нужно изменить базовую структуру, это указывает на существенную проблему проектирования: если вы правильно используете интерфейсы, вам обычно не нужен доступ к базовой структуре.

3. @mkopriva Да, я имею в виду незарегистрированный. Нет, я не хочу вносить изменения в свой собственный пакет.

4. @Адриан, я понимаю, спасибо.

Ответ №1:

Ваш интерфейс уже предоставляет changeName метод, но его реализация не изменяет имя получателя person , он возвращает новое person с тем же возрастом, что и получатель, и переданное имя.

Идея интерфейса заключается в том, что коду, который взаимодействует с интерфейсом, не нужно знать особенности реализации. Но если changeName возвращает a *person , вы уже заставляете вызывающего абонента знать детали реализации.

Так почему бы не реализовать изменение имени таким образом ?

 func (p *person) changeName(n string) {
  p.name = n
}
 

Комментарии:

1. Спасибо Дэниелу за ваш ответ, на самом деле я не контролирую реализацию changeName в моем случае, но реализация этой внешней библиотеки, которую я использую, возвращает нового человека того же возраста, что и получатель, и переданное имя.