Найти тему

Объектно-ориентированное программирование на LUA

Всем привет!

Сегодня я расскажу о применении объектно-ориентированного программирования (ООП) на LUA. Ранее, я показывал, как можно его обойти. И, в целом, считаю, что, если без него можно обойтись, то нужно обходиться без него.

Но, к сожалению, не всегда без него удобно обходиться. Я в своих скриптах столкнулся с необходимостью сильного вложения данных. То есть, есть массив с перечнем бумаг, есть массив с записью всех свечей, в нем по каждой свече есть таблица с данными свечей, а среди них есть таблица времени. В общем, ужас какой-то. И не самое страшное написать так работающий код. Это половина дела. Если же через какое-то время встанет необходимость корректировать код, то там будет сложно разобраться.

Ну, ладно, надеюсь, что я вас убедил, что иногда применение ООП необходимо.

Материалы, на основе которых я постигал ООП в LUA:

1. Книга автора языка программирования Роберту Иерузалимски «Программирование на языке LUA» (но в книге достаточно сложно изложен данный вопрос);

2. Видео-ролик на youtube «Костыльный ООП (Объектно Ориентированное Программирование в Lua)» - https://www.youtube.com/watch?v=Sw7mpDUoRo0&t=718s. Тут достаточно хорошо все показано и рассказано.

Что же такое это ООП?

Есть простейшие типы данных. В том числе числа, строковые данные. В ООП основные операции идут с более сложным типом данных – с объектами. В свою очередь, объект представляет из себя некую сущность внутри которой есть переменные, которые называются свойствами и функции, которые называются методами.

Зачастую при объяснении ООП используют различные примеры. Я же буду показывать примеры на нашей области применения – на акциях.

Итак, вот пример объекта:

Stock = {
name = 'SBER',
code = 'TQBR',
price = 255,
lots = 12
}

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

Stock.name = 'ROSN'
Stock.price = 290

И так далее.

Это пример объекта только со свойствами, без методов. Давайте добавим какой-нибудь метод в данный объект. Например, это будет метод, который возвращает шаг цены по инструменту.

function Stock:giveStepPrice()
local info_po_bum = getSecurityInfo(self.code,self.name);
result = 0;
if (info_po_bum) then
result = info_po_bum["min_price_step"];
end
return result;
end

Обратите внимание, что запись «self» говорит, что обращение идет к текущему объекту, то есть к Stock и в вычислениях использует свойства этого объекта.

Теперь, если написать так:

aaa = Stock:giveStepPrice();

То, в переменной ааа будет находиться шаг цены по данному инструменту.

Таких методов в объекте может быть много.

Итак, надеюсь, что разобрались с этим.

В классических Си-подобных языках программирования при оперировании с ООП есть понятие класс – это своего рода шаблон для создания объектов. При описании класса указывают какие свойства и методы будут в объектах, которые будут создаваться на основе этого класса.

Например, в нашем случае могло бы быть. Класс акция, в котором указано, что у акции есть код, код класса, количество лотов, которыми мы будем работать, цена по которой мы будем работать и пр. Также класс может содержать методы определения текущей рыночной цены, определения шага цены и пр. И на основе этого класса могут создаваться конкретные объекты. Например, объект акций Сбера. В этом объекте уже будет прописано название – SBER, код класса – TQBR и т.д.

В Си-подобных языках программирования создание новых объектов на базе класса прописывается примерно так:

Stock_SBER = new Stock();

Где – Stock – класс, на основе которого создается объект Stock_SBER.

Так вот…. В LUA нет классов!

И автор данного языка программирования в качестве альтернативы предлагает создавать базовый объект, который будет являться шаблоном для создания других объектов. И, вот этот базовый объект как бы и будет классом.

Давайте напишем базовый объект Stock и на его базе создадим парочку других объектов-акций.

Вот так создаем базовый объект:

Stock = {
name = '',
code = 'TQBR',
price = 0,
lots = 0
}
function Stock:giveStepPrice()
local info_po_bum = getSecurityInfo(self.code,self.name);
result = 0;
if (info_po_bum) then
result = info_po_bum["min_price_step"];
end
return result;
end

На его базе создаются новые объекты так:

Stock_SBER = {}
setmetatable (Stock_SBER, {__index = Stock});
Stock_SBER.name = "SBER"
Stock_SBER.price = 255
Stock_SBER.lots = 12
Stock_ROSN = {}
setmetatable (Stock_ROSN, {__index = Stock});
Stock_ROSN.name = "ROSN"
Stock_ROSN.price = 444
Stock_ROSN.lots = 5

То есть, сначала объект инициализируется как таблица с помощью фигурных скобок, а потом с помощью такой комбинации он создается на основе базового объекта:

setmetatable (Stock_SBER, {__index = Stock});

Надеюсь, что Вам тут всё понятно, потому что мне самому многое в деталях не ясно.

Если после создания так двух объектов мы запустим такой код:

message("Stock_SBER.name = "..Stock_SBER.name);
message("Stock_SBER.code = "..Stock_SBER.code);
message("Stock_SBER.price = "..Stock_SBER.price);
message("Stock_SBER.lots = "..Stock_SBER.lots);
message("Stock_SBER.lots:giveStepPrice() = "..Stock_SBER:giveStepPrice())
message("===================================");
message("Stock_ROSN.name = "..Stock_ROSN.name);
message("Stock_ROSN.code = "..Stock_ROSN.code);
message("Stock_ROSN.price = "..Stock_ROSN.price);
message("Stock_ROSN.lots = "..Stock_ROSN.lots);
message("Stock_ROSN.lots:giveStepPrice() = "..Stock_ROSN:giveStepPrice())

То, в таблице сообщений получим следующее:

-2

Обратите внимание, что мы при создании объектов не меняли свойство code. Но, оно вывелось у обоих объектов как TQBR. Это значение было перенесено из базового объекта (класса).

Так же обратите внимание, что метод отработал и для каждой бумаги определил шаг цены.

В общем-то, на этом можно было бы закончить. Единственное, я хотел бы поделиться одной ситуацией, с которой сам столкнулся. Мне нужно было в свойство объекта с именем days_week добавить таблицу (массив). В нем указывалось в какой день недели идут торги по этой бумаге, а в какой не идут.

Дело в том, что если просто прописать базовый объект так:

days_week_baza = {}
Stock = {
name = '',
code = 'TQBR',
price = 0,
days_week = days_week_baza,
lots = 0
}

И потом на его основе создавать объекты, то будет проблема, связанная с тем, что у всех объектов будет не свой массив значений days_week, а ссылка на один и тот же массив.

Что бы избежать такой ситуации необходимо на базе таблицы days_week_baza создавать объект для каждой акции. То есть синтаксис должен быть такой при создании нового объекта акции:

week_days_sber = {}
setmetatable (week_days_sber, {__index = days_week_baza });
Stock_SBER = {}
setmetatable (Stock_SBER, {__index = Stock});
Stock_SBER. days_week = week_days_sber

То есть, сначала создается объект для свойства дней недели, потом создается объект акций. И потом прописывается, что в свойстве конкретной акции как день недели должен быть ранее созданный объект таблицы дней недели.

Надеюсь, что я вас не запутал.

🧐 Посмотреть код данного выпуска

В этой статье я показал некоторые основные моменты работы с ООП. Безусловно, ООП является достаточно сложной системой программирования. И на основе ООП строится множество различных механизмов, или паттернов проектирования. Так, в 1994 году группой авторов, которых в среде программистов называют «бандой четырех» была написана книга «Design Patterns». В этой книге авторы описали множество схем построения различных систем. И, во многом уровень программиста зависит от того насколько он посвящен в эти паттерны и умеет их применять на практике, используя тот язык программирования, на котором он специализируется.

На этом у меня все.

Всем удачи – всем пока!

⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇

📃 Краткое содержание данного канала.

➖➖➖➖➖➖➖➖➖➖➖➖➖

Landingcentr.ru - разработка сайтов для малого и среднего бизнеса.

⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆