Abstract base class design in Go vs C++
I am still learning the Go way of doing things, coming from a C++ background. I am looking for feedback contrasting OOP inheritance to interface composition.
I have a design situation in a Go program where, if I was implementing in C++, I would solve with an abstract base class.
Suppose I need a base class, which has many implementors. The base class has shared methods that do work on abstract data items. Different Worker implementations provide CRUD operations on different item types, but workers all use the shared methods of the base class for general work.
In C++ I might do it this way
class IItem
{
// virtual methods
};
class IWorker
{
public:
// one of many virtual functions that deal with IItem CRUD
virtual IItem* createItem() = 0;
// concrete method that works on interfaces
void doWork()
{
IItem* item = createItem();
// do stuff with an IItem*
}
};
class Toy : public IItem
{
};
// one of many kinds of workers
class ElfWorker : public IWorker
{
public:
ElfWorker()
{
// constructor implicitly calls IWorker()
}
IItem* createItem() override
{
return new Toy;
}
};
In Go you don't have abstract virtual methods such as IWorker::createItem(). Concrete classes need to supply the base with an interface or function that do the right thing.
So I think it is the case that the Go code the ew.ItemCRUD interface has to be explicitly set with a pointer to an ElfWorker.
The elf knows how to createItem(), which in his case happens to be Toy object. Other workers would implement their own ItemCRUD for their data objects.
type Item interface {
// various methods
}
type ItemCRUD interface {
create() Item
// other CRUD
}
type Worker struct {
ItemCRUD // embedded interface
}
func (w *Worker) doWork() {
item := w.create()
// do stuff with item
}
type Toy struct {
}
type ElfWorker struct {
Worker // embedded
// ..
}
func NewElfWorker() *ElfWorker {
ew := &ElfWorker{}
ew.ItemCRUD = ew // <-- #### set Worker ItemCRUD explicitly ####
return ew
}
func (ew *ElfWorker) createItem() Item {
return &Toy{}
}
// more ElfWorker Item CRUD
func bigFunction(w *Worker) {
// ...
w.doWork()
// ..
}
So the part that I am wrestling a bit with is explicit setting. Seems like the "Go way" of composition does require this explicit step if I want the base Worker class to provide shared methods on Items.
Thoughts?
go virtual composition
add a comment |
I am still learning the Go way of doing things, coming from a C++ background. I am looking for feedback contrasting OOP inheritance to interface composition.
I have a design situation in a Go program where, if I was implementing in C++, I would solve with an abstract base class.
Suppose I need a base class, which has many implementors. The base class has shared methods that do work on abstract data items. Different Worker implementations provide CRUD operations on different item types, but workers all use the shared methods of the base class for general work.
In C++ I might do it this way
class IItem
{
// virtual methods
};
class IWorker
{
public:
// one of many virtual functions that deal with IItem CRUD
virtual IItem* createItem() = 0;
// concrete method that works on interfaces
void doWork()
{
IItem* item = createItem();
// do stuff with an IItem*
}
};
class Toy : public IItem
{
};
// one of many kinds of workers
class ElfWorker : public IWorker
{
public:
ElfWorker()
{
// constructor implicitly calls IWorker()
}
IItem* createItem() override
{
return new Toy;
}
};
In Go you don't have abstract virtual methods such as IWorker::createItem(). Concrete classes need to supply the base with an interface or function that do the right thing.
So I think it is the case that the Go code the ew.ItemCRUD interface has to be explicitly set with a pointer to an ElfWorker.
The elf knows how to createItem(), which in his case happens to be Toy object. Other workers would implement their own ItemCRUD for their data objects.
type Item interface {
// various methods
}
type ItemCRUD interface {
create() Item
// other CRUD
}
type Worker struct {
ItemCRUD // embedded interface
}
func (w *Worker) doWork() {
item := w.create()
// do stuff with item
}
type Toy struct {
}
type ElfWorker struct {
Worker // embedded
// ..
}
func NewElfWorker() *ElfWorker {
ew := &ElfWorker{}
ew.ItemCRUD = ew // <-- #### set Worker ItemCRUD explicitly ####
return ew
}
func (ew *ElfWorker) createItem() Item {
return &Toy{}
}
// more ElfWorker Item CRUD
func bigFunction(w *Worker) {
// ...
w.doWork()
// ..
}
So the part that I am wrestling a bit with is explicit setting. Seems like the "Go way" of composition does require this explicit step if I want the base Worker class to provide shared methods on Items.
Thoughts?
go virtual composition
7
Drawing parallels between C++ and any other language can prove to be counter-productive.
– Ron
Jan 2 at 18:30
first derivative to @Ron’s comment: Applying patterns found in other programming languages to Go can prove counter-productive.
– Markus W Mahlberg
Jan 2 at 23:43
I'm voting to close this question as off-topic because it shouldbe moved to Codereview
– thst
Jan 3 at 8:11
Down vote because I asked for feedback on a design choice? I think I clearly stated I was looking to learn? I stated up front the languages would solve the problem in different ways.
– meissnersd
Jan 3 at 19:24
add a comment |
I am still learning the Go way of doing things, coming from a C++ background. I am looking for feedback contrasting OOP inheritance to interface composition.
I have a design situation in a Go program where, if I was implementing in C++, I would solve with an abstract base class.
Suppose I need a base class, which has many implementors. The base class has shared methods that do work on abstract data items. Different Worker implementations provide CRUD operations on different item types, but workers all use the shared methods of the base class for general work.
In C++ I might do it this way
class IItem
{
// virtual methods
};
class IWorker
{
public:
// one of many virtual functions that deal with IItem CRUD
virtual IItem* createItem() = 0;
// concrete method that works on interfaces
void doWork()
{
IItem* item = createItem();
// do stuff with an IItem*
}
};
class Toy : public IItem
{
};
// one of many kinds of workers
class ElfWorker : public IWorker
{
public:
ElfWorker()
{
// constructor implicitly calls IWorker()
}
IItem* createItem() override
{
return new Toy;
}
};
In Go you don't have abstract virtual methods such as IWorker::createItem(). Concrete classes need to supply the base with an interface or function that do the right thing.
So I think it is the case that the Go code the ew.ItemCRUD interface has to be explicitly set with a pointer to an ElfWorker.
The elf knows how to createItem(), which in his case happens to be Toy object. Other workers would implement their own ItemCRUD for their data objects.
type Item interface {
// various methods
}
type ItemCRUD interface {
create() Item
// other CRUD
}
type Worker struct {
ItemCRUD // embedded interface
}
func (w *Worker) doWork() {
item := w.create()
// do stuff with item
}
type Toy struct {
}
type ElfWorker struct {
Worker // embedded
// ..
}
func NewElfWorker() *ElfWorker {
ew := &ElfWorker{}
ew.ItemCRUD = ew // <-- #### set Worker ItemCRUD explicitly ####
return ew
}
func (ew *ElfWorker) createItem() Item {
return &Toy{}
}
// more ElfWorker Item CRUD
func bigFunction(w *Worker) {
// ...
w.doWork()
// ..
}
So the part that I am wrestling a bit with is explicit setting. Seems like the "Go way" of composition does require this explicit step if I want the base Worker class to provide shared methods on Items.
Thoughts?
go virtual composition
I am still learning the Go way of doing things, coming from a C++ background. I am looking for feedback contrasting OOP inheritance to interface composition.
I have a design situation in a Go program where, if I was implementing in C++, I would solve with an abstract base class.
Suppose I need a base class, which has many implementors. The base class has shared methods that do work on abstract data items. Different Worker implementations provide CRUD operations on different item types, but workers all use the shared methods of the base class for general work.
In C++ I might do it this way
class IItem
{
// virtual methods
};
class IWorker
{
public:
// one of many virtual functions that deal with IItem CRUD
virtual IItem* createItem() = 0;
// concrete method that works on interfaces
void doWork()
{
IItem* item = createItem();
// do stuff with an IItem*
}
};
class Toy : public IItem
{
};
// one of many kinds of workers
class ElfWorker : public IWorker
{
public:
ElfWorker()
{
// constructor implicitly calls IWorker()
}
IItem* createItem() override
{
return new Toy;
}
};
In Go you don't have abstract virtual methods such as IWorker::createItem(). Concrete classes need to supply the base with an interface or function that do the right thing.
So I think it is the case that the Go code the ew.ItemCRUD interface has to be explicitly set with a pointer to an ElfWorker.
The elf knows how to createItem(), which in his case happens to be Toy object. Other workers would implement their own ItemCRUD for their data objects.
type Item interface {
// various methods
}
type ItemCRUD interface {
create() Item
// other CRUD
}
type Worker struct {
ItemCRUD // embedded interface
}
func (w *Worker) doWork() {
item := w.create()
// do stuff with item
}
type Toy struct {
}
type ElfWorker struct {
Worker // embedded
// ..
}
func NewElfWorker() *ElfWorker {
ew := &ElfWorker{}
ew.ItemCRUD = ew // <-- #### set Worker ItemCRUD explicitly ####
return ew
}
func (ew *ElfWorker) createItem() Item {
return &Toy{}
}
// more ElfWorker Item CRUD
func bigFunction(w *Worker) {
// ...
w.doWork()
// ..
}
So the part that I am wrestling a bit with is explicit setting. Seems like the "Go way" of composition does require this explicit step if I want the base Worker class to provide shared methods on Items.
Thoughts?
go virtual composition
go virtual composition
edited Jan 2 at 19:38
Flimzy
40.2k1367100
40.2k1367100
asked Jan 2 at 18:28
meissnersdmeissnersd
7261818
7261818
7
Drawing parallels between C++ and any other language can prove to be counter-productive.
– Ron
Jan 2 at 18:30
first derivative to @Ron’s comment: Applying patterns found in other programming languages to Go can prove counter-productive.
– Markus W Mahlberg
Jan 2 at 23:43
I'm voting to close this question as off-topic because it shouldbe moved to Codereview
– thst
Jan 3 at 8:11
Down vote because I asked for feedback on a design choice? I think I clearly stated I was looking to learn? I stated up front the languages would solve the problem in different ways.
– meissnersd
Jan 3 at 19:24
add a comment |
7
Drawing parallels between C++ and any other language can prove to be counter-productive.
– Ron
Jan 2 at 18:30
first derivative to @Ron’s comment: Applying patterns found in other programming languages to Go can prove counter-productive.
– Markus W Mahlberg
Jan 2 at 23:43
I'm voting to close this question as off-topic because it shouldbe moved to Codereview
– thst
Jan 3 at 8:11
Down vote because I asked for feedback on a design choice? I think I clearly stated I was looking to learn? I stated up front the languages would solve the problem in different ways.
– meissnersd
Jan 3 at 19:24
7
7
Drawing parallels between C++ and any other language can prove to be counter-productive.
– Ron
Jan 2 at 18:30
Drawing parallels between C++ and any other language can prove to be counter-productive.
– Ron
Jan 2 at 18:30
first derivative to @Ron’s comment: Applying patterns found in other programming languages to Go can prove counter-productive.
– Markus W Mahlberg
Jan 2 at 23:43
first derivative to @Ron’s comment: Applying patterns found in other programming languages to Go can prove counter-productive.
– Markus W Mahlberg
Jan 2 at 23:43
I'm voting to close this question as off-topic because it shouldbe moved to Codereview
– thst
Jan 3 at 8:11
I'm voting to close this question as off-topic because it shouldbe moved to Codereview
– thst
Jan 3 at 8:11
Down vote because I asked for feedback on a design choice? I think I clearly stated I was looking to learn? I stated up front the languages would solve the problem in different ways.
– meissnersd
Jan 3 at 19:24
Down vote because I asked for feedback on a design choice? I think I clearly stated I was looking to learn? I stated up front the languages would solve the problem in different ways.
– meissnersd
Jan 3 at 19:24
add a comment |
2 Answers
2
active
oldest
votes
Beeing new to go myself, this answer is not backed by years of go experience :-)
I don't know, if the way you tackle this is the correct approach.
go allows interfaces to be implemented without explicit declaration. If you have elves, and you need them to do ItemCRUD
methods, just implement them.
The method set will then match the interface and you can use the elf as a ItemCRUD
where required.
To supply any elf object with a default ItemCRUD
Implementation, you should implement an adapter for the ItemCRUD
and compose the adapter with your abstract elf. The abstract methods could have a default implementation as log.Fatal("not implemented")
The concrete elves shadow the adapter's methods, this answers your question: It is not required to insert the object during creation.
Yet, since go has no generics, it may not be the right approach to have an ItemCRUD
.
add a comment |
Im not entirely clear what the plan is with the above code and without understanding that its hard to suggest specific solutions, what is clear is you are very much coming to the party with an established OOP mindset (I did too) which is rarely helpful in finding the best solution in golang.
In Golang I wouldnt usually embed an interface in an implementation, interfaces are satisfied implicitly in Golang which allows for a nice separation of expectation and implementation which should generally be respected.
A reciever method should expect an interface, the implementation passed at runtime should just satisfy the signature of that interface implicitly.
So perhaps my doWork method needs to be able to createItems then it would be the doWork method that would accept any implementation of ItemCRUD which it could call to create an item. But this is me guessing at what you really want to do here, I suspect if you just separate implementation from interface you will probably answer your own question.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54011364%2fabstract-base-class-design-in-go-vs-c%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
Beeing new to go myself, this answer is not backed by years of go experience :-)
I don't know, if the way you tackle this is the correct approach.
go allows interfaces to be implemented without explicit declaration. If you have elves, and you need them to do ItemCRUD
methods, just implement them.
The method set will then match the interface and you can use the elf as a ItemCRUD
where required.
To supply any elf object with a default ItemCRUD
Implementation, you should implement an adapter for the ItemCRUD
and compose the adapter with your abstract elf. The abstract methods could have a default implementation as log.Fatal("not implemented")
The concrete elves shadow the adapter's methods, this answers your question: It is not required to insert the object during creation.
Yet, since go has no generics, it may not be the right approach to have an ItemCRUD
.
add a comment |
Beeing new to go myself, this answer is not backed by years of go experience :-)
I don't know, if the way you tackle this is the correct approach.
go allows interfaces to be implemented without explicit declaration. If you have elves, and you need them to do ItemCRUD
methods, just implement them.
The method set will then match the interface and you can use the elf as a ItemCRUD
where required.
To supply any elf object with a default ItemCRUD
Implementation, you should implement an adapter for the ItemCRUD
and compose the adapter with your abstract elf. The abstract methods could have a default implementation as log.Fatal("not implemented")
The concrete elves shadow the adapter's methods, this answers your question: It is not required to insert the object during creation.
Yet, since go has no generics, it may not be the right approach to have an ItemCRUD
.
add a comment |
Beeing new to go myself, this answer is not backed by years of go experience :-)
I don't know, if the way you tackle this is the correct approach.
go allows interfaces to be implemented without explicit declaration. If you have elves, and you need them to do ItemCRUD
methods, just implement them.
The method set will then match the interface and you can use the elf as a ItemCRUD
where required.
To supply any elf object with a default ItemCRUD
Implementation, you should implement an adapter for the ItemCRUD
and compose the adapter with your abstract elf. The abstract methods could have a default implementation as log.Fatal("not implemented")
The concrete elves shadow the adapter's methods, this answers your question: It is not required to insert the object during creation.
Yet, since go has no generics, it may not be the right approach to have an ItemCRUD
.
Beeing new to go myself, this answer is not backed by years of go experience :-)
I don't know, if the way you tackle this is the correct approach.
go allows interfaces to be implemented without explicit declaration. If you have elves, and you need them to do ItemCRUD
methods, just implement them.
The method set will then match the interface and you can use the elf as a ItemCRUD
where required.
To supply any elf object with a default ItemCRUD
Implementation, you should implement an adapter for the ItemCRUD
and compose the adapter with your abstract elf. The abstract methods could have a default implementation as log.Fatal("not implemented")
The concrete elves shadow the adapter's methods, this answers your question: It is not required to insert the object during creation.
Yet, since go has no generics, it may not be the right approach to have an ItemCRUD
.
edited Jan 2 at 19:00
answered Jan 2 at 18:53
thstthst
3,70611634
3,70611634
add a comment |
add a comment |
Im not entirely clear what the plan is with the above code and without understanding that its hard to suggest specific solutions, what is clear is you are very much coming to the party with an established OOP mindset (I did too) which is rarely helpful in finding the best solution in golang.
In Golang I wouldnt usually embed an interface in an implementation, interfaces are satisfied implicitly in Golang which allows for a nice separation of expectation and implementation which should generally be respected.
A reciever method should expect an interface, the implementation passed at runtime should just satisfy the signature of that interface implicitly.
So perhaps my doWork method needs to be able to createItems then it would be the doWork method that would accept any implementation of ItemCRUD which it could call to create an item. But this is me guessing at what you really want to do here, I suspect if you just separate implementation from interface you will probably answer your own question.
add a comment |
Im not entirely clear what the plan is with the above code and without understanding that its hard to suggest specific solutions, what is clear is you are very much coming to the party with an established OOP mindset (I did too) which is rarely helpful in finding the best solution in golang.
In Golang I wouldnt usually embed an interface in an implementation, interfaces are satisfied implicitly in Golang which allows for a nice separation of expectation and implementation which should generally be respected.
A reciever method should expect an interface, the implementation passed at runtime should just satisfy the signature of that interface implicitly.
So perhaps my doWork method needs to be able to createItems then it would be the doWork method that would accept any implementation of ItemCRUD which it could call to create an item. But this is me guessing at what you really want to do here, I suspect if you just separate implementation from interface you will probably answer your own question.
add a comment |
Im not entirely clear what the plan is with the above code and without understanding that its hard to suggest specific solutions, what is clear is you are very much coming to the party with an established OOP mindset (I did too) which is rarely helpful in finding the best solution in golang.
In Golang I wouldnt usually embed an interface in an implementation, interfaces are satisfied implicitly in Golang which allows for a nice separation of expectation and implementation which should generally be respected.
A reciever method should expect an interface, the implementation passed at runtime should just satisfy the signature of that interface implicitly.
So perhaps my doWork method needs to be able to createItems then it would be the doWork method that would accept any implementation of ItemCRUD which it could call to create an item. But this is me guessing at what you really want to do here, I suspect if you just separate implementation from interface you will probably answer your own question.
Im not entirely clear what the plan is with the above code and without understanding that its hard to suggest specific solutions, what is clear is you are very much coming to the party with an established OOP mindset (I did too) which is rarely helpful in finding the best solution in golang.
In Golang I wouldnt usually embed an interface in an implementation, interfaces are satisfied implicitly in Golang which allows for a nice separation of expectation and implementation which should generally be respected.
A reciever method should expect an interface, the implementation passed at runtime should just satisfy the signature of that interface implicitly.
So perhaps my doWork method needs to be able to createItems then it would be the doWork method that would accept any implementation of ItemCRUD which it could call to create an item. But this is me guessing at what you really want to do here, I suspect if you just separate implementation from interface you will probably answer your own question.
answered Jan 2 at 19:03
SwiftDSwiftD
3,35642753
3,35642753
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54011364%2fabstract-base-class-design-in-go-vs-c%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
7
Drawing parallels between C++ and any other language can prove to be counter-productive.
– Ron
Jan 2 at 18:30
first derivative to @Ron’s comment: Applying patterns found in other programming languages to Go can prove counter-productive.
– Markus W Mahlberg
Jan 2 at 23:43
I'm voting to close this question as off-topic because it shouldbe moved to Codereview
– thst
Jan 3 at 8:11
Down vote because I asked for feedback on a design choice? I think I clearly stated I was looking to learn? I stated up front the languages would solve the problem in different ways.
– meissnersd
Jan 3 at 19:24