# Объектно-ориентированное программирование в Golang

Давайте поучимся работать с объектно-ориентированной архитектурой в Golang. Здесь нет классов, зато есть структуры, работа с которыми является единственным способом поддержки объектно-ориентированной модели.

#### Создание структур в Golang

Структуры могут использоваться для представления сложного объекта, состоящего из нескольких пар «ключ — значение».

Обратимся к конкретному примеру. Допустим, нам надо представить в качестве объекта сотрудника нашей организации. Для этого понадобится комбинация пар «ключ — значение» со всеми данными о сотруднике. Объект, представляющий сотрудника, может быть составлен из нескольких ключей/параметров, например, `имени`, `возраста`, `должности` и `оклада`. Все эти атрибуты или свойства в совокупности представляют сотрудника организации.

Теперь создадим простую структуру сотрудника `Employee` с основными его параметрами.

```
type Employee struct {
  Name string
  Age int
  Designation string
  Salary int
}
```

В этом коде можно выделить следующие стандартные блоки:

1. Ключевое слово `type`, которое используется для определения нового `типа` в Golang.
2. `Employee` передан в качестве имени создаваемой структуры.
3. Ключевое слово `struct` указывает на тип данных — структура.
4. И, наконец, в структуру добавляются параметры вместе с их типами.

В Golang предусмотрена строгая типобезопасность, поэтому типы параметров надо указывать во время объявления `структуры`. Присвоение значения любого другого типа в этих параметрах чревато ошибками, которые обнаружит компилятор.

#### Создание объектов из структуры

Теперь, когда у нас есть новая структура, можно создавать из неё объекты. Давайте подробно разберём, как это делать на примере уже определённой нами структуры (`Employee`). Существует несколько способов создания объектов, и мы рассмотрим разные возможные сценарии со всеми их преимуществами и недостатками.

**Передача в структуру значений, разделённых запятыми**

Это самый простой способ создания объектов: все необходимые значения передаются в структуру в виде значений, разделённых запятыми. Эти значения должны следовать в том же порядке, в котором они указаны при объявлении структуры.

```
type Employee struct {
  Name string
  Age int
  Designation string
  Salary int
}

var newEmployee = Employee{"Mayank", 30, "Developer", 40}
```

В этом коде мы создаём объект структуры `Employee` (как видите, значения разделены запятыми), причём объект этот присваивается переменной `newEmployee`.

**Проблемы, которые здесь возникают:**

1. Во время создания объекта приходится держать в голове все параметры да ещё и в нужном порядке.
2. В структуру надо передавать все значения.

**Передача пар «ключ — значение»**

Решаем эти проблемы, передавая пары «ключ — значение» во время объявления.

Что это нам даёт?

1. Во-первых, больше не нужно следить за порядком следования значений.
2. Во-вторых, не нужно указывать все пары «ключ — значение».

```
type Employee struct {
  Name string
  Age int
  Designation string
  Salary int
}

var newEmployee = Employee{Name: "Mayank", Age: 40, Designation: "Developer"}

var otherEmployee = Employee{Designation: "Developer", Name: "Mayank", Age: 40}
```

Видите? Мы передаём пары «ключ — значение» в произвольном порядке и даже пропускаем некоторые параметры. Тем не менее структура создаётся!

**Стандартные значения для недостающих параметров**

При создании объекта `newEmployee` мы пропустили одну из пар «ключ — значение» (`Salary`). Golang по умолчанию добавляет к параметру `Salary` некое стандартное значение. Каким же именно будет это стандартное значение? А это зависит от типа параметра. В соответствии с типом параметра задаются следующие стандартные значения данных:

* Значение целочисленного типа `int` задаётся равным `0`.
* Значения строкового типа `string` оставляются пустыми `“”` (пустая строка).
* Значениям логического типа `bool` по умолчанию задаётся `false`.

В нашем случае параметру `Salary` соответствует целочисленный тип `int`, поэтому стандартным значением этого параметра будет `0`. Теперь давайте посмотрим, как получить доступ к этим значениям параметра, используя объект `struct`.

```
type Employee struct {
  Name string
  Age int
  Designation string
  Salary int
}

var newEmployee = Employee{Name: "Mayank", Age: 40, Designation: "Developer"}

// Обращение к параметрам «имени»...
fmt.Println(newEmployee.Name)
```

**Ссылка на объект или значение объекта?**

Ещё нам важно в создании `структуры` определить, представляет ли параметр `newEmployee` тип значения или ссылочный тип. Объект, возвращаемый нам при объявлении, приносит не ссылки, а значение объекта. `newEmployee` не указывает на адрес в памяти.

Если этот объект в качестве параметра передаётся какой-то другой функции, мы даём вызываемой функции значения объекта. Исходный объект копируется, и новый объект данных присваивается параметру вызывающей функции. Объект не передаётся как ссылка. А раз так, никакие изменения копированного объекта не дублируются в исходном. Рассмотрим пример:

```
func UpdateEmployee(empDetials Employee) {
  empDetails.Name = "Anshul";
}

var newEmployee = Employee{Name: "Mayank", Age: 40, Designation: "Developer"}

UpdateEmployee(newEmployee)

fmt.Println(newEmployee.Name)
```

Здесь даже при обновлении значений в функции `UpdateEmployee` никакого влияния на исходный объект `newEmployee` не оказывается, ведь объект мы передали не как ссылку, а как значение.

**Передача объекта как ссылки**

Итак, мы увидели, что обновления объекта никак не отразятся на функции, потому что в функцию не была передана ссылка. Для передачи объекта как ссылки можно вместо значений передать ссылку на объект: просто ставим в начале оператор взятия адреса `&`. Чтобы принялась ссылка на объект, а не значение, вызываемую функцию тоже надо обновить.

```
func UpdateEmployee(empDetials *Employee) {
  empDetails.Name = "Anshul";
}

var newEmployee = Employee{Name: "Mayank", Age: 40, Designation: "Developer"}

UpdateEmployee(&newEmployee)

// Исходный объект отправлен в обновлённую функцию...
fmt.Println(newEmployee.Name)
```

Здесь функцию (и вызов функции) обновили, чтобы можно было отправлять и получать не значение объекта, а адрес в памяти. Теперь, если обновятся данные в вызываемой функции, то же самое произойдёт и в исходном объекте данных.

В чём проблема этого подхода? В том, что приходится использовать оператор взятия адреса, чтобы вытащить адрес объекта и отправить его в функцию.

#### Использование ключевого слова `new` для создания объектов

Следующий способ создания объекта из структуры — использовать ключевое слово `new`. При этом Golang создаёт новый объект типа `struct` и возвращает переменной его адрес в памяти. То есть, ключевое слово `new` возвращает адрес этого объекта.

Используя ссылку, возвращаемую от ключевого слова `new`, можно присваивать значения параметрам вновь созданного объекта. Все параметры по умолчанию принимают неявные значения, и для каждого конкретного типа это будет своё стандартное значение.

1. Стандартным значением для булева типа будет `false`.
2. Стандартным значением для целочисленного типа будет `0`.
3. Стандартным значением для строкового типа будет `“”` (пустая строка).

Для лучшего понимания снова обратимся к коду:

```
type Employee struct {
  Name string
  Age int
  Designation string
  Salary int
}

var newEmployee = new(Employee)

fmt.Println(newEmployee.Name)
```

Мы создаём новый объект с помощью ключевого слова `new`, которое не позволяет передавать параметрам объекта стандартные значения. При создании объекта это делает за нас Golang. В нашем случае обращение к параметру `name` вернёт пустую строку (`“”`).

В коде с помощью ключевого слова `new` возвращается адрес вновь созданного объекта. Переменная `newEmployee` выступает здесь в роли указателя на объект `Employee`.

**Передача объекта в функцию**

При создании объекта с использованием ключевого слова `new` нам возвращается адрес объекта. Поэтому, если мы передаём в функцию в качестве параметра объект, то отправляется ссылка на объект. Любые изменения в параметре входного объекта будут отражаться на исходном объекте.

```
func UpdateEmployee(empDetials *Employee) {
  empDetails.Name = "Anshul";
}

var newEmployee = new(Employee)

newEmployee.Name = "Mayank"
newEmployee.Age = 30

UpdateEmployee(newEmployee)

fmt.Println(newEmployee.Name)
```

Здесь от ключевого слова `new` возвращается ссылка на объект. А значит, что при вызове функции нет необходимости использовать `&` для отправки ссылки на объект.

#### Добавление функции в структуру

Структура не только определяет свойства, относящиеся к объекту, она представляет поведение объекта. Попробуем понять, как это возможно, добавив в структуру функции. Код для этой операции в Golang довольно-таки своеобразный.

Пример:

```
type Employee struct {
  Name string
  Age int
  Designation string
  Salary int
}

func (emp Employee) ShowDetails() {
  fmt.Println("User Name: ", emp.Name)
}
```

Здесь мы добавляем новую функцию, которая бы имела привязку к структуре `Employee`. То есть чётко привязываем функцию к структуре. Функция определена и может принимать ссылку на созданный объект с помощью `emp`.

Посмотрите, как в Golang вызывается добавленная к структуре функция.

```
type Employee struct {
  Name string
  Age int
  Designation string
  Salary int
}

func (emp Employee) ShowDetails() {
  fmt.Println("User Name: ", emp.Name)
}

var newEmployee = new(Employee)
newEmployee.Name = "Mayank"

newEmployee.ShowDetails()
```

Ссылки:

<https://nuancesprog.ru/p/6524/>
