Subtypes Supertypes in go

huangapple go评论73阅读模式
英文:

Subtypes Supertypes in go

问题

从面向对象编程(OOP)范式中转换代码并将其从一种OOP语言移植过来时,我现在遇到了一个问题,这个问题在OOP中通过抽象解决了,所以我想知道在Go语言中如何处理以下问题,因为Go语言使用组合而不是继承。

在这种情况下,我的值对象(DTO、POJO等)由其他值对象组成。我通过Web服务调用来填充它们,这些调用返回JSON,因此基本上我的函数/方法调用对所有类型和子类型都是通用的。

我的超类型EntityVO:

type EntityVO struct {
    EntityName    string
    EntityType    string
    PublicationId string
    Version       string
}

一个由EntityVO组成的子类型1:

type ArticleVO struct {
    EntityVO
    ContentSize string
    Created     string
}

一个由EntityVO组成的子类型2,具有自己独特的字段集:

type CollectionVO struct {
    EntityVO
    ProductId string
    Position  string
}

我调用Web服务来检索数据并填充这些VO。

以前,我有一个函数来调用Web服务并填充数据,但现在我为每个VO复制了代码。

type Article struct{}

func (a *Article) RequestList(articleVO *valueObject.ArticleVO) (*valueObject.ArticleVO, error) { 
    // 一些代码
}

复制相同的代码,但更改签名。

type Collection struct{}

func (c *Collection) RequestList(collectionVO *valueObject.CollectionVO) (*valueObject.ArticleVO, error) { 
    // 一些代码 - 与上面的代码相同,只是方法签名不同
}

我有几个实体,只是因为我的VO不同,我被迫复制代码并为每种类型的VO提供支持。在OOP中,子类型可以传递给接受超类型的函数,但在Go中不行,所以我想知道应该如何处理,以免出现仅在签名上有所不同的重复代码?

在这种情况下,有什么更好的方法吗?

英文:

Coming from OOP paradigms and porting a code from an OOP language, I come across a problem now which is solved in OOP via abstraction so I'm wondering how can I approach the following problem in Go which follows composition instead of inheritance.

In this scenario my ValueObjects (DTO, POJO etc.) are composed of other ValueObjects. I'm populating them through web service calls that returns json so basically my functions/method calls are common for all types and subtypes.

My super type EntityVO

type EntityVO struct {
    EntityName    string
    EntityType    string
    PublicationId string
    Version       string
}

A subtype 1 composed with EntityVO

type ArticleVO struct {
    EntityVO
    ContentSize string
    Created     string
}

subtype 2 composed with EntityVO with it's own unique set of fields

type CollectionVO struct {
	EntityVO
	ProductId string
    Position string
}

I'm calling web services to retrieve data and populate these VOs.

Earlier I had one function to call the web service and populate the data but now I'm duplicating the code for each VO.

type Article struct{}

func (a *Article) RequestList(articleVO *valueObject.ArticleVO) (*valueObject.ArticleVO, error) { 
    // some code
}

Duplicating the same code but changing the signature.

type Collection struct{}

func (c * Collection) RequestList(collectionVO *valueObject.CollectionVO) (*valueObject.ArticleVO, error) { 
    // some code - duplicate same as above except method signature
}

and I've several entities and just because my VO's are different I'm forced to duplicate the code and cater to each type of VO I've. In OOP sub types can be passed to a function accepting super types but not in go, so wondering how it should be done so I don't end up duplicated code that's different in signature only?

Any advice for a better approach in this kind of scenario?

答案1

得分: 3

这是golang接口可以发挥作用的地方。

值得注意的是,在golang中编写子类/继承代码是困难的。我们更倾向于将其视为组合。

type EntityVO interface {
    GetName() string
    SetName(string) error
    GetType() string
    ...
}

type EntityVOImpl struct {
    EntityName    string
    EntityType    string
    PublicationId string
    Version       string
}

func (e EntityVOImpl) GetName() string {
    return e.EntityName
}

...

type ArticleVOImpl struct {
    EntityVOImpl
    ContentSize string
    Created     string
}

type CollectionVOImpl struct {
    EntityVO
    ProductId string
    Position string
}

// 代码

func (e *Entity) RequestList(entityVO valueObject.EntityVO) (valueObject.EntityVO, error) { 
    // 一些代码
}

此外,只要您共享接口文件,我认为在网络上传输/编组/解组结构体不应该有任何问题。

英文:

This is where golang interfaces can shine.

It's worth noting, however, that it's difficult to write subclass/inheritance code in golang. We'd prefer thinking of it as composition.

type EntityVO interface {
    GetName() string
    SetName(string) error
    GetType() string
    ...
}

type EntityVOImpl struct {
    EntityName    string
    EntityType    string
    PublicationId string
    Version       string
}

func (e EntityVOImpl) GetName() string {
    return e.EntityName
}

...

type ArticleVOImpl struct {
    EntityVOImpl
    ContentSize string
    Created     string
}

type CollectionVOImpl struct {
    EntityVO
    ProductId string
    Position string
}

// CODE

func (e *Entity) RequestList(entityVO valueObject.EntityVO) (valueObject.EntityVO, error) { 
    // some code
}

In addition, as long as your interface files are shared, I don't think there should by any problem sending/marshalling/unmarshalling the structs over the wire.

答案2

得分: 1

json.Unmarshal的第二个参数是一个"通用"的interface{}类型。我认为类似的方法适用于你的情况。

  1. 定义用于存储每个实体可能不同的Web服务属性的数据类型,例如:

     type ServiceDescriptor struct {
         URI string
         //其他字段/属性...
     }
    
  2. 用于填充实体的函数可能如下所示:

     func RequestList(desc *ServiceDescriptor, v interface{}) error {
         //从Web服务中获取JSON数据
         //一些代码
    
         return json.Unmarshal(data, v)
     }
    
  3. 你可以调用该函数来填充不同类型的实体,例如:

     desc := &ServiceDescriptor{}
    
     article := ArticleVO{}
     desc.URI = "article.uri"
     //...
     //你必须传递实体的指针作为第二个参数
     RequestList(desc, &article)
    
     collection := CollectionVO{}
     desc.URI = "collection.uri"
     //...
     //你必须传递实体的指针作为第二个参数
     RequestList(desc, &collection)
    
英文:

The second parameter of json.Unmarshal is a "universal" interface{} type. I think similar approach is applicable to your case.

  1. Define data type for storing web service properties which may differ for each entity, e.g.

    type ServiceDescriptor struct {
      URI string
      //other field/properties...
    }
    
  2. Function for populating the entity may look like

    func RequestList(desc *ServiceDescriptor, v interface{}) error {
        //Retrieve json data from web service
        //some code
    
        return json.Unmarshal(data, v)
    }
    
  3. You can call the function to populate different type of entities, e.g.

    desc := &ServiceDescriptor{}
    
    article := ArticleVO{}
    desc.URI = "article.uri"
    //...
    //You must pass pointer to entity for 2nd param
    RequestList(desc, &article)
    
    collection := CollectionVO{}
    desc.URI = "collection.uri"
    //...
    //You must pass pointer to entity for 2nd param
    RequestList(desc, &collection)
    

huangapple
  • 本文由 发表于 2017年5月4日 04:39:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/43769590.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定