【问题标题】:How to make an object of a class and have getters for struct?如何制作一个类的对象并为结构设置吸气剂?
【发布时间】:2022-01-01 02:52:20
【问题描述】:

我最近开始与golang 合作,因此很难理解如何实现相同的目标,而我可以在JavaC# 中轻松做到这一点。我正在尝试创建configmanager 类的对象,当第一次调用configmanager 类时,它应该初始化我的所有配置并将其存储在内存中的某个结构对象中。然后我应该可以访问 configmanager 对象,并且应该能够使用一些 getter 从我的主函数访问所有这些配置?

下面是我的configmanager 上课。现在很简单,让它更容易理解。

package config

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "github.com/david/internal/utilities"
)

type ClientMetrics struct {
    ClientMetrics []ClientMetric `json:"clientMetrics"`
}
type CustomerData struct {
    Process []string `json:"Process"`
    Mat     []string `json:"Mat"`
}
type ClientMetric struct {
    ClientID     int          `json:"clientId"`
    CustomerData CustomerData `json:"customerData,omitempty"`
    LegCustomer  []int        `json:"legCustomer"`
    OtherIds     []int        `json:"otherIds,omitempty"`
    CatID        int          `json:"catId,omitempty"`
}

func Init(root string) (err error) {
    files, err := utilities.FindFiles(root, "process-data.json")
    if err != nil {
        return fmt.Errorf("cannot find process-data.json file: %v", err)
    }
    for _, file := range files {
        body, err := ioutil.ReadFile(file)
        if err != nil {
            return fmt.Errorf("unable to read file: %v", err)
        }
        var auto ClientMetrics
        json.Unmarshal(body, &auto)

    }

    return nil
}

这是我在我的主要功能中使用它 - 这只是基本代码,只是为了演示我在做什么,但这不是 production 准备好的代码。

package main

import (
    "github.com/david/internal/config"
)

func main() {
    root := "/home/Configurations"
    config.Init(root)

  //
}

在我上面的Init 函数中,如果process-data.json 文件在磁​​盘上,我会找到它,然后通过将其反序列化为ClientMetrics 对象将其加载到内存中。如上所示,一切正常。

问题陈述

由于我来自JavaC# 背景,所以我的困惑是如何创建configmanager 类的对象,以及我应该如何在第一次调用configmanager 时初始化所有配置并且还可以使用一些 getter 访问 ClientMetrics 结构。在 Java 和 C# 中,我曾经使用构造函数来初始化所有这些东西,然后使用一些 getter 来访问配置。我应该如何在 golang 中做同样的事情。

我的主要困惑是我们在 go 中是否有构造函数,我应该如何让 getter 访问我的 main 函数中的结构对象?我只是在 go 中寻找更好的设计,我应该如何在 golang 中执行上述代码?

更新

我想我无法正确解释。我在一个文件夹中有 X 个(现在假设 5 个)不同的 json 文件,每个 json 文件都需要有自己的结构,因为它们是完全不同的 json 文件。我的configmanager 文件将负责将所有这 5 个 json 文件加载到它们自己的结构中,然后我应该能够从configmanager 的对象访问所有这 5 个structs 及其字段。这一切都应该发生在configmanager类第一次被调用时的初始化过程中。

这只是一个示例,我在它们自己的相应文件夹(folderX) 中有一堆 json 文件。我有如下三类文件(clientMap-*.json, dataMap-*.json, process-data.json)

Products
├── folder1
│   ├── clientMap-123.json
│   ├── clientMap-987.json
│   ├── clientMap-7161.json
├── folder2
│   ├── dataMap-18271.json
│   ├── dataMap-12921.json
│   ├── dataMap-98121.json
└── folder3
    ├── process-data.json

现在我需要在它们自己的结构中读取所有这些文件(clientMap-*.json, dataMap-*.json, process-data.json)。而且我应该能够在解组后使用configmanager 类来获取相应的结构及其字段。

例如:读取clientMap-*.json文件。

files, err := utilities.FindFiles(root, "clientMap-*.json")
// load all clientMap-*.json files in its own struct after unmarshalling

dataMap-*.json 文件也是如此

files, err := utilities.FindFiles(root, "dataMap-*.json")
// load all dataMap-*.json files in its own struct after unmarshalling

也适用于process-data.json 文件

files, err := utilities.FindFiles(root, "process-data.json")
// load all process-data.json files in its own struct after unmarshalling

我的FindFiles 方法将找到所有文件,即使是上面的regexfiles 变量是一个数组,其中包含与特定模式匹配的所有文件的列表。现在我可以为我的配置创建ConfigManager 结构类型来保存所有其他structs,但我正在尝试找到一个易于扩展的解决方案,以便将来如果我添加任何其他json文件类别,它应该能够轻松扩展。解决这个问题的正确方法是什么?

【问题讨论】:

  • 是的,这就是我在阅读大量 Go 教程后之前的想法,但我的用例略有不同,因为我可以说 10 个不同的配置(文件),并且每个配置都可以有自己的结构,因为这是一个不同的配置,所以我需要为它们设置单独的结构,所以我该如何在你的建议中做到这一点?此外,即使我们可以做到这一点,那么 unmarshal 将如何工作? @CeriseLimón
  • 你将如何在 Java 或 C# 中做到这一点?你是说还有一个类,但是有重载的构造函数?如果是这样,在 Go 中可以使用一个结构和不同的函数来完成(每个函数返回一个指针/引用,但只是以不同的方式构建结构)。
  • 听起来 ConfigManager 应该有一个切片,其中包含从 JSON 编组的所有对象。需要切片(或地图),因为您不知道可以有多少这样的文件。 VonC 的答案中显示的组成也可能对您有所帮助。
  • 在开始时,我将假设 5 个 json 文件,它们需要解组到自己的结构中,所以基本上总共 5 个结构,ConfigManager 应该负责做所有这些并提供返回每个的机制那些结构和他们的领域。您能否提供一个切片示例,如果需要可以轻松扩展?我也已经回复了 VonC 的回答。问题是我无法修改我的 json 以向其中添加类型。让我知道这是否仍然可以针对我的用例进行扩展。 @Sangam
  • @CeriseLimón 我已更新我的问题以添加更多详细信息。现在让我知道这是否有意义。

标签: go struct


【解决方案1】:

这个问题很难回答,因为你有点忘了以明确的方式问一个问题,所以我将根据你写的内容提取一个问题来开始我的回答。我相信我们可以这样做:

问题陈述

[...] 我的困惑是如何创建configmanager 类的对象,以及当我第一次调用configmanager 并且还可以使用ClientMetrics 结构访问时,我应该如何初始化所有配置一些吸气剂

我相信这里真正的问题是“我如何将读取和解组文件的关注与存储结果以供我的程序使用的关注分开?”。

通过将事物分解为多个函数/方法来分离关注点是很常见的,您已经在某种程度上做到了这一点。然而,存储严格来说是类型的问题,所以我们需要一种能够保存结果的类型。我将利用这个机会从类型名称中省略 Manager 这个词,因为它所做的只是提供无用的抽象。这种类型不管理配置。它配置,因为它包含所有的配置。

type Config struct {
    ClientMapConfigs   []ClientMapConfig
    DataMapConfigs     []DataMapConfig
    ProcessDataConfigs []ProcessDataConfig
}

请注意,这些字段以大写字母开头,将它们设为公开。这表明那里可能有废话,因为没有任何东西可以防止写入,这与我们从文件中读取这些数据的事实一致。正确的程序必须在使用之前验证这些数据。然后,您可以在变量名称中传达已验证数据的有效性。

func main() {
    validConfig := getValidConfig("path/to/dir")
    // ...
}

func getValidConfig(configDirectoryPath string) *Config {
    config, err := NewConfigFromConfigDirectory(configDirectoryPath)
    if err != nil {
        log.Printf("Failed to read config from dir '%s': %v\n", configDirectoryPath, err)
        os.Exit(1)
    }
    if err = ValidateConfig(config); err != nil {
        log.Printf("Config from dir '%s' failed to validate: %v\n", configDirectoryPath, err)
        os.Exit(1)
    }
}

func NewConfigFromConfigDirectory(configDirectoryPath string) *Config {
    // <- read individual configs and add to slices here

    return &Config{ // This is the closest to a "constructor" that Go has.
        ClientMapConfigs: clientMapConfigs,
        DataMapConfigs: dataMapConfigs,
        ProcessDataConfigs: processDataConfigs,
    }
}

请注意,验证和读取配置的函数不需要接收器,即成为结构的方法。它们可以作为独立函数使用,直到您的需求发生变化,需要为任一逻辑引入状态性。

我还使用退出代码1 来处理这里的错误情况,因为当程序因恐慌而终止时,Golang 使用代码2。前者可以被认为是环境问题,而后者则表明程序本身存在问题。这是一个有用的区别,并且符合 ExceptionRuntimeException 的语义,您可能从 Java 中知道。

【讨论】:

  • 感谢您的帮助!是的,我在写这个问题时有点搞砸了,但我很高兴你明白我想要什么。这看起来不错,但我的主要困惑也在这里// &lt;- read individual configs and add to slices here。我怎样才能正确有效地做到这一点?我的意思是我可以为每个文件类别设置单独的方法并读取它们,但是如果我添加更多文件类别,那么它会变得拥挤并且看起来很丑。有没有更好的方法来处理这个?如果你能提供一个例子,那将有很大的帮助。
  • 我现在明白你的问题了。唉,你不能比你想的更好,因为 Go 还没有泛型。您提供给Unmarshal() 的指针必须是指向您最终想要的具体类型的指针,因此您必须为您拥有的每种类型的文件编写实现它所需的代码行。在 Java 中这样的方法会简单地接受Object,但在 Go 中没有继承,只有嵌入,所以无法做到。
  • 我明白了。现在明白了。这对我来说很清楚。另外,如果您不介意可以添加一些关于如何将单个配置添加到切片的虚拟示例吗? // &lt;- read individual configs and add to slices here 这将帮助我理解我的想法是正确的。
  • 您需要append() 内置函数。它包含在围棋之旅中,这是一个很好的练习课程,您应该从头到尾进行:go.dev/tour/moretypes/15 巡回赛开始于go.dev/tour/welcome/1
  • 我在这里有些困惑。 The first parameter s of append is a slice of type T, and the rest are T values to append to the slice. 在我的情况下,T 这里是什么?这就是我感到困惑的地方。如果您可以添加一些示例,那么它可以对我的用例有很大帮助。
【解决方案2】:

我可以说 10 个不同的配置(文件),每个配置都可以有自己的结构,因为它是不同的配置,所以我需要为它们有单独的结构

这看起来像是动态 JSON 结构解组,这是由 John Asmuth 在 2015 年在 decoding with mixed structures 中提出的

您可以运行following example here

type Dog struct {
  Name     string
  Frisbees int
}
type Cat struct {
  Name    string
  Strings int
}
type RawAnimal struct {
  Type string
  Cat
  Dog
}
type Animal RawAnimal
func (a *Animal) UnmarshalJSON(data []byte) error {
  if err := json.Unmarshal(data, (*RawAnimal)(a)); err != nil {
    return err
  }
  switch a.Type {
  case "cat":
    return json.Unmarshal(data, &a.Cat)
  case "dog":
    return json.Unmarshal(data, &a.Dog)
  }
  return fmt.Errorf("unknown type %q", a.Type)
}

从那里,您的 ConfigManager 将实例化正确的 Config 结构,具体取决于读取的原始 JSON。

【讨论】:

  • 注意:关于直接结构实例化与 Go 中的构造函数:pauldigian.hashnode.dev/…
  • 抱歉我的回复晚了。非常感谢你的帮助。我在最后更新了我的问题,以准确解释我在寻找什么。我的ConfigManager 应该将所有 5 个不同的配置实例化到它自己的结构中,并且应该提供一种机制来访问所有这些结构。在您的示例中,您认为我有一堆不同类型的类似 json 配置,但事实并非如此。在我的例子中,ConfigManager 负责通过使用已经定义的 fileName 或 filePattern 从磁盘读取它来将所有 X 配置实例化到它自己的结构中。让我知道现在是否有意义。
  • 就像在任何其他编程语言中一样,我将为每个唯一的 json 文件类别设置 5 个不同的类,并将在将它们自己的 json 反序列化到它们中后填充所有这 5 个类。现在我的ConfigManager 类将提供我可以从 getter 返回的所有这 5 个类的对象。
  • 基本上每个结构在磁盘上都有自己的 json 文件(或文件)。因为它们是完全不同的 json 文件。我的ConfigManager 类将负责将所有这些 json 文件加载到它自己的结构中,并且应该提供访问每个结构及其字段的机制。
  • @user1950349 好的。有道理。尽管如此,作为“一种易于扩展的解决方案,以便将来如果我添加任何其他 json 文件”,我喜欢我的提议,即一个 JSON 解组器能够从不同的 JSON 文件生成不同的类。
【解决方案3】:

我认为问题在于您从 Java/C# 的角度看待 Go,因此难以理解这些特性。如果你有时间,那么我建议你在开始编码之前阅读一些 Go 教程或书籍(这本很不错:https://www.amazon.com/Programming-Language-Addison-Wesley-Professional-Computing/dp/0134190440

要直接回答您的问题,您需要做的是创建一个函数,该函数返回指向结构对象的指针(请参见此处的简单示例:https://gobyexample.com/structs

以ClientMetric为例:

func NewClientMetric(ClientID int, CustomerData CustomerData, LegCustomer []int, OtherIds []int, CatID int) (*ClientMetric, error) {

//validate your inputs
//in case of errors, create and error message in the variable err and then: return nil, err
//other code which would go into a typical constructor would go here

return &ClientMetric{ClientID,CustomerData, LegCustomer, OtherIds, CatID}, nil
}

在这种情况下,函数NewClientMetric构造函数,它返回一个指向新创建对象的指针/引用。它还返回一个错误对象,这和说构造函数抛出异常是一样的。正如您需要在 Java 中使用 try/catch 一样,您需要检查以处理此函数返回的 err 变量。

您需要为每种类型创建类似的函数。

关于 getter 和 setter,一般来说,在 Go 中应该避免。您可以直接访问结构的属性。仅当您要在返回属性之前对属性执行某些操作时,函数(如 getter)才有用。像这样的:

type Customer struct {
FirstName string
LastName string
}

func (this *Customer) GetFullName() string {
return this.FirstName + " " + this.LastName
}

然后可以像这样访问这些:

var customer *Customer
customer = &Customer{"John","Smith")
fmt.Println(customer.FirstName)
fmt.Println(customer.LastName)
fmt.Println(customer.GetFullName())

请注意,以大写字母开头的属性、函数和方法是公共的,其他的都是私有的。如果FirstName 写成firstName,则在声明它的包之外将无法访问它。

请注意,如果指针为 null/nil,我不会检查错误,但始终建议这样做。

【讨论】:

  • 抱歉我的回复晚了。非常感谢你的帮助。我在最后更新了我的问题,以准确解释我在寻找什么。我的ConfigManager 应该将所有 5 个不同的配置实例化到它自己的结构中,并且应该提供一种机制来访问所有这些结构。在我的例子中,ConfigManager 负责将所有 X 配置实例化到它自己的结构中,方法是使用已经定义的 fileName 或 filePattern 从磁盘读取它。让我知道现在是否有意义。
  • 基本上每个结构在磁盘上都有自己的 json 文件(或文件)。因为它们是完全不同的 json 文件。我的 ConfigManager 类将负责将所有这些 json 文件加载到它自己的结构中,并且应该提供访问每个结构及其字段的机制。
猜你喜欢
  • 2021-08-17
  • 1970-01-01
  • 2018-04-05
  • 2019-09-27
  • 1970-01-01
  • 2021-08-28
  • 1970-01-01
  • 2021-09-29
  • 1970-01-01
相关资源
最近更新 更多