Модуль управления является frontend — узлом проекта. Скрипт cvs.lua взаимодействует с исполнительными модулями, которые являются бизнес — логикой проекта.
В недавно я переделал строение модуля базы и исполнительных модулей, но на модуль управления это не повлияло. Он представляет собой тексто-графический «ламповый» интерфейс, в котором пользователь вводом цифры выбирает действие и параметры:
Модуль cvs.lua:
На самом деле становится обёрткой для остальной системы и работает, как циклический переключатель. Исполнительные процедуры отрабатывают свой функционал и возвращают управление центральному модулю. Точнее, его циклу управления.
Строка:
Используется для инициализации общего цикла при вызове самого модуля. Поскольку cvs.lua определён, как обычный модуль, то вызываться он автоматически не будет. Для этого в теле модуля, который находится между определением:
И командой возврата экземпляра модуля:
Пишется порядок команд и функций для инициализации. Фактически, здесь можно расписать порядок запуска различных функций и циклов исполнения, которые создадут любую произвольную логику. Функции здесь являются логическими элементами, повторяющимися в любом произвольном порядке и с любыми произвольными входными / исходящими данными.
Фактическки, я мог сделать локальный модуль без определения метатаблицы и структуры возврата (ибо структура позволяет). Я не сделал это по следующим причинам:
Итоговый вид управляющего модуля (извините, в xterm):
В следующей статье покажу работу нового модуля database.lua
P.S. Маленькая просьба — если хотите передать замечание по этому циклу статей, прошу предварительно прочитать статью «Разработка микро-учётной системы на lua, часть вторая. Постановка задачи», в которой я указываю условия разработки программы и функции, которые на неё возлагаются. Учитывайте это.
В недавно я переделал строение модуля базы и исполнительных модулей, но на модуль управления это не повлияло. Он представляет собой тексто-графический «ламповый» интерфейс, в котором пользователь вводом цифры выбирает действие и параметры:
Модуль cvs.lua:
local cvs = {}
function cvs.run() --[[ Локальное определение центральной функции. Наименование её может быть любое, главное, чтобы она вызывала сама себя :-D --]]
customer = require "customer"
document = require "document"
position = require "position"
feedback = require "feedback"
saldo = require "saldo"
init = nil
--[[ Определение исполнительных модулей: по этим адресам вызываются функции, определённые сценариями взаимодействия с пользователем. --]]
repeat
print("\n Выберите действие: \n")
print(": 0 : Выход из программы")
print(": 1 : Добавить клиента")
print(": 2 : Добавить позицию")
print(": 3 : Добавить документ")
print(": 4 : Добавить телефон клиента \n")
print(": 5 : Изменить наименование клиента")
print(": 6 : Изменить наименование позиции")
print(": 7 : Изменить стоимость позиции")
print(": 8 : Изменить выплату в документе")
print(": 9 : Изменить телефон клиента \n")
print(": 10 : Удалить клиента")
print(": 11 : Удалить позицию")
print(": 12 : Удалить документ")
print(": 13 : Удалить телефон клиента \n")
print(": 14 : Показать список клиентов")
print(": 15 : Показать список позиций \n")
print(": 16 : Показать баланс по клиенту")
print(": 17 : Показать общий баланс \n") --[[ Все процедуры строго определены списком use case. Остальные процедуры определяют background. --]]
init = tonumber( io.stdin:read() )
if init == 1 then --[[ Цикл определения операции. --]]
customer.add()
elseif init == 2 then
position.add()
elseif init == 3 then
document.add()
elseif init == 4 then
feedback.add()
elseif init == 5 then
customer.change()
elseif init == 6 then
position.change_nominal()
elseif init == 7 then
position.change_price()
elseif init == 8 then
document.change()
elseif init == 9 then
feedback.change()
elseif init == 10 then
customer.drop()
elseif init == 11 then
position.drop()
elseif init == 12 then
document.drop()
elseif init == 13 then
feedback.drop()
elseif init == 14 then
customer.show()
elseif init == 15 then
position.show()
elseif init == 16 then
saldo.customer()
elseif init == 17 then
saldo.common()
elseif init == 0 then
print("\n Завершение программы. \n")
else
print("\n Команда не корректна. \n")
end
until init == 0
customer = nil
document = nil
position = nil
feedback = nil
saldo = nil
init = nil --[[ По окончании просто сливаем экземпляры модулей. Таким образом очищаем программный мусор. --]]
end
cvs.run() --[[ Вот здесь мы инициализируем центральную функцию. Один раз и до конца исполнения. --]]
return cvs
На самом деле становится обёрткой для остальной системы и работает, как циклический переключатель. Исполнительные процедуры отрабатывают свой функционал и возвращают управление центральному модулю. Точнее, его циклу управления.
Строка:
cvs.run()
Используется для инициализации общего цикла при вызове самого модуля. Поскольку cvs.lua определён, как обычный модуль, то вызываться он автоматически не будет. Для этого в теле модуля, который находится между определением:
local cvs = {}
И командой возврата экземпляра модуля:
return cvs
Пишется порядок команд и функций для инициализации. Фактически, здесь можно расписать порядок запуска различных функций и циклов исполнения, которые создадут любую произвольную логику. Функции здесь являются логическими элементами, повторяющимися в любом произвольном порядке и с любыми произвольными входными / исходящими данными.
Фактическки, я мог сделать локальный модуль без определения метатаблицы и структуры возврата (ибо структура позволяет). Я не сделал это по следующим причинам:
- Неструктурированый код — большая проблема вне зависимости от того, какой модуль по размеру пишется;
- Модульная структура главного модуля позволяет делать меньше ошибок при планировании взаимодействия исполнительных модулей;
- Также она позволяет уменьшить количество «осечек» при конечной компилляции командой luac, ибо компиллятор будет точно знать — что с чем связано и из чего собирать;
- В конце — концов, структурированный код сам по себе выглядит «причёсаным» и удобочитаем.
Итоговый вид управляющего модуля (извините, в xterm):
В следующей статье покажу работу нового модуля database.lua
P.S. Маленькая просьба — если хотите передать замечание по этому циклу статей, прошу предварительно прочитать статью «Разработка микро-учётной системы на lua, часть вторая. Постановка задачи», в которой я указываю условия разработки программы и функции, которые на неё возлагаются. Учитывайте это.
Поделиться с друзьями
nick0x01
Используйте local для объявления переменных, ибо customer, document, position, feedback, saldo, init — глобальные.
Тут и так нет метатаблицы.
Lua позволяет делать очень клевый и действительно удобочитаемый код. Зачем столько print'ов и if-else?
hantenellotf
Можно разъяснить подробнее функционал этого кода?
nick0x01
Для определения доступных пользователю действий использовал таблицу actions — это массив с начальным индексом 0, элементы оного содержат всю необходимую информацию для действия — названия и само действие — собственно функция.
В вашем случае будет что-то вроде:
Обратите внимание, что action будет хранить адрес ф-ии (поля add таблицы customer, например), которую необходимо вызвать для данного действия, то есть там не вызов ф-ии. В title я бы хранил некий id (а не само названием), который можно использовать для локализации, но это не существенно. Еще бы добавил теги — буквенная строка, и использовал ее наравне с цифрами для выбора действия пользователя.
Далее в map_ просто выводится список доступных действий (это устраняет необходимость писать print для каждого элемента меню). После получения пользовательского ввода, в if проверяется есть ли такое действие (по индексу), и если все ок — выполняется: строка
Этим убирается монструозная конструкция ifelse'ов.
Посредством такого подхода автоматически решается вопрос добавления/изменения функционала, доступного пользователю — необходимо отредактировать только действия в actions, а остальной код останется неизменным. Разделение данных, бизнес-логики и UI.
Как-то так.
Еще такой момент, я бы вынес requires модулей за рамки ф-ии cvs.run — сделал бы их локальными для модуля (судя повашей архитектуре, это не повлечет нежелательных последствий), и таблицу actions так же бы сделал локальной для модуля cvs — это бы позволило бы лишь раз инициализировать данные переменные (небольшая оптимизация).