Паттерн "Очередь" (Queue) в программировании обычно представляет собой структуру данных или архитектурный подход, используемый для обработки сообщений, задач или запросов в определённом порядке (обычно FIFO — "первым вошёл, первым вышел").
Но если рассматривать количество очередей в каком-то конкретном паттерне проектирования, то всё зависит от контекста. Например:
Все задачи обрабатываются в одной очереди по порядку. Используется, когда важен строгий порядок выполнения. Пример: канал (channel) в Go, куда отправляются задачи для обработки.
Используется, когда разные типы задач требуют разной обработки. Например, очереди с разными приоритетами (высокий, средний, низкий). В многопоточных системах каждая очередь может обслуживаться отдельным worker'ом.
Есть одна очередь задач, но несколько рабочих (goroutines), которые извлекают задачи и обрабатывают их параллельно.
Уменьшает нагрузку на систему и ускоряет выполнение.
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, jobs <-chan int, results chan<- int, wg *sync.WaitGroup) {
defer wg.Done()
for job := range jobs {
fmt.Printf("Worker %d started job %d\n", id, job)
time.Sleep(time.Second) // имитация работы
fmt.Printf("Worker %d finished job %d\n", id, job)
results <- job * 2
}
}
func main() {
jobs := make(chan int, 5)
results := make(chan int, 5)
var wg sync.WaitGroup
numWorkers := 3
for i := 1; i <= numWorkers; i++ {
wg.Add(1)
go worker(i, jobs, results, &wg)
}
for j := 1; j <= 5; j++ {
jobs <- j
}
close(jobs)
wg.Wait()
close(results)
for res := range results {
fmt.Println("Result:", res)
}
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Да, в SQL возможен
JOIN с вложенными запросами. Это позволяет объединять результаты подзапросов с другими таблицами, что полезно, когда нужно предварительно отфильтровать или агрегировать данные перед объединением.JOIN можно использовать не только с реальными таблицами, но и с результатами подзапросов, если они представляют собой временные таблицы.
Допустим, у нас есть две таблицы:
-
orders (id, user_id, total)-
users (id, name)Мы хотим получить всех пользователей и их заказы, но только те заказы, где сумма больше 100
SELECT u.name, sub.total
FROM users u
JOIN (
SELECT user_id, total
FROM orders
WHERE total > 100
) AS sub ON u.id = sub.user_id;
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
NAT может использоваться для реализации прокси-серверов или маршрутизации сетевых запросов с помощью библиотек вроде net или net/http. Позволяет взаимодействовать с NAT, включая обработку частных и публичных IP-адресов.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Этот подход обеспечивает ряд преимуществ, которые способствуют улучшению качества кода, его поддерживаемости и соответствию требованиям.
Написание тестов перед кодом помогает разработчикам четко определить требования к функции или модулю. Тесты выступают в роли спецификации, описывающей, как должен работать код. Это помогает избежать недоразумений и гарантирует, что все требования учтены с самого начала.
TDD заставляет разработчиков сосредоточиться на минимальной функциональности, необходимой для прохождения тестов. Это предотвращает избыточное проектирование и разработку ненужных функций. Каждый тест фокусируется на одном аспекте функциональности, что помогает создавать чистый и целенаправленный код.
Написание тестов перед кодом стимулирует создание модульного и хорошо структурированного кода. Поскольку тесты проверяют небольшие и изолированные части кода, это побуждает разработчиков разделять большие задачи на более мелкие и управляемые модули, что улучшает дизайн и архитектуру системы.
Тесты, написанные перед кодом, обеспечивают немедленную обратную связь. Если код не проходит тест, это означает, что он не соответствует требованиям. Это позволяет разработчикам быстро обнаруживать и исправлять ошибки, повышая уверенность в корректности кода.
Тесты служат живой документацией кода. Они показывают, как должен работать код и какие случаи использования поддерживаются. Это облегчает понимание кода другим разработчикам и упрощает его поддержку в будущем.
Постоянное написание тестов и их прохождение помогает поддерживать высокое качество кода. Тесты выявляют дефекты на ранних стадиях разработки и обеспечивают, что новые изменения не нарушают существующую функциональность.
package main
import "testing"
func TestAdd(t *testing.T) {
result := Add(2, 3)
if result != 5 {
t.Errorf("Expected 5, but got %d", result)
}
}
На этом этапе тест не пройдет, так как функция
Add еще не реализована.package main
func Add(a, b int) int {
return a + b
}
Теперь тест пройдет, так как функция
Add реализована корректно.При необходимости выполняется рефакторинг кода для улучшения его структуры, при этом тесты продолжают проходить, что гарантирует сохранение функциональности.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
- Структуры заменяют классы и служат для хранения данных.
2. Методы:
- Методы определяются отдельно и привязываются к структурам.
3. Интерфейсы:
- Полиморфизм реализуется через интерфейсы, задающие набор методов.
4. Встраивание:
- Структуры могут встраивать другие структуры для наследования поведения.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
SSL (Secure Sockets Layer) — это протокол, который обеспечивает безопасность передачи данных в интернете, используя шифрование. Он был разработан для защиты данных, передаваемых между клиентом (например, веб-браузером) и сервером (например, веб-сайтом), от перехвата и манипуляций.
Защита данных от перехвата и чтения посторонними лицами путем их шифрования.
Подтверждение подлинности сервера (и иногда клиента) с помощью цифровых сертификатов, что позволяет клиенту убедиться, что он подключен к настоящему серверу.
Проверка того, что данные не были изменены во время передачи, с помощью контрольных сумм и хеш-функций.
Клиент инициирует соединение с сервером, запрашивая защищенное соединение.
Сервер отправляет свой цифровой сертификат, который содержит его публичный ключ и информацию о сервере.
Клиент проверяет сертификат, используя доверенные центры сертификации (CA), чтобы удостовериться в подлинности сервера.
Клиент и сервер используют асимметричное шифрование для обмена ключами сеанса, которые затем используются для симметричного шифрования данных в течение сессии.
Все данные, передаваемые между клиентом и сервером, шифруются с использованием симметричных ключей, обеспечивая безопасность передачи.
При посещении веб-сайта с использованием HTTPS (например, https://example.com), SSL обеспечивает шифрование и безопасность данных, передаваемых между вашим браузером и сервером.
Никогда не был выпущен публично из-за серьезных уязвимостей.
Выпущен в 1995 году, но вскоре был признан небезопасным из-за множества уязвимостей.
Выпущен в 1996 году, значительно улучшил безопасность, но со временем также был признан устаревшим из-за уязвимостей (например, POODLE-атака).
SSL был заменен протоколом TLS, который является его преемником и предлагает улучшенную безопасность. Текущие версии TLS (1.2 и 1.3) используются вместо SSL.
TLS обеспечивает более сильное шифрование, лучшее управление сессионными ключами и устранение уязвимостей, найденных в SSL.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🤔1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Концепции объектно-ориентированного программирования (ООП) реализованы несколько иначе, чем в традиционных ООП-языках, таких как Java или C++. Не использует классы, наследование и полиморфизм на основе классов в привычном понимании. Вместо этого он применяет интерфейсы, структуры и встраивание для достижения гибкости и мощи ООП.
Основной способ организации и капсуляции данных — это структуры (
structs). Структуры объединяют данные в одну сущность, но в отличие от классов, они не включают определение методов внутри себя. Вместо этого методы определяются отдельно и ассоциируются со структурой через определение получателя метода.type Person struct {
Name string
Age int
}
func (p Person) Greet() string {
return "Hello, my name is " + p.Name
}Реализуется через интерфейсы. Интерфейс — это набор сигнатур методов. Тип считается реализующим интерфейс, если он имеет все методы, указанные в интерфейсе. Важной особенностью интерфейсов в Go является то, что типы могут удовлетворять интерфейсам неявно, без специального объявления.
type Greeter interface {
Greet() string
}
func GreetSomeone(g Greeter) {
fmt.Println(g.Greet())
}Один из способов реализации композиции — встраивание структур. Можно встроить одну структуру в другую, что позволяет делегировать часть работы встроенной структуре.
type Employee struct {
Person
Position string
}
func (e Employee) Work() string {
return e.Name + " is working as a " + e.Position
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Таймауты:
- Ограничивают время ожидания ответа, чтобы не зависать навечно.
- Защищают от зависших серверов или сетевых проблем.
- Позволяют освободить ресурсы в клиентском приложении.
- Повышают надёжность: без таймаутов приложение может «подвисать» или блокировать выполнение других операций.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Функция append используется для добавления элементов к слайсу. Эта функция может увеличивать длину слайса и, при необходимости, выделять новый подлежащий массив, если текущий массив не имеет достаточной емкости для хранения новых элементов.
Если текущий подлежащий массив слайса имеет достаточно места (емкости) для добавления новых элементов,
append просто добавляет элементы к существующему массиву.Если емкость текущего массива недостаточна для размещения новых элементов,
append выделяет новый массив, копирует в него существующие элементы и добавляет новые элементы. Новый массив будет иметь увеличенную емкость (как правило, в два раза больше, чем предыдущая).Синтаксис
slice = append(slice, elem1, elem2, ...)
Пример использования
package main
import "fmt"
func main() {
// Создаем слайс из 3 целых чисел
slice := []int{1, 2, 3}
// Добавляем один элемент
slice = append(slice, 4)
// Добавляем несколько элементов
slice = append(slice, 5, 6, 7)
// Выводим слайс
fmt.Println(slice) // Выводит: [1 2 3 4 5 6 7]
}
Создаем слайс с тремя элементами
[1, 2, 3].slice := []int{1, 2, 3} Добавляем элемент
4 к слайсу. Теперь слайс содержит [1, 2, 3, 4].slice = append(slice, 4)
Добавляем элементы
5, 6, и 7. Теперь слайс содержит [1, 2, 3, 4, 5, 6, 7].slice = append(slice, 5, 6, 7)
Выводим слайс, который содержит
[1, 2, 3, 4, 5, 6, 7].fmt.Println(slice)
Работа с емкостью и длиной
package main
import "fmt"
func main() {
slice := []int{1, 2, 3}
fmt.Printf("Before append: len=%d cap=%d %v\n", len(slice), cap(slice), slice)
slice = append(slice, 4)
fmt.Printf("After append: len=%d cap=%d %v\n", len(slice), cap(slice), slice)
slice = append(slice, 5, 6, 7)
fmt.Printf("After multiple appends: len=%d cap=%d %v\n", len(slice), cap(slice), slice)
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Это интерфейс без методов. Он совместим с любым типом, так как не накладывает ограничений на его реализацию. Это мощный инструмент для работы с универсальными данными.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
В многопоточных (параллельных) программах горутины (goroutines) могут одновременно изменять одни и те же данные. Если не синхронизировать доступ, это приведёт к гонке данных (data race), когда несколько потоков читают/пишут одно и то же значение одновременно.
Используется для блокировки критической секции кода, чтобы в один момент только одна горутина могла изменять данные.
package main
import (
"fmt"
"sync"
)
var (
counter int
mutex sync.Mutex
)
func increment(wg *sync.WaitGroup) {
defer wg.Done()
mutex.Lock() // Блокируем доступ
counter++ // Изменяем данные
mutex.Unlock() // Разблокируем доступ
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go increment(&wg)
}
wg.Wait()
fmt.Println("Итоговый счетчик:", counter) // 1000
}
Позволяет нескольким горутинам читать данные одновременно, но блокирует запись.
var (
data int
mutex sync.RWMutex
)
func readData(wg *sync.WaitGroup) {
defer wg.Done()
mutex.RLock() // Разрешаем чтение
fmt.Println("Читаем данные:", data)
mutex.RUnlock()
}
func writeData(wg *sync.WaitGroup) {
defer wg.Done()
mutex.Lock() // Блокируем на запись
data++
mutex.Unlock()
}
Позволяет дождаться завершения всех горутин без блокировки данных.
var wg sync.WaitGroup
wg.Add(2) // Ожидаем 2 горутины
go func() {
defer wg.Done()
fmt.Println("Горутина 1 завершилась")
}()
go func() {
defer wg.Done()
fmt.Println("Горутина 2 завершилась")
}()
wg.Wait() // Ждём завершения всех горутин
fmt.Println("Все горутины завершены")
Атомарные операции быстрее мьютексов и гарантируют безопасное обновление переменных без гонок данных.
import "sync/atomic"
var counter int64
func incrementAtomic() {
atomic.AddInt64(&counter, 1) // Атомарное увеличение
}
В Go рекомендуется избегать блокировок и использовать каналы для передачи данных между горутинами.
package main
import "fmt"
func main() {
ch := make(chan int) // Канал для передачи данных
go func() {
ch <- 42 // Отправляем данные
}()
data := <-ch // Получаем данные
fmt.Println("Получено:", data)
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2💊2
Карты (maps) представляют собой ассоциативные массивы, которые связывают ключи с соответствующими значениями. Работа с картами включает получение и запись значений, и Go предоставляет удобный синтаксис для этих операций. Рассмотрим особенности синтаксиса получения и записи значений в карты.
Для этого используется синтаксис индексирования
value := myMap[key]
Пример
package main
import "fmt"
func main() {
myMap := map[string]int{
"Alice": 25,
"Bob": 30,
}
value := myMap["Alice"]
fmt.Println("Alice:", value) // Alice: 25
}
Для этого используется синтаксис двойного присваивания
value, exists := myMap[key]
Пример
package main
import "fmt"
func main() {
myMap := map[string]int{
"Alice": 25,
"Bob": 30,
}
value, exists := myMap["Charlie"]
if exists {
fmt.Println("Charlie:", value)
} else {
fmt.Println("Charlie not found")
}
}
Для этого используется синтаксис индексирования:
myMap[key] = value
Пример
package main
import "fmt"
func main() {
myMap := map[string]int{}
// Добавление значений
myMap["Alice"] = 25
myMap["Bob"] = 30
fmt.Println("Map:", myMap) // Map: map[Alice:25 Bob:30]
// Обновление значения
myMap["Alice"] = 26
fmt.Println("Updated Map:", myMap) // Updated Map: map[Alice:26 Bob:30]
}
Для этого используется встроенная функция
delete:delete(myMap, key)
Пример
package main
import "fmt"
func main() {
myMap := map[string]int{
"Alice": 25,
"Bob": 30,
}
// Удаление значения по ключу
delete(myMap, "Alice")
fmt.Println("Map after deletion:", myMap) // Map after deletion: map[Bob:30]
}
Если ключ отсутствует в карте, при попытке получения значения будет возвращено нулевое значение типа значения карты. Например, для карты
map[string]int это будет 0.package main
import "fmt"
func main() {
myMap := map[string]int{
"Alice": 25,
}
value := myMap["Bob"] // Ключ "Bob" отсутствует
fmt.Println("Value:", value) // Value: 0
}
Нельзя записывать значения в
nil карту. Это приведет к панике времени выполнения.package main
func main() {
var myMap map[string]int // nil карта
myMap["Alice"] = 25 // Вызовет панику: runtime error: assignment to entry in nil map
}
Порядок итерации по карте не определен и может различаться между разными запусками программы.
package main
import "fmt"
func main() {
myMap := map[string]int{
"Alice": 25,
"Bob": 30,
"Carol": 35,
}
for key, value := range myMap {
fmt.Printf("%s: %d\n", key, value)
}
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥1
Создание дочернего контекста данных в Go предоставляет мощные возможности для управления временем выполнения операций, отмены и передачи метаданных.
Контексты позволяют задавать дедлайны и таймауты для операций.
context.WithTimeout создает контекст с таймаутом, после которого операция будет автоматически отменена.ctx, cancel := context.WithTimeout(parentCtx, 2*time.Second)
defer cancel()
context.WithDeadline создает контекст с дедлайном, который определяет точное время, после которого операция будет отменена.deadline := time.Now().Add(5 * time.Second)
ctx, cancel := context.WithDeadline(parentCtx, deadline)
defer cancel()
Контексты позволяют явно отменять операции, что полезно для управления горутинами и предотвращения утечек памяти.
context.WithCancel создает контекст, который может быть отменен вручную с помощью функции cancel.ctx, cancel := context.WithCancel(parentCtx)
defer cancel()
Контексты позволяют передавать данные между функциями, что полезно для передачи информации о запросах, пользователей и других данных.
context.WithValue создает контекст, который содержит пару ключ-значение для передачи метаданных.type key string
ctx := context.WithValue(parentCtx, key("userID"), 12345)
Можно создавать иерархии контекстов, где каждый дочерний контекст наследует отмену и дедлайны от родительского.
Создание вложенных контекстов позволяет строить гибкие и сложные системы управления временем выполнения и отмены.
ctx1, cancel1 := context.WithCancel(parentCtx)
ctx2, cancel2 := context.WithTimeout(ctx1, 1*time.Second)
defer cancel1()
defer cancel2()
Контексты обеспечивают механизм для синхронизации и координации работы нескольких горутин.
Пример синхронизации горутин
var wg sync.WaitGroup
ctx, cancel := context.WithCancel(parentCtx)
defer cancel()
wg.Add(2)
go func() {
defer wg.Done()
worker(ctx, "Worker 1")
}()
go func() {
defer wg.Done()
worker(ctx, "Worker 2")
}()
wg.Wait()
func worker(ctx context.Context, name string) {
for {
select {
case <-ctx.Done():
fmt.Println(name, "stopped")
return
default:
fmt.Println(name, "working")
time.Sleep(1 * time.Second)
}
}
}
Контексты широко используются в веб-серверах для управления жизненным циклом HTTP-запросов, обеспечивая таймауты и отмену при завершении обработки запросов.
Пример использования контекста в обработчике HTTP-запроса
func handler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
select {
case <-time.After(5 * time.Second):
fmt.Fprintln(w, "Request processed")
case <-ctx.Done():
err := ctx.Err()
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Слайс (
slice) — это динамический массив, который ссылается на часть массива в памяти. В отличие от массивов (array), слайсы могут изменять размер. Слайс в Go — это структура
type SliceHeader struct {
Data uintptr // Указатель на массив в памяти
Len int // Длина слайса (количество элементов)
Cap int // Вместимость (capacity) — сколько элементов может вместить без перевыделения памяти
}Пример структуры слайса
arr := [5]int{1, 2, 3, 4, 5}
s := arr[1:4] // Берём срез от 2-го до 4-го элемента
fmt.Println(s) // [2 3 4]Есть несколько способов создать слайс:
Способ 1: Срез массива
arr := [5]int{10, 20, 30, 40, 50}
s := arr[1:4] // [20 30 40]Способ 2: Использование
make()s := make([]int, 3, 5) // Длина 3, вместимость 5
fmt.Println(s, len(s), cap(s)) // [0 0 0] 3 5
Способ 3: Литерал (инициализация значениями)
s := []int{1, 2, 3}
fmt.Println(s) // [1 2 3]Слайсы можно изменять, используя
append(). s := []int{1, 2, 3}
s = append(s, 4, 5)
fmt.Println(s) // [1 2 3 4 5]Когда
append() увеличивает slice, Go использует оптимизированный алгоритм роста:- Если
cap < 1024, слайс удваивает размер (cap *= 2). - Если
cap >= 1024, рост идёт примерно на 25% (cap += cap / 4). s := []int{}
for i := 0; i < 10; i++ {
s = append(s, i)
fmt.Printf("Len: %d, Cap: %d\n", len(s), cap(s))
}Выход (пример)
Len: 1, Cap: 1
Len: 2, Cap: 2
Len: 3, Cap: 4
Len: 5, Cap: 8
Len: 9, Cap: 16
Так как слайсы хранят ссылку на массив, возможны побочные эффекты.
arr := [5]int{1, 2, 3, 4, 5}
s1 := arr[:3] // [1 2 3]
s2 := arr[2:] // [3 4 5]
s2[0] = 100 // Меняем первый элемент s2
fmt.Println(s1) // [1 2 100] ❗️ s1 тоже изменилсяРешение: используйте
copy() для создания нового массива. s1 := []int{1, 2, 3}
s2 := make([]int, len(s1))
copy(s2, s1) // Копируем данные
s2[0] = 100
fmt.Println(s1) // [1 2 3] ✅ Оригинал не изменилсяСтавь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥1
При возникновении паники функция defer откладывает выполнение восстановления до выхода из текущей функции, а recover перехватывает ошибку, предотвращая завершение программы. Это полезно для логирования ошибок и безопасного завершения работы.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2