Idiomatic Go equivalent to subclasses

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

Idiomatic Go equivalent to subclasses

问题

我在C++方面的经验比Go更丰富。我正在尝试理解如何在Go中以惯用方式表达组合设计模式,特别是涉及属性的部分。在C++中,我会使用一个父类来保存一组子类共有的属性和方法。但是我没有看到在Go中如何实现这一点。接口可以让我定义要实现的方法,但它不允许我提供默认实现。我必须在每个实现接口的结构体中重新实现该方法,并在每个结构体中复制所有属性。我不能将共有属性放在接口中,因为接口没有数据元素。在Go中如何进行这种重构呢?

这是一个我希望在Go中能够实现的示例(使用C++编写):

#include <string>

/*
 * 可食用物品的父类。保存“name”属性。
 */

class Edible {
public:
        Edible(const std::string &aName):
                ed_Name(aName) { }
        const std::string &name() const { return ed_Name; }

protected:
        void setName(const std::string &aName) { ed_Name = aName; }

private:
        std::string ed_Name;
};

/*
 * 水果的Edible子类。依赖于Edible来存储名称。
 */

class Fruit: public Edible {
public:
        Fruit(const std::string &aName,
              const std::string &aPlant):
                Edible(aName),
                fr_Plant(aPlant) { }
        const std::string &plant() const { return fr_Plant; }

protected:
        void setPlant(const std::string &aPlant) { fr_Plant = aPlant; }

private:
        std::string fr_Plant;
};

/*
 * 肉类的Edible子类。依赖于Edible来存储名称。
 * 具有动物和肉类切割方式的属性。
 */

class Meat: public Edible {
public:
        Meat(const std::string &aName,
             const std::string &aAnimal,
             const std::string &aCut):
                Edible(aName),
                me_Animal(aAnimal),
                me_Cut(aCut) { }
        const std::string &animal() const { return me_Animal; }
        const std::string &cut() const { return me_Cut; }
protected:
        void setAnimal(const std::string &aAnimal) { me_Animal = aAnimal; }
        void setCut(const std::string &aCut) { me_Cut = aCut; }
private:
        std::string me_Animal;
        std::string me_Cut;
};
英文:

I have a lot more experience in C++ than Go. I'm trying to understand how the Composite design pattern is expressed idiomatically in Go, in particular with reference to attributes. In C++, I'd use a parent class to hold attributes and methods that are common to a set of subclasses. I'm not seeing how this works in Go. An interface lets me define which methods to implement, but it doesn't let me provide a default implementation. I have to reimplement the method in every struct that implements the interface, and replicate all of the attributes in each struct. I can't keep common attributes in an interface, because interfaces don't have data elements. How do you do this sort of refactoring in Go?

Here's an example (in C++) of what I'd like to be able to do in Go:

#include &lt;string&gt;
/*
* Parent class for edible things. Holds the &quot;name&quot; attribute.
*/
class Edible {
public:
Edible(const std::string &amp;aName):
ed_Name(aName) { }
const std::string &amp;name() const { return ed_Name; }
protected:
void setName(const std::string &amp;aName) { ed_Name = aName; }
private:
std::string ed_Name;
};
/*
* Subclass of Edible for fruits. Depends on Edible to store the name.
*/
class Fruit: public Edible {
public:
Fruit(const std::string &amp;aName,
const std::string &amp;aPlant):
Edible(aName),
fr_Plant(aPlant) { }
const std::string &amp;plant() const { return fr_Plant; }
protected:
void setPlant(const std::string &amp;aPlant) { fr_Plant = aPlant; }
private:
std::string fr_Plant;
};
/*
* Subclass of Edible for meats. Depends on Edible to store the name.
* Has attributes for the animal and the cut of meat.
*/
class Meat: public Edible {
public:
Meat(const std::string &amp;aName,
const std::string &amp;aAnimal,
const std::string &amp;aCut):
Edible(aName),
me_Animal(aAnimal),
me_Cut(aCut) { }
const std::string &amp;animal() const { return me_Animal; }
const std::string &amp;cut() const { return me_Cut; }
protected:
void setAnimal(const std::string &amp;aAnimal) { me_Animal = aAnimal; }
void setCut(const std::string &amp;aCut) { me_Cut = aCut; }
private:
std::string me_Animal;
std::string me_Cut;
};

答案1

得分: 1

在这个例子中,你可以创建一个Edible接口,以及type Fruit structtype Meat struct,它们分别实现了该接口。每个结构体还可以通过组合方式包含一个EdibleName,它提供了设置/获取名称的方法和存储空间。

例如:

type Edible interface {
    eat() int // 公共方法
}

type EdibleName struct {
    name string
}

// 注意:getter和setter可能不符合惯用写法
func (n *EdibleName) getName() string {
    return n.name
}

func (n *EdibleName) setName(name string) {
    n.name = name
}

type Fruit struct {
    pips int
    EdibleName
}

func (f *Fruit) eat() int {
    // ...
    return 0
}

type Meat struct {
    animal int
    EdibleName
}

func (m *Meat) eat() int {
    // ...
    return 0
}

以上是代码的翻译部分。

英文:

In this instance, you could have an Edible interface, and type Fruit struct and type Meat struct which each implemented them. Each of those could also be composed such that it contained an EdibleName which would provide the methods and storage space to set / get the name.

e.g.

type Edible interface {
eat() int // common method
}
type EdibleName struct {
name string
}
// NB getters and setters may not be idiomatic
func (n *EdibleName) getName() string {
return n.name
}
func (n *EdibleName) setName(name string) {
n.name = name
}
type Fruit struct {
pips int
EdibleName
}
func (f *Fruit) eat() int {
// ...
return 0
}
type Meat struct {
animal int
EdibleName
}
func (m *Meat) eat() int {
animal int
return 0
}

huangapple
  • 本文由 发表于 2016年1月6日 23:13:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/34636455.html
匿名

发表评论

匿名网友

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

确定