Factory methods with struct vs. interface

Hi,

I don’t know which subject could be better so first I show you the code:

// You can edit this code!
// Click here and start typing.
package main

import "fmt"

// The interface fulfilled by S
type I interface {
	Method()
}

// Fulfills interface I
type S struct{}

// Method to fulfill I
func (s S) Method() {
	fmt.Println("Method exists")
}

// Factory that returns the implementation of I
func Factory() func() S {
	return func() S {
		return S{}
	}
}

// Factory that returns the interface I
type InterfaceFactory func() I

// Wants to have the "normal" factory
func IWantToGetAFactory(f func() S) {
	f().Method()
}

// Wants to have the "interface" factory
func IWantToGetAnInterfaceFactory(f InterfaceFactory) {
	f().Method()
}

func main() {
	IWantToGetAFactory(Factory())
	IWantToGetAnInterfaceFactory(Factory())
}

The problem is that IWantToGetAnInterfaceFactory accepts only InterfaceFactory. Now that I provide Factory it fails since Go seems not to recognize that

func() I

Is a subset of

func() S

Is there a way how I can achive this nevertheless?

Change the type of your factory to return the interface.

func Factory() func() I {

That is not possible. In my real-world use-case the actual factory method returns a concrete object (Type A). This factory method is injected in another package where I do not want to use A but an Interface.

Then you can write a wrapper function around it that conforms to the factory interface.

Hi Jakob. Thank you for your input.

I know that I could do this :slight_smile: But I have the interface in package A where I use the factory and the factory itself in package B while di takes place in package C. And A should not know anything about the implementation within package B.

But it seems to be not possible. Maybe sth. else comes up.

Maybe you can have *I as a function parameter and then pass *S to it and return nothing or error.

Maybe you have too many packages. But, as I understand it, you have a package A that defines and uses the factory interface.

package a

type I interface ...

type Factory func() I

func UseFactory(f Factory) { ... }

You have a package B that is the actual factory thing.

package b

type Thing ... // implements a.I

func MyThingFactory() Thing { ... } // you'd like this to be accepted as a.Factory

And you have a package C that does dependency injection.

package c

import ("a", "b")

func main() {
  a.UseFactory(b.MyThingFactory) // what you want to do, does not compile
}

So:

package c

import ("a", "b")

func main() {
  a.UseFactory(adapt(b.MyThingFactory)) // this compiles
}

func adapt(fn func() b.Thing) func a.I {
    return func() a.I {
        return fn()
    }
}

(barring typos etc, I haven’t actually tried to compile anything)

Thank you for this detailed instruction. This is a good idea indeed. But then c needs to know about a.I which I would like to avoid.

The intention behind this is to have internal interfaces specified where there are used at the end. They are just for the internal package usage. They are even not exported.

To make it more visible: We have a multi tenancy application which provides one database per tenant. I implemented a store which has methods like CreateUser, UpdateUser etc. There are many of these methods which are all implemented by a type Store in the package store.

In the http handler I only want to provide methods via interface that are needed. so there is an interface http.createUserStore (just an example) which contains only the method CreateUser. That interface is not exported.

For the multi tenancy I have a factory method which gives me a store (contains the database connection) by tenant. The implementation looks like:

func DatabaseStoreFactory(tenantID models.ID) *store.Store {
    ...
}

store package:

package store

type Store struct {
   database sth....
    ...
}

func  (s *Store) CreateUser(...) ... {
    ...
}

func  (s *Store) SuperManyOtherFunctions(...) ... {
    ...
}

in the http package it looks like this:

package http
...
type createUserStore interface {
    CreateUser(...) ...
}

type storeFactory func(tenantID models.ID) createUserStore

func NewHttpHandler(..., storeFactory storeFactory, ...) {
   ...

And then there is the di:

package di

http.NewHttpHandler(..., store.DatabaseStoreFactory, ...)

And this is what I wanted to achieve. Except the http package no one knows about the createUserStore interface.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.