mirror of
https://github.com/teivah/100-go-mistakes.git
synced 2026-06-20 16:45:56 +08:00
Interface pollution
This commit is contained in:
parent
26fd68e02c
commit
1af0884ef4
28 changed files with 1975 additions and 44 deletions
BIN
.cache/plugin/social/bd6ca84b050596902d3a4ec22259de0b.png
Normal file
BIN
.cache/plugin/social/bd6ca84b050596902d3a4ec22259de0b.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
257
docs/5-interface-pollution.md
Normal file
257
docs/5-interface-pollution.md
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
---
|
||||
title: Interface pollution (#5)
|
||||
comments: true
|
||||
hide:
|
||||
- toc
|
||||
---
|
||||
|
||||
# Interface pollution
|
||||
|
||||
Interfaces are one of the cornerstones of the Go language when designing and structuring our code. However, like many tools or concepts, abusing them is generally not a good idea. Interface pollution is about overwhelming our code with unnecessary abstractions, making it harder to understand. It’s a common mistake made by developers coming from another language with different habits. Before delving into the topic, let’s refresh our minds about Go’s interfaces. Then, we will see when it’s appropriate to use interfaces and when it may be considered pollution.
|
||||
|
||||
## Concepts
|
||||
|
||||
An interface provides a way to specify the behavior of an object. We use interfaces to create common abstractions that multiple objects can implement. What makes Go interfaces so different is that they are satisfied implicitly. There is no explicit keyword like `implements` to mark that an object X implements interface Y.
|
||||
|
||||
To understand what makes interfaces so powerful, we will dig into two popular ones from the standard library: `io.Reader` and `io.Writer`. The `io` package provides abstractions for I/O primitives. Among these abstractions, `io.Reader` relates to reading data from a data source and `io.Writer` to writing data to a target, as represented in the next figure:
|
||||
|
||||
<figure markdown>
|
||||

|
||||
</figure>
|
||||
|
||||
The `io.Reader` contains a single Read method:
|
||||
|
||||
```go
|
||||
type Reader interface {
|
||||
Read(p []byte) (n int, err error)
|
||||
}
|
||||
```
|
||||
|
||||
Custom implementations of the `io.Reader` interface should accept a slice of bytes, filling it with its data and returning either the number of bytes read or an error.
|
||||
|
||||
On the other hand, `io.Writer` defines a single method, Write:
|
||||
|
||||
```go
|
||||
type Writer interface {
|
||||
Write(p []byte) (n int, err error)
|
||||
}
|
||||
```
|
||||
|
||||
Custom implementations of `io.Writer` should write the data coming from a slice to a target and return either the number of bytes written or an error. Therefore, both interfaces provide fundamental abstractions:
|
||||
|
||||
* `io.Reader` reads data from a source
|
||||
* `io.Writer` writes data to a target
|
||||
|
||||
What is the rationale for having these two interfaces in the language? What is the point of creating these abstractions?
|
||||
|
||||
Let’s assume we need to implement a function that should copy the content of one file to another. We could create a specific function that would take as input two `*os.Files`. Or, we can choose to create a more generic function using `io.Reader` and `io.Writer` abstractions:
|
||||
|
||||
```go
|
||||
func copySourceToDest(source io.Reader, dest io.Writer) error {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
This function would work with `*os.File` parameters (as `*os.File` implements both `io.Reader` and `io.Writer`) and any other type that would implement these interfaces. For example, we could create our own `io.Writer` that writes to a database, and the code would remain the same. It increases the genericity of the function; hence, its reusability.
|
||||
|
||||
Furthermore, writing a unit test for this function is easier because, instead of having to handle files, we can use the `strings` and `bytes` packages that provide helpful implementations:
|
||||
|
||||
```go
|
||||
func TestCopySourceToDest(t *testing.T) {
|
||||
const input = "foo"
|
||||
source := strings.NewReader(input) // Creates an io.Reader
|
||||
dest := bytes.NewBuffer(make([]byte, 0)) // Creates an io.Writer
|
||||
|
||||
err := copySourceToDest(source, dest) // Calls copySourceToDest from a *strings.Reader and a *bytes.Buffer
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
got := dest.String()
|
||||
if got != input {
|
||||
t.Errorf("expected: %s, got: %s", input, got)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In the example, source is a `*strings.Reader`, whereas dest is a `*bytes.Buffer`. Here, we test the behavior of `copySourceToDest` without creating any files.
|
||||
|
||||
While designing interfaces, the granularity (how many methods the interface contains) is also something to keep in mind. A [known proverb](https://www.youtube.com/watch?v=PAAkCSZUG1c&t=318s) in Go relates to how big an interface should be:
|
||||
|
||||
!!! quote "Rob Pike"
|
||||
|
||||
The bigger the interface, the weaker the abstraction.
|
||||
|
||||
Indeed, adding methods to an interface can decrease its level of reusability. `io.Reader` and `io.Writer` are powerful abstractions because they cannot get any simpler. Furthermore, we can also combine fine-grained interfaces to create higher-level abstractions. This is the case with `io.ReadWriter`, which combines the reader and writer behaviors:
|
||||
|
||||
```go
|
||||
type ReadWriter interface {
|
||||
Reader
|
||||
Writer
|
||||
}
|
||||
```
|
||||
|
||||
???+ note
|
||||
|
||||
As Einstein said, “_Everything should be made as simple as possible, but no simpler._” Applied to interfaces, this denotes that finding the perfect granularity for an interface isn’t necessarily a straightforward process.
|
||||
|
||||
Let’s now discuss common cases where interfaces are recommended.
|
||||
|
||||
## When to use interfaces
|
||||
|
||||
When should we create interfaces in Go? Let’s look at three concrete use cases where interfaces are usually considered to bring value. Note that the goal isn’t to be exhaustive because the more cases we add, the more they would depend on the context. However, these three cases should give us a general idea:
|
||||
|
||||
* Common behavior
|
||||
* Decoupling
|
||||
* Restricting behavior
|
||||
|
||||
### Common behavior
|
||||
|
||||
The first option we will discuss is to use interfaces when multiple types implement a common behavior. In such a case, we can factor out the behavior inside an interface. If we look at the standard library, we can find many examples of such a use case. For example, sorting a collection can be factored out via three methods:
|
||||
|
||||
* Retrieving the number of elements in the collection
|
||||
* Reporting whether one element must be sorted before another
|
||||
* Swapping two elements
|
||||
|
||||
Hence, the following interface was added to the `sort` package:
|
||||
|
||||
```go
|
||||
type Interface interface {
|
||||
Len() int // Number of elements
|
||||
Less(i, j int) bool // Checks two elements
|
||||
Swap(i, j int) // Swaps two elements
|
||||
}
|
||||
```
|
||||
|
||||
This interface has a strong potential for reusability because it encompasses the common behavior to sort any collection that is index-based.
|
||||
|
||||
Throughout the `sort` package, we can find dozens of implementations. If at some point we compute a collection of integers, for example, and we want to sort it, are we necessarily interested in the implementation type? Is it important whether the sorting algorithm is a merge sort or a quicksort? In many cases, we don’t care. Hence, the sorting behavior can be abstracted, and we can depend on the `sort.Interface`.
|
||||
|
||||
Finding the right abstraction to factor out a behavior can also bring many benefits. For example, the `sort` package provides utility functions that also rely on `sort.Interface`, such as checking whether a collection is already sorted. For instance:
|
||||
|
||||
```go
|
||||
func IsSorted(data Interface) bool {
|
||||
n := data.Len()
|
||||
for i := n - 1; i > 0; i-- {
|
||||
if data.Less(i, i-1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
```
|
||||
|
||||
Because `sort.Interface` is the right level of abstraction, it makes it highly valuable.
|
||||
|
||||
Let’s now see another main use case when using interfaces.
|
||||
|
||||
### Decoupling
|
||||
|
||||
Another important use case is about decoupling our code from an implementation. If we rely on an abstraction instead of a concrete implementation, the implementation itself can be replaced with another without even having to change our code. This is the Liskov Substitution Principle (the L in Robert C. Martin’s [SOLID](https://en.wikipedia.org/wiki/SOLID) design principles).
|
||||
|
||||
One benefit of decoupling can be related to unit testing. Let’s assume we want to implement a `CreateNewCustomer` method that creates a new customer and stores it. We decide to rely on the concrete implementation directly (let’s say a `mysql.Store` struct):
|
||||
|
||||
```go
|
||||
type CustomerService struct {
|
||||
store mysql.Store // Depends on the concrete implementation
|
||||
}
|
||||
|
||||
func (cs CustomerService) CreateNewCustomer(id string) error {
|
||||
customer := Customer{id: id}
|
||||
return cs.store.StoreCustomer(customer)
|
||||
}
|
||||
```
|
||||
|
||||
Now, what if we want to test this method? Because `customerService` relies on the actual implementation to store a `Customer`, we are obliged to test it through integration tests, which requires spinning up a MySQL instance (unless we use an alternative technique such as [`go-sqlmock`](https://github.com/DATA-DOG/go-sqlmock), but this isn’t the scope of this section). Although integration tests are helpful, that’s not always what we want to do. To give us more flexibility, we should decouple `CustomerService` from the actual implementation, which can be done via an interface like so:
|
||||
|
||||
```go
|
||||
type customerStorer interface { // Creates a storage abstraction
|
||||
StoreCustomer(Customer) error
|
||||
}
|
||||
|
||||
type CustomerService struct {
|
||||
storer customerStorer // Decouples CustomerService from the actual implementation
|
||||
}
|
||||
|
||||
func (cs CustomerService) CreateNewCustomer(id string) error {
|
||||
customer := Customer{id: id}
|
||||
return cs.storer.StoreCustomer(customer)
|
||||
}
|
||||
```
|
||||
|
||||
Because storing a customer is now done via an interface, this gives us more flexibility in how we want to test the method. For instance, we can:
|
||||
|
||||
* Use the concrete implementation via integration tests
|
||||
* Use a mock (or any kind of test double) via unit tests
|
||||
* Or both
|
||||
|
||||
Let’s now discuss another use case: to restrict a behavior.
|
||||
|
||||
### Restricting behavior
|
||||
|
||||
The last use case we will discuss can be pretty counterintuitive at first sight. It’s about restricting a type to a specific behavior. Let’s imagine we implement a custom configuration package to deal with dynamic configuration. We create a specific container for `int` configurations via an `IntConfig` struct that also exposes two methods: `Get` and `Set`. Here’s how that code would look:
|
||||
|
||||
```go
|
||||
type IntConfig struct {
|
||||
// ...
|
||||
}
|
||||
|
||||
func (c *IntConfig) Get() int {
|
||||
// Retrieve configuration
|
||||
}
|
||||
|
||||
func (c *IntConfig) Set(value int) {
|
||||
// Update configuration
|
||||
}
|
||||
```
|
||||
|
||||
Now, suppose we receive an `IntConfig` that holds some specific configuration, such as a threshold. Yet, in our code, we are only interested in retrieving the configuration value, and we want to prevent updating it. How can we enforce that, semantically, this configuration is read-only, if we don’t want to change our configuration package? By creating an abstraction that restricts the behavior to retrieving only a config value:
|
||||
|
||||
```go
|
||||
type intConfigGetter interface {
|
||||
Get() int
|
||||
}
|
||||
```
|
||||
|
||||
Then, in our code, we can rely on `intConfigGetter` instead of the concrete implementation:
|
||||
|
||||
```go
|
||||
type Foo struct {
|
||||
threshold intConfigGetter
|
||||
}
|
||||
|
||||
func NewFoo(threshold intConfigGetter) Foo { // Injects the configuration getter
|
||||
return Foo{threshold: threshold}
|
||||
}
|
||||
|
||||
func (f Foo) Bar() {
|
||||
threshold := f.threshold.Get() // Reads the configuration
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
In this example, the configuration getter is injected into the `NewFoo` factory method. It doesn’t impact a client of this function because it can still pass an `IntConfig` struct as it implements `intConfigGetter`. Then, we can only read the configuration in the `Bar` method, not modify it. Therefore, we can also use interfaces to restrict a type to a specific behavior for various reasons, such as semantics enforcement.
|
||||
|
||||
In this section, we saw three potential use cases where interfaces are generally considered as bringing value: factoring out a common behavior, creating some decoupling, and restricting a type to a certain behavior. Again, this list isn’t exhaustive, but it should give us a general understanding of when interfaces are helpful in Go.
|
||||
|
||||
Now, let’s finish this section and discuss the problems with interface pollution.
|
||||
|
||||
## Interface pollution
|
||||
|
||||
It’s fairly common to see interfaces being overused in Go projects. Perhaps the developer’s background was C# or Java, and they found it natural to create interfaces before concrete types. However, this isn’t how things should work in Go.
|
||||
|
||||
As we discussed, interfaces are made to create abstractions. And the main caveat when programming meets abstractions is remembering that **abstractions should be discovered, not created**. What does this mean? It means we shouldn’t start creating abstractions in our code if there is no immediate reason to do so. We shouldn’t design with interfaces but wait for a concrete need. Said differently, we should create an interface when we need it, not when we foresee that we could need it.
|
||||
|
||||
What’s the main problem if we overuse interfaces? The answer is that they make the code flow more complex. Adding a useless level of indirection doesn’t bring any value; it creates a worthless abstraction making the code more difficult to read, understand, and reason about. If we don’t have a strong reason for adding an interface and it’s unclear how an interface makes a code better, we should challenge this interface’s purpose. Why not call the implementation directly?
|
||||
|
||||
???+ note
|
||||
|
||||
We may also experience performance overhead when calling a method through an interface. It requires a lookup in a hash table’s data structure to find the concrete type an interface points to. But this isn’t an issue in many contexts as the overhead is minimal.
|
||||
|
||||
In summary, we should be cautious when creating abstractions in our code—abstractions should be discovered, not created. It’s common for us, software developers, to overengineer our code by trying to guess what the perfect level of abstraction is, based on what we think we might need later. This process should be avoided because, in most cases, it pollutes our code with unnecessary abstractions, making it more complex to read.
|
||||
|
||||
!!! quote "Rob Pike"
|
||||
|
||||
Don’t design with interfaces, discover them.
|
||||
|
||||
Let’s not try to solve a problem abstractly but solve what has to be solved now. Last, but not least, if it’s unclear how an interface makes the code better, we should probably consider removing it to make our code simpler.
|
||||
3
docs/img/ioreaderwriter.svg
Normal file
3
docs/img/ioreaderwriter.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 15 KiB |
|
|
@ -116,20 +116,7 @@ Remember that Go is a unique language designed for many characteristics, includi
|
|||
|
||||
Abstractions should be discovered, not created. To prevent unnecessary complexity, create an interface when you need it and not when you foresee needing it, or if you can at least prove the abstraction to be a valid one.
|
||||
|
||||
An interface provides a way to specify the behavior of an object. We use interfaces to create common abstractions that multiple objects can implement. What makes Go interfaces so different is that they are satisfied implicitly. There is no explicit keyword like `implements` to mark that an object `X` implements interface `Y`.
|
||||
|
||||
In general, we can define three main use cases where interfaces are generally considered as bringing value: factoring out a common behavior, creating some decoupling, and restricting a type to a certain behavior. Yet, this list isn't exhaustive and will also depend on the context we face.
|
||||
|
||||
In many occasions, interfaces are made to create abstractions. And the main caveat when programming meets abstractions is remembering that abstractions should be discovered, not created. What does this mean? It means we shouldn’t start creating abstractions in our code if there is no immediate reason to do so. We shouldn’t design with interfaces but wait for a concrete need. Said differently, we should create an interface when we need it, not when we foresee that we could need it.
|
||||
What’s the main problem if we overuse interfaces? The answer is that they make the code flow more complex. Adding a useless level of indirection doesn’t bring any value; it creates a worthless abstraction making the code more difficult to read, understand, and reason about. If we don’t have a strong reason for adding an interface and it’s unclear how an interface makes a code better, we should challenge this interface’s purpose. Why not call the implementation directly?
|
||||
|
||||
We should be cautious when creating abstractions in our code (abstractions should be discovered, not created). It’s common for us, software developers, to overengineer our code by trying to guess what the perfect level of abstraction is, based on what we think we might need later. This process should be avoided because, in most cases, it pollutes our code with unnecessary abstractions, making it more complex to read.
|
||||
|
||||
!!! quote "Rob Pike"
|
||||
|
||||
Don’t design with interfaces, discover them.
|
||||
|
||||
Let’s not try to solve a problem abstractly but solve what has to be solved now. Last, but not least, if it’s unclear how an interface makes the code better, we should probably consider removing it to make our code simpler.
|
||||
Read the full section [here](5-interface-pollution.md).
|
||||
|
||||
[:simple-github: Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/5-interface-pollution/)
|
||||
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ nav:
|
|||
- Go Mistakes:
|
||||
- index.md
|
||||
- Full Sections:
|
||||
- 5-interface-pollution.md
|
||||
- 9-generics.md
|
||||
- 20-slice.md
|
||||
- 28-maps-memory-leaks.md
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block announce %}
|
||||
📢 A new section is now fully available: <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
{% endblock %}
|
||||
|
||||
{% block analytics %}
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@
|
|||
<div class="md-banner__inner md-grid md-typeset">
|
||||
|
||||
|
||||
📢 A new section is now fully available: <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
|
||||
</div>
|
||||
|
||||
|
|
@ -626,6 +626,27 @@
|
|||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../5-interface-pollution/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Interface pollution (#5)
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../9-generics/" class="md-nav__link">
|
||||
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@
|
|||
<div class="md-banner__inner md-grid md-typeset">
|
||||
|
||||
|
||||
📢 A new section is now fully available: <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
|
||||
</div>
|
||||
|
||||
|
|
@ -626,6 +626,27 @@
|
|||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../5-interface-pollution/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Interface pollution (#5)
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../9-generics/" class="md-nav__link">
|
||||
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@
|
|||
<div class="md-banner__inner md-grid md-typeset">
|
||||
|
||||
|
||||
📢 A new section is now fully available: <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
|
||||
</div>
|
||||
|
||||
|
|
@ -570,6 +570,27 @@
|
|||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="/5-interface-pollution/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Interface pollution (#5)
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="/9-generics/" class="md-nav__link">
|
||||
|
||||
|
|
|
|||
1348
site/5-interface-pollution/index.html
Normal file
1348
site/5-interface-pollution/index.html
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -121,7 +121,7 @@
|
|||
<div class="md-banner__inner md-grid md-typeset">
|
||||
|
||||
|
||||
📢 A new section is now fully available: <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
|
||||
</div>
|
||||
|
||||
|
|
@ -626,6 +626,27 @@
|
|||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../5-interface-pollution/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Interface pollution (#5)
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../9-generics/" class="md-nav__link">
|
||||
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@
|
|||
<div class="md-banner__inner md-grid md-typeset">
|
||||
|
||||
|
||||
📢 A new section is now fully available: <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
|
||||
</div>
|
||||
|
||||
|
|
@ -626,6 +626,27 @@
|
|||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../5-interface-pollution/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Interface pollution (#5)
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../9-generics/" class="md-nav__link">
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
<link rel="canonical" href="https://100go.co/9-generics/">
|
||||
|
||||
|
||||
<link rel="prev" href="..">
|
||||
<link rel="prev" href="../5-interface-pollution/">
|
||||
|
||||
|
||||
<link rel="next" href="../20-slice/">
|
||||
|
|
@ -121,7 +121,7 @@
|
|||
<div class="md-banner__inner md-grid md-typeset">
|
||||
|
||||
|
||||
📢 A new section is now fully available: <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
|
||||
</div>
|
||||
|
||||
|
|
@ -624,6 +624,27 @@
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../5-interface-pollution/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Interface pollution (#5)
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@
|
|||
<div class="md-banner__inner md-grid md-typeset">
|
||||
|
||||
|
||||
📢 A new section is now fully available: <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
|
||||
</div>
|
||||
|
||||
|
|
@ -626,6 +626,27 @@
|
|||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../5-interface-pollution/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Interface pollution (#5)
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../9-generics/" class="md-nav__link">
|
||||
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@
|
|||
<div class="md-banner__inner md-grid md-typeset">
|
||||
|
||||
|
||||
📢 A new section is now fully available: <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
|
||||
</div>
|
||||
|
||||
|
|
@ -626,6 +626,27 @@
|
|||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../5-interface-pollution/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Interface pollution (#5)
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../9-generics/" class="md-nav__link">
|
||||
|
||||
|
|
|
|||
BIN
site/assets/images/social/5-interface-pollution.png
Normal file
BIN
site/assets/images/social/5-interface-pollution.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
|
|
@ -119,7 +119,7 @@
|
|||
<div class="md-banner__inner md-grid md-typeset">
|
||||
|
||||
|
||||
📢 A new section is now fully available: <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
|
||||
</div>
|
||||
|
||||
|
|
@ -697,6 +697,27 @@
|
|||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../5-interface-pollution/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Interface pollution (#5)
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../9-generics/" class="md-nav__link">
|
||||
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@
|
|||
<div class="md-banner__inner md-grid md-typeset">
|
||||
|
||||
|
||||
📢 A new section is now fully available: <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
|
||||
</div>
|
||||
|
||||
|
|
@ -768,6 +768,27 @@
|
|||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../5-interface-pollution/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Interface pollution (#5)
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../9-generics/" class="md-nav__link">
|
||||
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@
|
|||
<div class="md-banner__inner md-grid md-typeset">
|
||||
|
||||
|
||||
📢 A new section is now fully available: <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
|
||||
</div>
|
||||
|
||||
|
|
@ -618,6 +618,27 @@
|
|||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../5-interface-pollution/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Interface pollution (#5)
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../9-generics/" class="md-nav__link">
|
||||
|
||||
|
|
|
|||
23
site/external/index.html
vendored
23
site/external/index.html
vendored
|
|
@ -121,7 +121,7 @@
|
|||
<div class="md-banner__inner md-grid md-typeset">
|
||||
|
||||
|
||||
📢 A new section is now fully available: <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
|
||||
</div>
|
||||
|
||||
|
|
@ -856,6 +856,27 @@
|
|||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../5-interface-pollution/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Interface pollution (#5)
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../9-generics/" class="md-nav__link">
|
||||
|
||||
|
|
|
|||
3
site/img/ioreaderwriter.svg
Normal file
3
site/img/ioreaderwriter.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 15 KiB |
|
|
@ -14,7 +14,7 @@
|
|||
<link rel="prev" href="external/">
|
||||
|
||||
|
||||
<link rel="next" href="9-generics/">
|
||||
<link rel="next" href="5-interface-pollution/">
|
||||
|
||||
|
||||
<link rel="icon" href="img/Go-Logo_LightBlue.svg">
|
||||
|
|
@ -121,7 +121,7 @@
|
|||
<div class="md-banner__inner md-grid md-typeset">
|
||||
|
||||
|
||||
📢 A new section is now fully available: <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
|
||||
</div>
|
||||
|
||||
|
|
@ -1828,6 +1828,27 @@
|
|||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="5-interface-pollution/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Interface pollution (#5)
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="9-generics/" class="md-nav__link">
|
||||
|
||||
|
|
@ -3297,16 +3318,7 @@
|
|||
<summary>TL;DR</summary>
|
||||
<p>Abstractions should be discovered, not created. To prevent unnecessary complexity, create an interface when you need it and not when you foresee needing it, or if you can at least prove the abstraction to be a valid one.</p>
|
||||
</details>
|
||||
<p>An interface provides a way to specify the behavior of an object. We use interfaces to create common abstractions that multiple objects can implement. What makes Go interfaces so different is that they are satisfied implicitly. There is no explicit keyword like <code>implements</code> to mark that an object <code>X</code> implements interface <code>Y</code>.</p>
|
||||
<p>In general, we can define three main use cases where interfaces are generally considered as bringing value: factoring out a common behavior, creating some decoupling, and restricting a type to a certain behavior. Yet, this list isn't exhaustive and will also depend on the context we face.</p>
|
||||
<p>In many occasions, interfaces are made to create abstractions. And the main caveat when programming meets abstractions is remembering that abstractions should be discovered, not created. What does this mean? It means we shouldn’t start creating abstractions in our code if there is no immediate reason to do so. We shouldn’t design with interfaces but wait for a concrete need. Said differently, we should create an interface when we need it, not when we foresee that we could need it.
|
||||
What’s the main problem if we overuse interfaces? The answer is that they make the code flow more complex. Adding a useless level of indirection doesn’t bring any value; it creates a worthless abstraction making the code more difficult to read, understand, and reason about. If we don’t have a strong reason for adding an interface and it’s unclear how an interface makes a code better, we should challenge this interface’s purpose. Why not call the implementation directly?</p>
|
||||
<p>We should be cautious when creating abstractions in our code (abstractions should be discovered, not created). It’s common for us, software developers, to overengineer our code by trying to guess what the perfect level of abstraction is, based on what we think we might need later. This process should be avoided because, in most cases, it pollutes our code with unnecessary abstractions, making it more complex to read.</p>
|
||||
<div class="admonition quote">
|
||||
<p class="admonition-title">Rob Pike</p>
|
||||
<p>Don’t design with interfaces, discover them.</p>
|
||||
</div>
|
||||
<p>Let’s not try to solve a problem abstractly but solve what has to be solved now. Last, but not least, if it’s unclear how an interface makes the code better, we should probably consider removing it to make our code simpler.</p>
|
||||
<p>Read the full section <a href="5-interface-pollution/">here</a>.</p>
|
||||
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/5-interface-pollution/"><span class="twemoji"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg></span> Source code</a></p>
|
||||
<h3 id="interface-on-the-producer-side-6">Interface on the producer side (#6)</h3>
|
||||
<details class="info" open="open">
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@
|
|||
<div class="md-banner__inner md-grid md-typeset">
|
||||
|
||||
|
||||
📢 A new section is now fully available: <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
|
||||
</div>
|
||||
|
||||
|
|
@ -612,6 +612,27 @@
|
|||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../5-interface-pollution/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Interface pollution (#5)
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../9-generics/" class="md-nav__link">
|
||||
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@
|
|||
<div class="md-banner__inner md-grid md-typeset">
|
||||
|
||||
|
||||
📢 A new section is now fully available: <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
|
||||
</div>
|
||||
|
||||
|
|
@ -614,6 +614,27 @@
|
|||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../5-interface-pollution/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Interface pollution (#5)
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../9-generics/" class="md-nav__link">
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -15,6 +15,11 @@
|
|||
<lastmod>2024-03-05</lastmod>
|
||||
<changefreq>daily</changefreq>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://100go.co/5-interface-pollution/</loc>
|
||||
<lastmod>2024-03-05</lastmod>
|
||||
<changefreq>daily</changefreq>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://100go.co/56-concurrency-faster/</loc>
|
||||
<lastmod>2024-03-05</lastmod>
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -117,7 +117,7 @@
|
|||
<div class="md-banner__inner md-grid md-typeset">
|
||||
|
||||
|
||||
📢 A new section is now fully available: <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
📢 Two new full sections are now available: <a href="https://100go.co/5-interface-pollution/">#5: Interface pollution</a> and <a href="https://100go.co/92-false-sharing/">#92: Writing concurrent code that leads to false sharing</a>.
|
||||
|
||||
</div>
|
||||
|
||||
|
|
@ -612,6 +612,27 @@
|
|||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../5-interface-pollution/" class="md-nav__link">
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Interface pollution (#5)
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../9-generics/" class="md-nav__link">
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue