Грядки как объекты
Грядка — это запись, и с ней работают две функции: growPlot и describePlot. Данные и функции, которые с ними работают, должны быть вместе, и для этого нужен класс, как в уроке про объекты. На этом шаге грядки становятся объектами класса Plot. Программа будет делать в точности то же, что и раньше, и её вывод вообще не изменится. Изменится только код: он станет аккуратнее. Изменение того, как написана программа, без изменения того, что она делает, называется рефакторингом, и программисты занимаются им постоянно.
Напомним, как работает класс, на примере одной из кур Эммы:
local Hen = {}
Hen.__index = Hen
function Hen.new(name)
local hen = {name = name, eggs = 0}
return setmetatable(hen, Hen)
end
function Hen:lay(count)
self.eggs = self.eggs + count
end
function Hen:__tostring()
return self.name .. " (яиц: " .. self.eggs .. ")"
end
local goldie = Hen.new("Златка")
goldie:lay(2)
goldie:lay(1)
print(goldie)
print("Лучшая курица: " .. tostring(goldie))Он выведет:
Златка (яиц: 3)
Лучшая курица: Златка (яиц: 3)Hen.new создаёт таблицу с данными курицы, назначает ей метатаблицу Hen и возвращает её: setmetatable возвращает таблицу, которую получила. Благодаря Hen.__index = Hen курица, у которой нет какого-то поля, например lay, ищет его в Hen. Двоеточие в goldie:lay(2) передаёт саму курицу как self. А __tostring говорит, что print и tostring сделают из курицы (%s в string.format тоже его использует). Но .. его не использует, поэтому "Лучшая курица: " .. goldie закончилось бы ошибкой. Вот почему в примере вызывается tostring(goldie).
То же относится к table.concat. Если добавить в список дня сами грядки вместо их строк, программа остановится:
main.lua:108: invalid value (table) at index 1 in table for 'concat'Поэтому добавляйте в список tostring(plot). Другая ошибка — точка вместо двоеточия. plot.grow(growth[today]) передаёт число дней как self, и метод не может прочитать поле числа:
main.lua:44: attempt to index a number value (local 'self')Сбор урожая в главной программе можно оставить как есть. Объект — по-прежнему таблица, поэтому plot.grown и plot.crop работают, как раньше.
Задание
- 1
Под
findCropсоздайте классPlot:local Plot = {}иPlot.__index = Plot. Затем напишитеPlot.new(crop, seeds), которая создаёт таблицу{crop = crop, seeds = seeds, grown = 0}, назначает ей метатаблицуPlotс помощьюsetmetatableи возвращает её. - 2
Превратите две функции в методы.
Plot:grow(amount)делает то же, что иgrowPlot, только сselfвместоplot, аPlot:__tostring()возвращает тот же текст, что иdescribePlot. - 3
Используйте класс в главной программе: сажайте с помощью
Plot.new(crop, item.seeds), растите грядки с помощьюplot:grow(growth[today])и добавляйте в список дняtostring(plot). Затем удалите функцииgrowPlotиdescribePlot, потому что они больше не нужны. (Если вызов одной из них где-то останется, программа остановится сattempt to call a nil value (global 'growPlot').) Вывод остаётся точно таким же, как на предыдущем шаге:СПИСОК ЦЕН морковь: семена 2, продажа 5 тыква: семена 5, продажа 20 клубника: семена 4, продажа 12 пшеница: семена 1, продажа 3 Грядка 1: морковь x4, стоимость: 8 Грядка 2: пшеница x6, стоимость: 6 Грядка 3: тыква x2, стоимость: 10 Не хватает монет: клубника x2 Монеты: 6 День 1 (солнечно): морковь 1/3, пшеница 1/2, тыква 1/6 День 2 (солнечно): морковь 2/3, пшеница 2/2, тыква 2/6 День 3 (дождливо): морковь 3/3, пшеница 2/2, тыква 4/6 День 4 (облачно): морковь 1/3, пшеница 1/2, тыква 5/6 День 5 (солнечно): морковь 2/3, пшеница 2/2, тыква 6/6 День 6 (облачно): морковь 3/3, пшеница 1/2, тыква 1/6 Рынок: морковь 8 x 5, пшеница 18 x 3, тыква 2 x 20 = 134 Конец сезона. Монеты: 140