Нияз Хадимуллин | Ментор по GO – Telegram
Нияз Хадимуллин | Ментор по GO
1.19K subscribers
130 photos
1 video
35 links
Авторский канал ментора Нияза про Go, базы данных и разработку

Если хочешь записаться на моё менторство и начать получать офферы, не стесняйся писать мне https://mentor-niyaz.ru
Download Telegram
🛠️ KISS (Keep It Simple, Stupid)

Что такое KISS?

KISS — это принцип проектирования, который гласит, что системы должны быть максимально простыми. Чем проще решение, тем легче его поддерживать и развивать.

💡 Как применять KISS?
- Избегайте излишней сложности в коде.
- Разбивайте задачи на более мелкие и простые части.
- Используйте понятные и прямолинейные решения.

⚠️ Пример:
Вместо сложного алгоритма с множеством условий используйте простой и понятный код:

func sum(a, b int) int {
return a + b
}


🎯 KISS — это ключ к созданию поддерживаемого и надежного кода.
👍3431🔥30
🛠️ YAGNI (You Aren’t Gonna Need It)

Что такое YAGNI?

YAGNI — это принцип, который утверждает, что не следует добавлять функциональность, пока она действительно не понадобится. Это помогает избежать переусложнения и избыточного кода.

💡 Как применять YAGNI?
- Не добавляйте код "на будущее".
- Реализуйте только то, что требуется прямо сейчас.
- Рефакторите код, когда появляются новые требования.

⚠️ Пример:
Не создавайте сложную систему логирования, если достаточно простого вывода в консоль:

fmt.Println("Error:", err)


🎯 YAGNI — это принцип, который помогает сосредоточиться на важном и избежать избыточности.
👍60🔥3123
🛠️ GRASP (General Responsibility Assignment Software Patterns)

Что такое GRASP?

GRASP — это набор принципов для распределения ответственностей между классами и объектами в объектно-ориентированном проектировании. Основные принципы включают:
- Информационный эксперт (Information Expert).
- Создатель (Creator).
- Контроллер (Controller).
- Низкая связанность (Low Coupling).
- Высокая связность (High Cohesion).

💡 Как применять GRASP?
- Информационный эксперт: Назначайте ответственность классу, который владеет необходимой информацией.
- Низкая связанность: Минимизируйте зависимости между классами.
- Высокая связность: Классы должны выполнять одну четкую задачу.

⚠️ Пример:
Если класс Order содержит информацию о заказе, он должен отвечать за расчет стоимости:

type Order struct {
Items []Item
}

func (o Order) CalculateTotal() float64 {
total := 0.0
for _, item := range o.Items {
total += item.Price
}
return total
}


🎯 GRASP — это набор принципов для создания качественного объектно-ориентированного дизайна.
🔥45👍3527
🛠️ Преобразование типов: строка в []byte и обратно в Go

Как преобразовывать строки и байты в Go?

В Go строки и срезы байт (`[]byte`) тесно связаны, так как строка — это неизменяемая последовательность байт. Преобразование между ними выполняется просто.

⚠️ Пример использования:
package main

import "fmt"

func main() {
s := "Go"
b := []byte(s) // Строка -> []byte
fmt.Println(b) // [71 111]

s2 := string(b) // []byte -> строка
fmt.Println(s2) // Go
}


Почему это важно?
- Работа с данными: Преобразование между строками и байтами часто требуется при работе с файлами, сетью или шифрованием.
- Эффективность: Преобразование выполняется быстро и без накладных расходов.

🎯 Преобразование строк и байтов — это базовая операция в Go, которая часто используется в реальных приложениях.
🔥3822👍20
🛠️ Операторы `break` и `continue` в Go

Что такое `break` и `continue`?

- `break`: Прерывает выполнение цикла (for, switch, select).
- `continue`: Пропускает текущую итерацию цикла и переходит к следующей.

💡 Как использовать `break` и `continue`?
- `break`:
  for i := 0; i < 10; i++ {
if i == 5 {
break // Выход из цикла при i == 5
}
fmt.Println(i)
}

- `continue`:
  for i := 0; i < 10; i++ {
if i%2 == 0 {
continue // Пропуск четных чисел
}
fmt.Println(i)
}


⚠️ Пример с вложенными циклами:
outer:
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
if i == 1 && j == 1 {
break outer // Выход из обоих циклов
}
fmt.Println(i, j)
}
}


Почему это важно?
- Управление выполнением: break и continue позволяют гибко управлять логикой циклов.
- Упрощение кода: Помогают избежать сложных условий и вложенных конструкций.

🎯 `break` и `continue` — это ключевые операторы для управления циклами в Go.
👍45🔥4328
🔐 X.509: Цифровые сертификаты

Что такое X.509?

X.509 — это стандарт инфраструктуры открытых ключей (PKI), который определяет формат цифровых сертификатов. Эти сертификаты используются для безопасной связи в интернете (HTTPS).

💡 Что содержит X.509 сертификат?
- Открытый ключ владельца
- Информация о владельце (DN - Distinguished Name)
- Срок действия
- Цифровая подпись удостоверяющего центра (CA)

Version: 3
Serial Number: 1234567890
Subject: CN=example.com, O=Example Inc, C=US
Issuer: CN=DigiCert Global Root CA
Valid From: 2023-01-01
Valid To: 2024-01-01
Public Key: RSA (2048 bits)


Почему это важно?
- Безопасность: Обеспечивает доверенную связь в интернете
- Аутентификация: Подтверждает подлинность веб-сайтов

🎯 X.509 — фундамент современной безопасности в интернете
👍30🔥2821
🔐 Web of Trust: Децентрализованное доверие

Что такое Web of Trust?

Web of Trust (WoT) — это концепция децентрализованного доверия, где пользователи сами подтверждают подлинность ключей друг друга. Популярна в PGP/GPG.

💡 Как работает WoT?
- Каждый пользователь создаёт пару ключей
- Пользователи подписывают ключи тех, кого знают лично
- Создаётся сеть доверительных отношений

⚠️ Пример подписи ключа в GPG:  # Подписать ключ
gpg --sign-key user@example.com

# Экспортировать подпись
gpg --export --armor user@example.com


Почему это важно?
- Децентрализация: Нет единой точки отказа
- Гибкость: Каждый определяет уровень доверия сам

🎯 WoT — альтернатива централизованным CA в определённых сценариях
🔥59👍5225
🔐 SSH: Безопасный доступ

Что такое SSH?

SSH (Secure Shell) — протокол для безопасного удалённого доступа к системам. Обеспечивает шифрование и аутентификацию.

💡 Ключевые особенности:
- Асимметричное шифрование для аутентификации
- Симметричное шифрование для передачи данных
- Проверка целостности сообщений

⚠️ Пример использования:  # Генерация ключевой пары
ssh-keygen -t ed25519 -C "user@example.com"

# Безопасное подключение
ssh user@server.com -p 22


Почему это важно?
- Безопасность: Защита от перехвата данных
- Универсальность: Стандарт для удалённого доступа

🎯 SSH — незаменимый инструмент для безопасного удалённого управления
👍6247🔥23
🔐 HMAC: Проверка целостности

Что такое HMAC?

HMAC (Hash-based Message Authentication Code) — это механизм проверки целостности данных, использующий криптографическую хеш-функцию и секретный ключ.

💡 Как работает HMAC?
- Комбинирует секретный ключ с данными
- Применяет хеш-функцию (SHA-256, SHA-3 и др.)
- Создаёт уникальный код аутентификации

func generateHMAC(message, key []byte) []byte {
mac := hmac.New(sha256.New, key)
mac.Write(message)
return mac.Sum(nil)
}


Почему это важно?
- Целостность: Гарантирует неизменность данных
- Аутентификация: Подтверждает источник данных

🎯 HMAC — стандарт для проверки целостности в криптографии
52🔥31👍23
🔐 Salt: Защита паролей

Что такое Salt?

Salt — это случайные данные, добавляемые к паролю перед хешированием для защиты от радужных таблиц и атак по словарю.

💡 Правила использования Salt:
- Уникальный для каждого пароля
- Достаточной длины (минимум 16 байт)
- Генерируется криптографически безопасно


⚠️ Пример на Go: import (
"crypto/rand"
"encoding/base64"
"golang.org/x/crypto/argon2"
)

func hashPassword(password string) (hash, salt []byte) {
// Генерируем соль
salt = make([]byte, 16)
rand.Read(salt)

// Хешируем пароль с солью
hash = argon2.IDKey(
[]byte(password),
salt,
1, // количество итераций
64*1024, // используемая память
4, // количество потоков
32, // длина выходного хеша
)

return hash, salt
}


Почему это важно?
- Уникальность: Одинаковые пароли дают разные хеши
- Безопасность: Защита от предварительно вычисленных атак

🎯 Salt — обязательный элемент современного хеширования паролей
👍7150🔥27
🏗️ Внедрение зависимостей (Dependency Injection)

Что такое Dependency Injection?

Dependency Injection (DI) — паттерн проектирования, который позволяет сделать код более модульным, тестируемым и гибким за счёт управления зависимостями извне.

💡 Ключевые концепции:
- Инверсия управления (IoC)
- Внедрение зависимостей через конструктор
- Слабая связанность компонентов
- Простота тестирования
- Возможность подмены реализаций
// Интерфейс репозитория
type UserRepository interface {
FindByID(id int) (*User, error)
Save(user *User) error
}

// Сервис, зависящий от репозитория
type UserService struct {
repo UserRepository
}

// Конструктор с внедрением зависимости
func NewUserService(repo UserRepository) *UserService {
return &UserService{repo: repo}
}

// Реальная имплементация репозитория
type PostgresUserRepository struct {
db *sql.DB
}

func (r *PostgresUserRepository) FindByID(id int) (*User, error) {
// Реализация поиска
}

// В тестах можно использовать мок
type MockUserRepository struct {
users map[int]*User
}

func (m *MockUserRepository) FindByID(id int) (*User, error) {
return m.users[id], nil
}

Почему это важно?
- Модульность: Легкая замена компонентов
- Тестируемость: Простое создание mock-объектов
- Гибкость: Независимость компонентов
- Расширяемость: Простое добавление новой функциональности

🎯 DI — ключ к созданию гибких и поддерживаемых систем
🔥94👍8837
🏗️ DDD: Проектирование сложных систем

Что такое DDD?

DDD (Domain-Driven Design) — методология разработки сложных программных систем, фокусирующаяся на бизнес-логике и моделировании предметной области.

💡 Ключевые концепции DDD:
- Сосредоточение на бизнес-правилах и логике предметной области
- Создание общего языка с заказчиком
- Декомпозиция сложной системы на изолированные bounded contexts
- Определение агрегатов, сущностей и доменных событий
- Явное выделение бизнес-логики от технической реализации

// Агрегат в DDD
type Order struct {
ID OrderID
Customer Customer
Items []OrderItem
Status OrderStatus
CreatedAt time.Time
}

// Бизнес-правила внутри агрегата
func (o *Order) AddItem(item Product, quantity int) error {
// Валидация бизнес-правил
if quantity <= 0 {
return errors.New("количество должно быть положительным")
}

if o.Status != StatusDraft {
return errors.New("нельзя добавлять товары в завершенный заказ")
}

// Сложная логика добавления товара
newItem := OrderItem{
Product: item,
Quantity: quantity,
TotalCost: item.Price * quantity,
}

o.Items = append(o.Items, newItem)
o.recalculateTotalCost()

return nil
}

// Доменное событие
type OrderCreatedEvent struct {
OrderID OrderID
CreatedAt time.Time
}


Почему это важно?
- Управление сложностью: Помогает структурировать сложные многослойные системы
- Коммуникация: Создаёт единый понятный язык между разработчиками и бизнесом
- Гибкость: Позволяет легко адаптировать систему к изменяющимся требованиям

🎯 DDD — методология для создания сложных, эволюционирующих систем с фокусом на бизнес-логике
49🔥45👍26
🏗️ Три слоя бизнес-логики

Что такое три слоя бизнес-логики?

Архитектурный подход, разделяющий приложение на три основных слоя для создания чистой, масштабируемой и поддерживаемой архитектуры.

💡 Характеристики слоёв:
- Слой представления (Presentation Layer):
- Отвечает за взаимодействие с пользователем
- Содержит контроллеры, обработчики HTTP-запросов
- Минимальная бизнес-логика
- Валидация входящих данных
- Преобразование DTO

- Слой бизнес-логики (Business Logic Layer):
- Реализация основных бизнес-процессов
- Сервисы и UseCase
- Сложная логика преобразования данных
- Транзакционное управление
- Бизнес-правила

- Слой данных (Data Access Layer):
- Работа с базами данных
- Репозитории
- ORM-операции
- Абстракция от конкретной базы данных

// Слой представления (Controller)
func CreateUserHandler(w http.ResponseWriter, r *http.Request) {
var dto UserDTO
// Валидация входных данных
if err := json.NewDecoder(r.Body).Decode(&dto); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}

userService := NewUserService()
user, err := userService.Create(dto)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

json.NewEncoder(w).Encode(user)
}

// Слой бизнес-логики (Service)
func (s *UserService) Create(dto UserDTO) (*User, error) {
// Бизнес-правила
if err := s.validator.Validate(dto); err != nil {
return nil, err
}

// Сложная логика преобразования
user := s.mapToUser(dto)

// Транзакционность
return s.repo.WithTransaction(func(tx *Transaction) (*User, error) {
return tx.Save(user)
})
}

// Слой данных (Repository)
func (r *UserRepository) Save(user *User) (*User, error) {
return r.db.Create(user)
}


Почему это важно?
- Разделение ответственности: Каждый слой имеет чёткие границы
- Тестируемость: Легко изолировать и тестировать отдельные компоненты
- Масштабируемость: Возможность независимого развития слоёв
- Поддерживаемость: Clear separation of concerns

🎯 Три слоя — фундамент чистой архитектуры приложения
35👍26🔥19
🏗️ 12 Factor App: Облачные приложения

Что такое 12 Factor App?

12 Factor App — методология создания современных веб-приложений, оптимизированных для облачного деплоя, масштабирования и непрерывной поставки.

💡 Подробное описание 12 принципов:

1. Единая кодовая база (Codebase)
- Один репозиторий для каждого приложения
- Возможность деплоя на разные окружения
- Использование систем контроля версий (Git)

2. Явные зависимости (Dependencies)
- Точное объявление и изоляция зависимостей
- Использование менеджеров пакетов
- Никаких системных библиотек

3. Конфигурация через ENV (Config)
- Хранение конфигурации в окружении
- Никаких хардкодов
- Легкая смена конфигурации между средами

4. Backing services как подключаемые ресурсы
- БД, кэши, очереди — внешние сервисы
- Возможность замены без изменения кода

5. Разделение сборки и запуска
- Стадия сборки создает артефакт
- Стадия запуска выполняет этот артефакт

6. Stateless процессы
- Никакого локального стейта
- Персистентность через внешние сервисы

7. Экспорт сервисов через порты
- Полная изоляция сервисов
- Все взаимодействия через сетевые порты

8. Конкурентность
- Горизонтальное масштабирование
- Процессы могут быть запущены в любом количестве

9. Утилизируемость
- Быстрый запуск и остановка
- Грациозное завершение работы

10. Паритет dev/prod
- Максимально близкие окружения
- Непрерывная интеграция

11. Логирование
- Логи как поток событий
- Captures in stdout/stderr

12. Admin-процессы
- Разовые задачи как отдельные процессы

Почему это важно?
- Облачная совместимость
- DevOps-практики
- Масштабируемость
- Простота развертывания
- Независимость от инфраструктуры

🎯 12 Factor App — стандарт современных облачных приложений
👍8019🔥16
🏗️ REST: Архитектура веб-сервисов

Что такое REST?

REST (Representational State Transfer) — архитектурный стиль взаимодействия компонентов распределённого приложения в сети.

💡 Принципы REST:

1. Клиент-серверная архитектура
- Четкое разделение ответственности
- Независимое развитие клиента и сервера

2. Отсутствие состояния (Stateless)
- Каждый запрос содержит всю необходимую информацию
- Сервер не хранит информацию о клиенте между запросами
- Каждый запрос — независимая транзакция

3. Кэширование
- Явное указание кэшируемости ресурсов
- Улучшение производительности

4. Единообразие интерфейса
- Стандартизация взаимодействия
- Использование HTTP-методов
- Идентификация ресурсов через URL

5. Многоуровневость
- Возможность размещения посредников
- Прозрачность для клиента
- Балансировка нагрузки

Почему это важно?
- Стандартизация API
- Масштабируемость систем
- Независимость компонентов
- Простота интеграции

🎯 REST — фундамент современных веб-сервисов
🔥3230👍17
🛠 Пространственная сложность: что это и как она влияет на производительность?

Что такое пространственная сложность?

Пространственная сложность (space complexity) — это мера объема памяти, необходимого алгоритму для выполнения задачи. Она измеряется в терминах количества используемых ячеек памяти в зависимости от размера входных данных.

💡 Как оценить пространственную сложность?
- О-нотация (Big O notation): Используется для описания асимптотического поведения алгоритма. Например, O(n) указывает на линейное увеличение использования памяти с ростом входных данных.
- Пример:
  func example(n int) {
arr := make([]int, n) // Пространственная сложность O(n)
for i := 0; i < n; i++ {
arr[i] = i
}
}


⚠️ Почему это важно?
- Оптимизация памяти: Понимание пространственной сложности помогает избежать излишнего использования памяти.
- Производительность: Эффективное использование памяти может улучшить производительность программы, особенно в условиях ограниченных ресурсов.

🎯 Пространственная сложность — это ключевой показатель, который помогает оптимизировать использование памяти и улучшить производительность алгоритмов.
👍6329🔥26
🛠 Временная сложность: что это и как она влияет на производительность?

Что такое временная сложность?

Временная сложность (time complexity) — это мера времени, необходимого алгоритму для выполнения задачи в зависимости от размера входных данных. Она также измеряется с помощью О-нотации.

💡 Как оценить временную сложность?
- О-нотация (Big O notation): Описывает асимптотическое поведение алгоритма. Например, O(n) указывает на линейное увеличение времени выполнения с ростом входных данных.
- Пример:
  func example(n int) {
sum := 0
for i := 0; i < n; i++ {
sum += i
}
}
// Временная сложность O(n)


⚠️ Почему это важно?
- Оптимизация времени: Понимание временной сложности помогает выбрать наиболее эффективные алгоритмы.
- Производительность: Эффективное использование времени может значительно улучшить производительность программы, особенно при работе с большими объемами данных.

🎯 Временная сложность — это ключевой показатель, который помогает оптимизировать время выполнения и улучшить производительность алгоритмов.
👍68🔥4133
🛠 Красно-черное дерево: что это и как оно работает?

Что такое красно-черное дерево?

Красно-черное дерево — это сбалансированное бинарное дерево поиска, которое обеспечивает логарифмическое время выполнения для основных операций (вставка, удаление, поиск). Оно использует дополнительные свойства для поддержания баланса.

💡 Как работает красно-черное дерево?
- Свойства:
1. Каждая нода либо красная, либо черная.
2. Корень всегда черный.
3. Все листья (NIL-ноды) черные.
4. Красная нода не может иметь красных детей.
5. Любой путь от ноды до листьев содержит одинаковое количество черных нод.
- Операции:
- Вставка: Новая нода всегда красная. После вставки выполняются ротации и перекраски для восстановления свойств.
- Удаление: После удаления ноды выполняются ротации и перекраски для восстановления свойств.

❗️Почему это важно?
- Сбалансированность: Красно-черное дерево обеспечивает логарифмическое время выполнения для основных операций.
- Производительность: Эффективное использование памяти и времени делает его популярным выбором для реализации структур данных, таких как словари и множества.

🎯 Красно-черное дерево — это мощная структура данных, которая обеспечивает высокую производительность для операций поиска, вставки и удаления.
👍4334🔥33
🛠 Сортировка пузырьком: что это и как она работает?

Что такое сортировка пузырьком?

Сортировка пузырьком (Bubble Sort) — это простой алгоритм сортировки, который многократно проходит по списку, сравнивая и обменивая соседние элементы, если они находятся в неправильном порядке.

💡 Как работает сортировка пузырьком?
- Процесс:
1. Проходим по списку от начала до конца.
2. Сравниваем каждую пару соседних элементов.
3. Если элементы находятся в неправильном порядке, меняем их местами.
4. Повторяем процесс до тех пор, пока список не будет отсортирован.
- Временная сложность: O(n^2) в худшем и среднем случае, O(n) в лучшем случае (если список уже отсортирован).

⚠️ Пример:
func bubbleSort(arr []int) {
n := len(arr)
for i := 0; i < n-1; i++ {
for j := 0; j < n-i-1; j++ {
if arr[j] > arr[j+1] {
arr[j], arr[j+1] = arr[j+1], arr[j]
}
}
}
}


❗️Почему это важно?
- Образовательная ценность: Сортировка пузырьком — это отличный пример для изучения основ алгоритмов сортировки.
- Простота: Алгоритм легко реализовать и понять, но неэффективен для больших объемов данных.

🎯 Сортировка пузырьком — это простой и наглядный алгоритм сортировки, который помогает понять основы работы с массивами.
👍4133🔥28
🛠 Пирамидальная сортировка: что это и как она работает?

Что такое пирамидальная сортировка?

Пирамидальная сортировка (Heap Sort) — это алгоритм сортировки, который использует структуру данных "куча" (heap) для эффективного сортирования элементов.

💡 Как работает пирамидальная сортировка?
- Процесс:
1. Построение кучи: Преобразуем массив в максимальную кучу (max-heap), где каждый родительский узел больше или равен своим дочерним узлам.
2. Извлечение элементов: Поочередно извлекаем максимальный элемент из кучи и перемещаем его в конец массива. После каждого извлечения восстанавливаем свойства кучи.
- Временная сложность: O(n log n) для всех случаев.

⚠️ Пример:
func heapSort(arr []int) {
n := len(arr)
for i := n/2 - 1; i >= 0; i-- {
heapify(arr, n, i)
}
for i := n - 1; i >= 0; i-- {
arr[0], arr[i] = arr[i], arr[0]
heapify(arr, i, 0)
}
}

func heapify(arr []int, n int, i int) {
largest := i
left := 2*i + 1
right := 2*i + 2

if left < n && arr[left] > arr[largest] {
largest = left
}
if right < n && arr[right] > arr[largest] {
largest = right
}
if largest != i {
arr[i], arr[largest] = arr[largest], arr[i]
heapify(arr, n, largest)
}
}


❗️Почему это важно?
- Эффективность: Пирамидальная сортировка обеспечивает логарифмическое время выполнения для всех случаев.
- Производительность: Эффективное использование памяти и времени делает ее популярным выбором для сортировки больших объемов данных.

🎯 Пирамидальная сортировка — это мощный алгоритм, который обеспечивает высокую производительность и эффективность для сортировки данных.
👍5644🔥23
🛠 Односвязный список: что это и как он работает?

Что такое односвязный список?

Односвязный список (Singly Linked List) — это структура данных, состоящая из узлов, где каждый узел содержит данные и ссылку на следующий узел. Последний узел списка указывает на null.

💡 Как работает односвязный список?
- Узел: Состоит из двух частей — данных и ссылки на следующий узел.
- Операции:
- Добавление: Новый узел добавляется в начало или конец списка.
- Удаление: Узел удаляется путем изменения ссылки предыдущего узла.
- Поиск: Проход по списку от начала до конца.

  type Node struct {
Data int
Next *Node
}

func addToFront(head *Node, data int) *Node {
newNode := &Node{Data: data, Next: head}
return newNode
}


⚠️ Почему это важно?
- Простота: Односвязные списки легко реализовать и использовать.
- Динамическое управление памятью: Позволяет эффективно управлять памятью, добавляя и удаляя узлы по мере необходимости.

🎯 Односвязный список — это простая и эффективная структура данных для хранения и управления последовательностями элементов.
43🔥36👍27