From a783270b09af3d873c08a01d13f802018b69fb02 Mon Sep 17 00:00:00 2001 From: a73x Date: Sun, 29 Dec 2024 19:13:27 +0000 Subject: new markdown renderer since TOC has a title now and it can compact toc headers, we can use header 2 for everything use the built in goldmark extension for syntax highlighting --- public/posts/2024-12-08-01.html | 82 ++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 42 deletions(-) (limited to 'public/posts/2024-12-08-01.html') diff --git a/public/posts/2024-12-08-01.html b/public/posts/2024-12-08-01.html index a32f539..2631b69 100644 --- a/public/posts/2024-12-08-01.html +++ b/public/posts/2024-12-08-01.html @@ -25,72 +25,70 @@ +
← Posts

Simplifying Interfaces with Function Types

+

Table of Contents

+

In Go, you can define methods on type aliases, which means that we can define a type alias of a function, and then define methods on that function.

-

Example

-

Given the following interface

-
type DB interface {
-	Get(string) (string, error)
-}
-
-

You can fulfill it using a function type like this:

-
type GetFn func(string) (string, error)
-
-func (f GetFn) Get(a string) (string, error) {
-	return f(a)
-}
-
-

Now you can use GetFn whenever a DB is required:

-
func main() {
-	var storeFn DB = GetFn(func(s string) (string, error) {
-		return "bar", nil
-	})
-	fmt.Println(storeFn.Get("Foo")) // Outputs: bar
-}
-
-

You can try this example in this [Go Playground](https://go.dev/play/p/hyBNIMblafs

+
type DB interface {
+	Get(string) (string, error)
+}
+

You can fulfill it using a function type like this:

+
type GetFn func(string) (string, error)
 
+func (f GetFn) Get(a string) (string, error) {
+	return f(a)
+}
+

Now you can use GetFn whenever a DB is required:

+
func main() {
+	var storeFn DB = GetFn(func(s string) (string, error) {
+		return "bar", nil
+	})
+	fmt.Println(storeFn.Get("Foo")) // Outputs: bar
+}
+

You can try this example in this [Go Playground](https://go.dev/play/p/hyBNIMblafs

How it works

-

In Go, interfaces are implicitly through method sets, which means any type (including a function type) that defines the required methods satisfies the interface. By defining the Get method on the GetFn type, the compiler treats GetFn as a valid implementation of the DB interface.

-

This flexibility allows you to use function types as lightweight, dynamic implementations of interfaces, without the need for full struct definitions.

-

Application

-

One common use case for this pattern is testing. Instead of implementing a full mock, you can use an inline function to provide test specific behavior.

-
func TestHandler() {
-    mockDB := GetFn(func(key string) (string, error) {
-        if key == "foo" {
-            return "bar", nil
-        }
-        return "", fmt.Errorf("not found")
-    })
-
-    result, err := mockDB.Get("foo")
-    fmt.Println(result, err) // Outputs: bar, <nil>
-}
-
-

This approach is not limited to testing. It’s also useful for dependency injection, where you can pass in lightweight or context specific implementations of an interface.

+
func TestHandler() {
+    mockDB := GetFn(func(key string) (string, error) {
+        if key == "foo" {
+            return "bar", nil
+        }
+        return "", fmt.Errorf("not found")
+    })
 
+    result, err := mockDB.Get("foo")
+    fmt.Println(result, err) // Outputs: bar, <nil>
+}
+

This approach is not limited to testing. It’s also useful for dependency injection, where you can pass in lightweight or context specific implementations of an interface.

This pattern is similar to how http.HandleFunc works. In the HTTP package, http.HandlerFunc is a function type that fulfills the http.Handler interface by implementing its ServeHTTP method. This allows functions to act as handlers, providing great flexibility in designing web servers.

-- cgit v1.2.3