Skip to content

Commit

Permalink
finished embedding block and added introduction
Browse files Browse the repository at this point in the history
  • Loading branch information
0x0FACED committed Sep 23, 2024
1 parent ecbcc04 commit fdf18e7
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 5 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Effective Go in Russian
# Effective Go Russian (Эффективный Go)

[Перевод](./effective_go_ru.md)
За переводом сюда ---> [Перевод](./effective_go_ru.md)

### Table of Contents

Expand Down Expand Up @@ -51,8 +51,8 @@
- Unused imports and variables
- Import for side effect
- Interface checks
13. Embedding **(IN PROGRESS)**
14. Concurrency
13. Embedding **(FINISHED)**
14. Concurrency **(IN PROGRESS)**
- Share by communicating
- Goroutines
- Channels
Expand Down
118 changes: 117 additions & 1 deletion effective_go_ru.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

- [Effective Go (Translated to Russian)](#effective-go-translated-to-russian)
- [Содержание](#содержание)
- [Введение](#введение)
- [Примеры](#примеры)
- [Форматирование](#форматирование)
- [Отступы](#отступы)
- [Длина строки](#длина-строки)
Expand Down Expand Up @@ -54,6 +56,19 @@
- [Неиспользуемые импорты и переменные](#неиспользуемые-импорты-и-переменные)
- [Импорт ради побочного эффекта](#импорт-ради-побочного-эффекта)
- [Проверка интерфейсов](#проверка-интерфейсов)
- [Встраивание (Embedding)](#встраивание-embedding)

## Введение

Go — это новый язык. Хотя он заимствует идеи из существующих языков, у него есть необычные свойства, которые делают эффективные программы на Go отличными по характеру от программ, написанных на его «родственных» языках. Прямой перевод программы на C++ или Java в Go вряд ли приведет к удовлетворительному результату — программы на Java пишутся для Java, а не для Go. С другой стороны, размышление о проблеме с точки зрения Go может привести к успешной, но совершенно иной программе. **Иными словами, чтобы писать на Go эффективно, важно понимать его особенности и идиомы**. **Также важно знать установленные соглашения по программированию на Go, такие как правила именования, форматирования, структуры программы и так далее, чтобы программы, которые вы пишете, были понятны другим программистам Go.**

Этот документ содержит советы по написанию ясного, идиоматичного кода на Go. Он дополняет спецификацию языка, «Tour of Go» и «How to Write Go Code», которые вы должны прочитать в первую очередь.

Примечание, добавленное в январе 2022 года: Этот документ был написан для релиза Go в 2009 году и с тех пор не обновлялся значительно. Хотя он остается хорошим руководством для понимания того, как использовать сам язык, благодаря его стабильности, в нем мало говорится о библиотеках и ничего о значительных изменениях в экосистеме Go, таких как система сборки, тестирование, модули и полиморфизм. Обновлять его не планируется, так как произошло много изменений, и обширный набор документов, блогов и книг хорошо описывает современные способы использования Go. **«Effective Go» по-прежнему полезен, но читателю следует понимать, что это далеко не полное руководство.** См. задачу 28782 для контекста.

### Примеры

Исходные коды пакетов Go предназначены не только для работы в качестве базовой библиотеки, но и как примеры того, как использовать язык. Более того, многие пакеты содержат рабочие, автономные исполняемые примеры, которые можно запустить прямо с веб-сайта `go.dev`, как этот (при необходимости, нажмите на слово «Example», чтобы открыть его). Если у вас есть вопрос о том, как подойти к решению проблемы или как может быть реализовано что-то, документация, код и примеры в библиотеке могут предоставить ответы, идеи и бэкграунд.

## Форматирование

Expand Down Expand Up @@ -2011,4 +2026,105 @@ $ go run main.go
var _ json.Marshaler = (*CustomData)(nil)
```
***Именно в ней и будет ошибка.***
***Именно в ней и будет ошибка.***
## Встраивание (Embedding)
Go не предоставляет типичное, основанное на типах, понятие наследования, но в нем есть возможность «заимствовать» части реализации, встраивая типы в структуры или интерфейсы.
Встраивание интерфейсов очень простое. Мы уже упоминали интерфейсы `io.Reader` и `io.Writer`; вот их определения:
```go
type Reader interface {
Read(p []byte) (n int, err error)
}

type Writer interface {
Write(p []byte) (n int, err error)
}
```
Пакет `io` также экспортирует несколько других интерфейсов, которые определяют объекты, способные реализовать несколько таких методов. Например, существует интерфейс `io.ReadWriter`, который включает методы Read и Write. Мы могли бы явно указать методы, составляющие `io.ReadWriter`, но проще и понятнее встраивать два интерфейса для создания нового, вот так:
```go
// ReadWriter — это интерфейс, объединяющий интерфейсы Reader и Writer.
type ReadWriter interface {
Reader
Writer
}
```
Это означает именно то, что и кажется: `ReadWriter` может выполнять функции как `Reader`, так и `Writer`; это объединение встроенных интерфейсов. Только интерфейсы могут быть встроены в другие интерфейсы.
Та же основная идея применима к структурам, но с более глубокими последствиями. Пакет `bufio` имеет две структуры: `bufio.Reader` и `bufio.Writer`, каждая из которых, разумеется, реализует соответствующие интерфейсы из пакета `io`. Пакет `bufio` также реализует буферизованный `Reader`/`Writer`, который объединяет `Reader` и `Writer` в одну структуру с помощью встраивания: типы перечислены внутри структуры, но им не присвоены имена полей.
```go
// ReadWriter хранит указатели на Reader и Writer.
// Он реализует io.ReadWriter.
type ReadWriter struct {
*Reader // *bufio.Reader
*Writer // *bufio.Writer
}
```
Встроенные элементы являются указателями на структуры и, конечно, должны быть инициализированы для указания на действительные структуры, прежде чем их можно будет использовать. Структуру `ReadWriter` можно было бы написать как:
```go
type ReadWriter struct {
reader *Reader
writer *Writer
}
```
Но тогда, чтобы «продвинуть» методы полей и удовлетворить интерфейсам `io`, нам пришлось бы предоставить методы пересылки, например:
```go
func (rw *ReadWriter) Read(p []byte) (n int, err error) {
return rw.reader.Read(p)
}
```
Встраивая структуры напрямую, мы избегаем этой работы. Методы встроенных типов автоматически становятся доступными, что означает, что `bufio.ReadWriter` не только имеет методы `bufio.Reader` и `bufio.Writer`, но и удовлетворяет всем трем интерфейсам: `io.Reader`, `io.Writer` и `io.ReadWriter`.
**Есть важное отличие встраивания от наследования**. **Когда мы встраиваем тип, его методы становятся методами внешнего типа, но при их вызове получателем метода является внутренний тип, а не внешний**. В нашем примере, когда вызывается метод `Read` у `bufio.ReadWriter`, это имеет такой же эффект, как если бы мы написали пересылающий метод: получателем будет поле `reader` структуры `ReadWriter`, а не сам `ReadWriter`.
Встраивание также может быть удобным инструментом. Этот пример показывает встроенное поле рядом с обычным именованным полем:
```go
type Job struct {
Command string
*log.Logger
}
```
Теперь тип `Job` имеет методы `Print`, `Printf`, `Println` и другие методы `*log.Logger`. **Конечно, мы могли бы дать `Logger` имя поля, но это не обязательно**. И теперь, после инициализации, мы можем вести логирование с помощью `Job`:
```go

job.Println("начинаю выполнение...")
```
`Logger` — это обычное поле структуры `Job`, поэтому мы можем инициализировать его обычным образом в конструкторе для `Job`, например так:
```go
func NewJob(command string, logger *log.Logger) *Job {
return &Job{command, logger}
}
```
или с помощью составного литерала:
```go
job := &Job{command, log.New(os.Stderr, "Job: ", log.Ldate)}
```
Если нам нужно напрямую обратиться к встроенному полю, имя типа этого поля, без квалификатора пакета, служит именем поля, как это было в методе `Read` нашей структуры `ReadWriter`. Здесь, если нам нужно получить доступ к *`log.Logger` переменной `job`, мы бы написали `job.Logger`, что было бы полезно, если бы мы хотели изменить методы `Logger`.
```go
func (job *Job) Printf(format string, args ...interface{}) {
job.Logger.Printf("%q: %s", job.Command, fmt.Sprintf(format, args...))
}
```
Встраивание типов может привести к конфликтам имен, но правила их разрешения просты. Во-первых, поле или метод `X` скрывает любой другой элемент `X`, находящийся глубже в структуре. Если бы в `log.Logger` было поле или метод с именем `Command`, поле `Command` в `Job` имело бы приоритет.
Во-вторых, если одно и то же имя появляется на одном уровне вложенности, это обычно ошибка; было бы ошибкой встраивать `log.Logger`, если структура `Job` содержала другое поле или метод с именем `Logger`. Однако, если дублируемое имя никогда не упоминается в программе за пределами определения типа, это допустимо. Это условие обеспечивает некоторую защиту от изменений типов, встроенных из внешних источников.

0 comments on commit fdf18e7

Please sign in to comment.