I am trying to understand better how one can use the protected space in Go. I am coming form java which means that I can have values accessible via protected inheritance, as there is only composition here I wanted to make sure I was on the right path.
The problem: I want to set a value in a child implementation, but not expose a setter on the generic interface.
What is the best way to provide a setter to a 'sub-class' when there really isn't a hierarchy?
This means I want:
type Bottom interface {
GetYouSome()
// rote things
SetSpeed(int)
DeliveryMechanism() chan string
}
Note that there is no SetDeliveryMechanism(chan string) method.
I thought I would start with a base that implements the rote things, meaning that actual implementations will provide the 'GetYouSome' method. I'd also like these to exist inside different packages. There will be dozens of implementations and I'd like to have the namespaces be sandboxed (i.e. they can both use the const DefaultPort).
To illustrate the problem I have made a little project. It is laid out like this:
.
└── src
├── main.go
└── parent
├── child
│ └── child.go
└── parent.go
Where in child.go we actually create a few types of Bottom, but in parent.go we actually define the boilerplate code (setters/getters). The problem is that I can't instantiate the channel anywhere!
Ideally an implementation would look like this:
//------------- parent.go -------------
package parent
type Bottom interface {
GetYouSome()
// rote things
SetSpeed(int)
DeliveryMechanism() chan string
}
// Intended to implement the boring things
type GenericBottom struct {
speed int
deliveryChan chan string
}
func (bot *GenericBottom) SetSpeed(speed int) {
bot.speed = speed
}
func (bot GenericBottom) DeliveryMechanism() chan string {
return bot.deliveryChan
}
//------------- child.go -------------
package child
import "parent"
func New(speed int) parent.Bottom {
impl := new(Composite)
impl.name = "simple"
impl.SetSpeed(speed)
// illegal! not exported
// impl.deliveryChannel = make(chan string)
return impl
}
// intended so that we can seamlessly treat the Composite
// as a Bottom
type Composite struct {
parent.GenericBottom
name string
}
func (a Composite) GetYouSome() {
fmt.Println("Inside the actual implementation")
}
There are two ways I can think to get around this.
(1) create a child class that would wrap the GenericBottom class, passing through all the methods. That is le sad, and it also still has the problem that I can't access the deliveryChannel class directly. I would have to build a new constructor into the parent package, then explicitly set the instance in the child class.
//------------- parent.go -------------
func NewGenericBottom() GenericBottom {
return GenericBottom{0, make(chan string)}
}
//------------- child.go -------------
func New(speed int) parent.Bottom {
impl := new(ExplicitComposite)
impl.name = "explicit"
// now I can set the channel? Nope
// impl.gb = parent.GenericBottom{speed, make(chan string)}
impl.gb = parent.NewGenericBottom()
impl.SetSpeed(speed)
return impl
}
// this has to pass through each method
type ExplicitComposite struct {
gb parent.GenericBottom
name string
}
func (e ExplicitComposite) GetYouSome() {
fmt.Println("Inside the explicit implementation")
}
func (e ExplicitComposite) DeliveryMechanism() chan string {
return e.gb.DeliveryMechanism()
}
func (e *ExplicitComposite) SetSpeed(speed int) {
e.gb.SetSpeed(speed)
}
OR! (2) I can add a setter method on the GenericBottom. But anyone using the GenericBottom can just cast to that an access it right? I wouldn't really be 'protected'.
Like this:
//------------- parent.go -------------
func (bot *GenericBottom) SetChannel(c chan string) {
bot.deliveryChan = c
}
//------------- child.go -------------
type CheatersBottom struct {
parent.GenericBottom
name string
}
func (a CheatersBottom) GetYouSome() {
fmt.Println("Inside the cheaters bottom")
}
func NewCheatersBottom(speed int) parent.Bottom {
impl := new(CheatersBottom)
impl.SetChannel(make(chan string))
impl.SetSpeed(speed)
return impl
}
What is the best way to provide a setter to a 'sub-class' when there really isn't a hierarchy?