Итоговый проект
Представьте, что вы собираете книжную полку из набора. Новых деталей в коробке нет: доски, винты и уголки вы уже держали в руках. Задача в другом — соединить их в правильном порядке, чтобы полка встала ровно.
С программами так же. Тип в настоящей программе обычно опирается сразу на несколько идей. Например, он:
- хранит данные в
map; - меняет их методами;
- возвращает ошибку, когда операцию выполнить нельзя.
Итоговая задача устроена так же: новых конструкций в ней нет, нужно соединить знакомые.
Склад
Задача — склад, который считает единицы каждого товара. Тип Inventory хранит количества в map, а конструктор NewInventory создаёт пустой склад. Методы делят работу так:
Addкладёт товар на склад;Takeзабирает его;Countназывает количество;Namesвозвращает список товаров.
Детали из прошлых тем
В таблице — какие детали из прошлых тем понадобятся для каждой части:
| Часть задачи | Что понадобится |
|---|---|
| поле и конструктор | структура, map, make, функция NewInventory |
Add и Take | методы с получателем-указателем, delete |
ошибки Take | возврат error, ошибки-маркеры из заготовки |
Names | range по map, append, slices.Sort или sort.Strings |
Ошибки Take
Если забрать товар нельзя, Take возвращает ошибку и склад не меняет. Заготовка уже объявляет обе ошибки, и тесты узнают их через errors.Is.
ErrUnknownItem
Take возвращает ErrUnknownItem, когда товара name на складе нет.
ErrNotEnough
Take возвращает ErrNotEnough, когда товар есть, но единиц меньше, чем qty.
Порядок в Names
Метод Names сортирует названия сам, потому что range по map обходит ключи в непредсказуемом порядке.
Порядок работы
Пишите решение по шагам:
- Начните с поля и конструктора: без
mapостальным методам не с чем работать. Это основание полки — без него остальные доски крепить не к чему. - Затем пишите по одному методу.
- После каждого метода запускайте тесты.
Два теста
Тестов два. Каждый тест создаёт свой склад, поэтому ошибка в одном не мешает другому.
TestInventory
TestInventory проверяет Add, Count, Names и удачный вызов Take.
TestTake
TestTake проверяет ошибки Take и товар, забранный целиком.
Сообщение о провале
Здесь аналогия с полкой заканчивается. Кривую полку видно сразу, а ошибку в коде показывают только тесты. Пройденная проверка ничего не печатает. Непройденная печатает строку с именем файла тестов и номером строки — так заготовка отвечает на первую проверку:
inventory_test.go:14: inv.Count("apple") = -1, want 8Давайте прочитаем эту строку:
- слева от
=стоит вызов из теста; - справа — то, что вернул ваш код;
- после
want— ожидаемое значение.
Здесь -1 пришло из заготовки: вы ещё не написали метод Count. Итог каждого теста показывает строка --- PASS или --- FAIL с его именем.
Исправляйте первую непройденную проверку: следующие часто не проходят из-за неё, потому что внутри теста результат вызова зависит от предыдущих.
Частая ошибка
Конструктор возвращает структуру, а map внутри неё не создан:
package main
type Inventory struct {
items map[string]int
}
func NewInventory() *Inventory {
return &Inventory{} // ✗
}
func main() {
inv := NewInventory()
inv.items["apple"] += 5
}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 и подпишись на канал — откроются все уроки.