I have a general handler that contains the data it needs to do all it has to do.
type Handler struct {
info HandlerInfo
}
Then the sender struct, which equally has its own information.
type Sender struct {
info SenderInfo
}
Finally, there's a receiver, that also has its own information.
type Receiver struct {
info ReceiverInfo
}
The handler generates communicating pairs along the execution of the program. I don't wanna give examples of the type of communication that occurs between the different structures I have defined since I want my example to be the broadest possible.
func (h Handler) Do() {
for /* something happens */ {
sender := Sender{}
receiver := Receiver{}
}
sender.DoSomething()
if sender.ShouldSend() {
receiver.Receive()
}
}
Everything looks quite simple up to here but I'm facing two issues :
1) How to handle the data being shared by Sender and Receiver without redundancy?
2) How to handle the data that's used by Handler but that Sender and Receiver also need to use?
One might say it's only necessary to fill both structures step by step with what they're going to need
But it makes things too much redundant IMO:
func DoSomethingAndFill() {
data1 := generateSomeData1()
recv.Receive1(data1)
sender.Send1(data1)
data2 := generateSomeData2()
recv.Receive2(data2)
sender.Send2(data2)
}
I told myself I could (litterally) share a base structure containing the data that needs to be shared :
type MetaData struct {
Data1 Data
Data2 Data
}
type Sender struct {
*MetaData
info SenderInfo
}
type Receiver struct {
*MetaData
info ReceiverInfo
}
And when initializing I only needed to put the same pointer?
func init() {
md := MetaData{}
recv, send := Receiver{&md}, Sender{&md}
}
But this forces me to do two things :
On one hand if I want to make things clean and make a package for Receiver and another one for Sender, I have to make all fields of MetaData exported, which, while not being dramatically bad, is not ideal.
On the other hand, I'm forced to make the code a little more confusing since when filling the structure I have to juggle between what goes into Sender, Receiver and MetaData.
This first thing that came to my mind was to make a sort of backward link to the parent structure :
type Handler struct {
Info HandlerInfo
}
type Sender struct {
handler Handler
info SenderInfo
}
type Receiver struct {
handler Handler
info ReceiverInfo
}
func (h Handler) MakeRecvAndSend() {
recv, send := Receiver{
handler: h,
}, Sender{
handler: h,
}
}
And at first sight it seems quite good, but again, if Sender and Receiver are in different packages, and since circular imports are not permitted in Golang, it is not possible.
Another solution I thought of would be to copy the relevant information piece by piece:
type Handler struct {
info1 HandlerInfo
info2 HandlerInfo
}
type Sender struct {
handlerInfo HandlerInfo
info SenderInfo
}
type Receiver struct {
handlerInfo HanlderInfo
info ReceiverInfo
}
func (h Handler) MakeRecvAndSend() {
recv, send := Receiver{
handlerInfo: h.info1,
}, Sender{
handler: h.info2,
}
}
But this is reaaaally verbose, and according to the way I generate senders and receivers I'm afraid this is going to impact runtime speed / memory.
If you already ran into such issues, I'd like your feedback and the way you solved this. I'm also interesting in any theoretical thought you might want to share!
Thx!