Preface

Encapsulation, as known as information hiding, is a key aspect of object-oriented programming. An object’s field or method is said to be encapsulated if it is inaccessible to users of the object. Unlike classical objected programming languages like Java, Go has very specific encapsulation rules. This blog is going to explore these “interesting” rules.

Encapsulation Rules in Go

Go has only one rule to set up encapsulation: capitalized identifiers are exported from the package where they are defined and un-capitalized ones are not. A field/method of a struct/interface is exported only when the names of the field/method and struct/interface are both capitalized (AND condition).

Encapsulation Example in Go
Encapsulation Example in Java
An Interesting Go Encapsulation Example
Enhance Encapsulation with Interfaces

Encapsulation in Internal Packages

With the above encapsulation rules, Go internal packages have an extra rule: “An import of a path containing the element “internal” is disallowed if the importing code is outside the tree rooted at the parent of the “internal” directory.” — Design Document of Go Internal Package

foo:    -> repo
cmd:
server:
main.go
internal:
module:
module1:
service:
service.go
internal:
repo:
repo.go
pkg:
pkg1:
code.go
pkg2:
code.go
pkg:
pkg1:
code.go
  • The deepest internal dominates encapsulation rules when there are multiple internals in a package's import path. For example, the packagefoo/internal/module1/service/internal/repo can only be imported by packages in the directory tree rooted at foo/internal/module1/service/ (other than foo/), which is only the package foo/internal/module1/service in this case.

When to Use Internal Packages

When to use internal packages? We only need to remember one rule: Define a package in the internal folder when you want it to be shared only among packages rooted at the parent of the "internal" directory. Take the above project layout of foo project (microservice) as an example, there are two typical use cases of internal packages:

  1. Define a package’s exclusive packages: the packagefoo/internal/module1/service/internal/repo can only be used by the package foo/internal/module1/service according to rules of internal packages and this makes sense in terms of domain-driven design. Normally a domain-driven "module" consists of three packages:api-server, service and repository and only service can access repository. In other words, the packageservice should own the repository package regarding design pattern and code organization. Therefore, in the example, it makes sense to define the packagerepository under the internal folder of the package foo/internal/module1/service.

Some Interesting Study cases

The mystery of Go encapsulation rules sometimes can make you confused and lost. For example, here are some “interesting” examples of Go encapsulation.

Public Data Structs with Private Fields.

Private Interface with Private Methods.

Private Data Structs with Public Fields.

Private Object Structs with Public Methods.

Summary

In summary, Go has the following encapsulation rules:

  • An import of a path containing the element “internal” is disallowed if the importing code is outside the tree rooted at the parent of the “internal” directory.
  • A field must be capitalized if it wants to be JSON marshal/unmarshal, no matter whether the struct it belongs to is capitalized or not.

Reference

A software engineer