Перейти к содержимому
Теория · Итоговый проект · урок 6 из 7

Итоговый проект

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

С программами так же. Тип в настоящей программе обычно опирается сразу на несколько идей. Например, он:

  • хранит данные в map;
  • меняет их методами;
  • возвращает ошибку, когда операцию выполнить нельзя.

Итоговая задача устроена так же: новых конструкций в ней нет, нужно соединить знакомые.

Склад

Задача — склад, который считает единицы каждого товара. Тип Inventory хранит количества в map, а конструктор NewInventory создаёт пустой склад. Методы делят работу так:

  • Add кладёт товар на склад;
  • Take забирает его;
  • Count называет количество;
  • Names возвращает список товаров.

Детали из прошлых тем

В таблице — какие детали из прошлых тем понадобятся для каждой части:

Часть задачиЧто понадобится
поле и конструкторструктура, map, make, функция NewInventory
Add и Takeметоды с получателем-указателем, delete
ошибки Takeвозврат error, ошибки-маркеры из заготовки
Namesrange по map, append, slices.Sort или sort.Strings

Ошибки Take

Если забрать товар нельзя, Take возвращает ошибку и склад не меняет. Заготовка уже объявляет обе ошибки, и тесты узнают их через errors.Is.

ErrUnknownItem

Take возвращает ErrUnknownItem, когда товара name на складе нет.

ErrNotEnough

Take возвращает ErrNotEnough, когда товар есть, но единиц меньше, чем qty.

Порядок в Names

Метод Names сортирует названия сам, потому что range по map обходит ключи в непредсказуемом порядке.

Порядок работы

Пишите решение по шагам:

  1. Начните с поля и конструктора: без map остальным методам не с чем работать. Это основание полки — без него остальные доски крепить не к чему.
  2. Затем пишите по одному методу.
  3. После каждого метода запускайте тесты.

Два теста

Тестов два. Каждый тест создаёт свой склад, поэтому ошибка в одном не мешает другому.

TestInventory

TestInventory проверяет Add, Count, Names и удачный вызов Take.

TestTake

TestTake проверяет ошибки Take и товар, забранный целиком.

Сообщение о провале

Здесь аналогия с полкой заканчивается. Кривую полку видно сразу, а ошибку в коде показывают только тесты. Пройденная проверка ничего не печатает. Непройденная печатает строку с именем файла тестов и номером строки — так заготовка отвечает на первую проверку:

TEXT
inventory_test.go:14: inv.Count("apple") = -1, want 8

Давайте прочитаем эту строку:

  • слева от = стоит вызов из теста;
  • справа — то, что вернул ваш код;
  • после want — ожидаемое значение.

Здесь -1 пришло из заготовки: вы ещё не написали метод Count. Итог каждого теста показывает строка --- PASS или --- FAIL с его именем.

Исправляйте первую непройденную проверку: следующие часто не проходят из-за неё, потому что внутри теста результат вызова зависит от предыдущих.

Частая ошибка

Конструктор возвращает структуру, а map внутри неё не создан:

GO
package main

type Inventory struct {
    items map[string]int
}

func NewInventory() *Inventory {
    return &Inventory{} // ✗
}

func main() {
    inv := NewInventory()
    inv.items["apple"] += 5
}
TEXT
panic: assignment to entry in nil map

Поле items получило нулевое значение nil, а запись в nil-map вызывает панику. Создайте map в конструкторе: return &Inventory{items: make(map[string]int)}.

Главное

  • Итоговая задача не вводит новых конструкций: в ней соединяются структура, map, методы и ошибки.
  • Конструктор создаёт map через make; запись в nil-map вызывает панику.
  • Пишите по одному методу и запускайте тесты после каждого.
  • Исправляйте первую непройденную проверку: строка = -1, want 8 показывает, что вернул код и что ожидалось.

Подпишись, чтобы пройти курс

Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.

Войти другим способом