Reputation: 3977
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 <string>
/*
* Parent class for edible things. Holds the "name" attribute.
*/
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;
};
/*
* Subclass of Edible for fruits. Depends on Edible to store the name.
*/
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;
};
/*
* 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 &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;
};
Upvotes: 1
Views: 279
Reputation: 25129
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
}
Upvotes: 1