← Назад к вопросам

Приведи пример агрегации

2.3 Middle🔥 152 комментариев
#Основы Go

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Пример агрегации в программировании на Go

Агрегация — это отношение "has-a" между объектами, где один объект содержит ссылку на другой как часть своей структуры, но при этом оба объекта могут существовать независимо. Это контрастирует с композицией, где жизненный цикл зависимого объекта полностью контролируется родителем. В Go агрегация реализуется через встраивание структур или хранение указателей/ссылок на другие структуры.

Пример: Система управления автомобилями и двигателями

Рассмотрим систему, где автомобиль использует двигатель, но двигатель может существовать независимо от автомобиля (например, его можно переставить в другую машину или хранить на складе).

package main

import (
	"fmt"
)

// Engine - независимый компонент
type Engine struct {
	ID           string
	Manufacturer string
	PowerHP      int
	VolumeL      float64
}

func (e *Engine) Start() {
	fmt.Printf("Двигатель %s запущен (%d л.с.)\n", e.ID, e.PowerHP)
}

func (e *Engine) Stop() {
	fmt.Printf("Двигатель %s остановлен\n", e.ID)
}

// Car - агрегирует Engine через указатель
type Car struct {
	VIN         string
	Model       string
	Year        int
	Engine      *Engine // АГРЕГАЦИЯ: Car имеет ссылку на Engine
	MileageKM   int
}

func (c *Car) Drive(distance int) {
	if c.Engine == nil {
		fmt.Println("Ошибка: у автомобиля нет двигателя!")
		return
	}
	c.Engine.Start()
	c.MileageKM += distance
	fmt.Printf("Автомобиль %s проехал %d км. Общий пробег: %d км\n", 
		c.Model, distance, c.MileageKM)
	c.Engine.Stop()
}

func main() {
	// Создаем независимый двигатель
	v8Engine := &Engine{
		ID:           "ENG-2024-V8",
		Manufacturer: "PowerMotors",
		PowerHP:      450,
		VolumeL:      5.0,
	}

	// Создаем автомобиль, агрегирующий двигатель
	myCar := &Car{
		VIN:       "1HGBH41JXMN109186",
		Model:     "TurboX",
		Year:      2024,
		Engine:    v8Engine, // Агрегация: присваиваем ссылку на существующий двигатель
		MileageKM:14700,
	}

	fmt.Println("=== Демонстрация агрегации ===")
	fmt.Printf("Двигатель: %s (%d л.с.)\n", v8Engine.ID, v8Engine.PowerHP)
	fmt.Printf("Автомобиль: %s %d года\n", myCar.Model, myCar.Year)

	// Используем автомобиль с двигателем
	myCar.Drive(150)

	// Демонстрируем независимость объектов
	fmt.Println("\n=== Демонстрация независимости ===")
	
	// 1. Двигатель может существовать без автомобиля
	standaloneEngine := &Engine{
		ID:      "ENG-STOCK-001",
		PowerHP: 200,
	}
	standaloneEngine.Start()
	standaloneEngine.Stop()

	// 2. Автомобиль может сменить двигатель
	newEngine := &Engine{
		ID:           "ENG-ECO-2024",
		Manufacturer: "EcoTech",
		PowerHP:      180,
		VolumeL:      2.0,
	}
	
	// Меняем двигатель - старая агрегация заменяется новой
	myCar.Engine = newEngine
	fmt.Printf("\nАвтомобиль %s получил новый двигатель: %s\n", 
		myCar.Model, newEngine.ID)
	myCar.Drive(50)

	// 3. Старый двигатель все еще существует и может быть использован
	fmt.Printf("\nСтарый двигатель %s все еще доступен: %d л.с.\n", 
		v8Engine.ID, v8Engine.PowerHP)
}

Ключевые характеристики агрегации в этом примере

  • Независимость жизненных циклов: Engine создается независимо от Car и продолжает существовать даже после удаления автомобиля или замены двигателя
  • Связь через ссылку/указатель: Car содержит указатель на Engine, а не сам объект
  • Возможность замены агрегируемого объекта: Двигатель можно поменять в любой момент (myCar.Engine = newEngine)
  • Отсутствие контроля над созданием/уничтожением: Car не создает и не уничтожает Engine, только использует его

Практическое применение агрегации

Агрегация особенно полезна в следующих сценариях:

  • Системы с общими ресурсами: Когда несколько объектов используют один и тот же ресурс (например, несколько сервисов используют один логгер или конфигурацию)
  • Паттерн "Стратегия": Когда поведение объекта определяется агрегированной стратегией, которую можно менять динамически
  • Сборки из независимых компонентов: Как в примере выше - автомобиль собирается из предварительно созданных компонентов
  • Системы с кэшированием и пулами объектов: Объекты могут брать ресурсы из пула и возвращать их обратно

Отличие от композиции в Go

// КОМПОЗИЦИЯ - жесткая зависимость, объект создается внутри
type CarWithComposition struct {
	Model string
	Engine Engine // Композиция: объект Engine создается вместе с Car
	// Engine не может существовать отдельно от этого автомобиля
}

// АГРЕГАЦИЯ - гибкая связь, объект передается извне
type CarWithAggregation struct {
	Model  string
	Engine *Engine // Агрегация: ссылка на внешний объект
}

Агрегация обеспечивает большую гибкость и слабую связность в системе, что соответствует принципам SOLID, особенно принципу единственной ответственности и инверсии зависимостей.

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Пример агрегации в программировании (Go)

Агрегация — это структурный паттерн проектирования, при котором один объект (контейнер) содержит ссылки на другие объекты (компоненты), образуя отношение "часть-целое". В отличие от композиции, компоненты могут существовать независимо от контейнера. В Go агрегация часто реализуется через встраивание структур или использование срезов/мап указателей.

Ключевые характеристики агрегации:

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

Пример: Система управления университетом

Рассмотрим университет (University), который агрегирует факультеты (Department), а те, в свою очередь, агрегируют студентов (Student). Все эти сущности могут существовать независимо.

package main

import (
	"fmt"
	"strings"
)

// Student - независимая сущность
type Student struct {
	ID        int
	Name      string
	Major     string
}

func (s *Student) Info() string {
	return fmt.Sprintf("%s (ID: %d), специальность: %s", s.Name, s.ID, s.Major)
}

// Department агрегирует студентов
type Department struct {
	Name     string
	Students []*Student // Агрегация: срез указателей на независимые объекты
}

func (d *Department) AddStudent(s *Student) {
	d.Students = append(d.Students, s)
}

func (d *Department) ListStudents() string {
	var sb strings.Builder
	sb.WriteString(fmt.Sprintf("Факультет: %s\n", d.Name))
	sb.WriteString("Студенты:\n")
	
	for _, student := range d.Students {
		sb.WriteString("  - " + student.Info() + "\n")
	}
	
	return sb.String()
}

// University агрегирует факультеты
type University struct {
	Name        string
	Departments []*Department // Агрегация через срез указателей
}

func (u *University) AddDepartment(d *Department) {
	u.Departments = append(u.Departments, d)
}

func (u *University) FindStudent(studentID int) *Student {
	for _, dept := range u.Departments {
		for _, student := range dept.Students {
			if student.ID == studentID {
				return student
			}
		}
	}
	return nil
}

func (u *University) Statistics() string {
	totalStudents := 0
	for _, dept := range u.Departments {
		totalStudents += len(dept.Students)
	}
	
	return fmt.Sprintf(
		"Университет: %s\nФакультетов: %d\nСтудентов: %d\n",
		u.Name, len(u.Departments), totalStudents,
	)
}

func main() {
	// Создаём независимых студентов
	student1 := &Student{ID: 1, Name: "Иван Петров", Major: "Информатика"}
	student2 := &Student{ID: 2, Name: "Мария Сидорова", Major: "Математика"}
	student3 := &Student{ID: 3, Name: "Алексей Иванов", Major: "Информатика"}
	student4 := &Student{ID: 4, Name: "Елена Кузнецова", Major: "Физика"}

	// Создаём факультеты
	csDept := &Department{Name: "Факультет информационных технологий"}
	mathDept := &Department{Name: "Математический факультет"}
	
	// Агрегируем студентов в факультеты
	csDept.AddStudent(student1)
	csDept.AddStudent(student3)
	mathDept.AddStudent(student2)
	mathDept.AddStudent(student4)

	// Создаём университет
	university := &University{Name: "Национальный технический университет"}
	
	// Агрегируем факультеты в университет
	university.AddDepartment(csDept)
	university.AddDepartment(mathDept)

	// Используем агрегацию
	fmt.Println(university.Statistics())
	
	fmt.Println("\nДетальная информация:")
	for _, dept := range university.Departments {
		fmt.Println(dept.ListStudents())
	}

	// Демонстрация независимого существования объектов
	foundStudent := university.FindStudent(3)
	if foundStudent != nil {
		fmt.Printf("Найден студент: %s\n", foundStudent.Info())
		
		// Студент существует вне контекста университета
		standaloneStudent := &Student{
			ID: 5, 
			Name: "Петр standalone", 
			Major: "Химия",
		}
		fmt.Printf("Независимый студент: %s\n", standaloneStudent.Info())
	}
}

Важные аспекты агрегации в Go:

1. Использование указателей

// Правильно - агрегация
type Department struct {
    Students []*Student
}

// Это композиция, а не агрегация
type DepartmentComposite struct {
    Students []Student // Объекты создаются и уничтожаются вместе с Department
}

2. Передача зависимостей через интерфейсы

Более гибкий вариант агрегации с использованием интерфейсов:

type Researcher interface {
    Research() string
}

type University struct {
    Researchers []Researcher // Агрегация через интерфейс
}

func (u *University) AddResearcher(r Researcher) {
    u.Researchers = append(u.Researchers, r)
}

3. Управление жизненным циклом

// Создание компонентов вне агрегата
student := &Student{ID: 1, Name: "Тест"}

// Агрегирование существующего объекта
dept.AddStudent(student)

// Удаление университета не удаляет студентов
university = nil // Студенты всё ещё существуют в памяти

Практическое применение агрегации:

  • ORM-модели: Пользователь может существовать без заказов, но заказ агрегирует пользователя
  • Микросервисная архитектура: Сервис-агрегатор может объединять данные из нескольких независимых сервисов
  • Кэширование: Кэш агрегирует объекты, которые создаются в других частях системы
  • Event-driven системы: Обработчики событий агрегируются в диспетчере, но существуют независимо

Преимущества агрегации:

  • Гибкость: Компоненты можно повторно использовать в разных агрегатах
  • Тестируемость: Компоненты легко тестировать изолированно
  • Модульность: Упрощает рефакторинг и изменение структуры
  • Разделение ответственности: Каждый объект отвечает за свою конкретную задачу

Агрегация в Go естественно ложится на идиоматичное использование указателей и интерфейсов, позволяя создавать гибкие, слабосвязанные системы, соответствующие принципам SOLID, особенно принципу единственной ответственности и принципу разделения интерфейсов.

Приведи пример агрегации | PrepBro