4
\$\begingroup\$

I am trying to transfer my c ++ experience to the Go area. I'm new to Go, so not sure if the implementation is correct. In any case, it looks very ugly compared to C ++ code. Is there a nicer solution? Both solutions are executable in playground and give same result.

c++ playground: https://code.sololearn.com/c67xbW2AEWW2/#cpp

#include <iostream>
using namespace std;

// Abstract class
class AbstractGenerator
{
public:
    AbstractGenerator() {}
    std::string generate(std::string some_internal_data)
    {
        _some_internal_data = some_internal_data;
        return step1() + step2();
    }

protected:
    // some shared fuctions
    std::string generationHelper1(std::string s) { return s + _some_internal_data + "123"; }
    std::string generationHelper2(std::string s) { return s + _some_internal_data + "456"; }
    // abstract fucntions
    virtual std::string step1() = 0;
    virtual std::string step2() = 0;

private:
    std::string _some_internal_data;
};
// Implementation
class MyGenerator : public AbstractGenerator
{
public:
    MyGenerator() {}

protected:
    std::string step1() override { return generationHelper1("my step1") + generationHelper2("my step1"); }
    std::string step2() override { return "my step2"; }
};

int main() {
    MyGenerator my_gen;
    AbstractGenerator* abstract_gen = &my_gen;
    cout << abstract_gen->generate("data");
    return 0;
}

Go playground: https://play.golang.org/p/e3iXfYar4YJ

package main

import "fmt"

// some shared fuctions - interface
type ImplementationHelper interface {
    generationHelper1(s string) string
    generationHelper2(s string) string
}

// abstract fucntions - interface
type Implementation interface {
    step1() string
    step2() string
}

// Public main interface
type Generator interface {
    Generate(s string) string
}

type AbstractGenerator struct {
    someInternalData string
    implementation   Implementation
}

func NewAbstractGenerator(i Implementation) *AbstractGenerator {
    g := new(AbstractGenerator)
    g.implementation = i
    return g
}
func (g *AbstractGenerator) Generate(s string) string {
    g.someInternalData = s
    return g.implementation.step1() + g.implementation.step2()
}
func (g *AbstractGenerator) generationHelper1(s string) string { return s + g.someInternalData + "123" }
func (g *AbstractGenerator) generationHelper2(s string) string { return s + g.someInternalData + "456" }

// Implementation of AbstractGenerator
type MyGenerator struct {
    basic *AbstractGenerator
}

// MyGenerator constructor
func NewMyGenerator() *MyGenerator {
    myGen := new(MyGenerator)
    myGen.basic = NewAbstractGenerator(myGen)
    return myGen
}

// Interface Generator
func (i *MyGenerator) Generate(s string) string {
    return i.basic.Generate(s)
}

// Interface Implementation
func (i *MyGenerator) step1() string {
    return i.basic.generationHelper1("my step1") + i.basic.generationHelper2("my step1")
}
func (i *MyGenerator) step2() string {
    return "my step2"
}

func main() {
    myGen := NewMyGenerator()

    var abstractGen Generator
    abstractGen = myGen

    fmt.Println(abstractGen.Generate("data"))
}
\$\endgroup\$
2
  • 1
    \$\begingroup\$ yeah, this code sucks. Whats does it try to achieve ? Thinking about it, i just dont see the point in abstracting anything in this example. I would just have two concrete implementations, and if i need to communicate instances to a third party, let it declare a private interface to receive this or that implementation.... \$\endgroup\$
    – mh-cbon
    Aug 16, 2020 at 13:25
  • \$\begingroup\$ the constructor NewMyGenerator is really funny. It creates a MyGenerator that is given to an AbstractGenerator that in turn is registered as a delegate to MyGenerator. That is not good. This complexity is unjustified. \$\endgroup\$
    – mh-cbon
    Aug 16, 2020 at 13:33

2 Answers 2

1
\$\begingroup\$

I dont understand OP code goals, it floats in thin air.

But i would simply not abstract anything unless required.

package main

import "fmt"

type GeneratorA struct {
}

func (g GeneratorA) Generate(data string) string {
    return "generator A"
}

type GeneratorB struct {
}

func (g GeneratorB) Generate(data string) string {
    return "generator B"
}

func main() {
    consumeGenerator(GeneratorA{})
    consumeGenerator(GeneratorB{})
}

func consumeGenerator(g interface{ Generate(string) string }) {
    fmt.Println(g.Generate("whatever"))
}

anyways, reproducing OOP in a language that promotes composition is just not going to work.

\$\endgroup\$
1
\$\begingroup\$

This:

g := new(AbstractGenerator)
g.implementation = i
return g

Can be more simply written as:

return &AbstractGenerator{implementation: i}
\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.