Перейти к содержимому


Фотография

Другой способ реализации Объектно-Ориентированного Программирования

ООП Lua

  • Авторизуйтесь для ответа в теме
Сообщений в теме: 35

#1 Оффлайн   Ktlo

Ktlo
  • Пользователи
  • Сообщений: 115
  • Уровень сигнала: 0%
  • В игре: 0 час. 0 мин.

Награды

                 

Отправлено 09 Июнь 2015 - 21:10

Я видел статью про ООП (Объектно-Ориентированного Программирования), написанную на этом форуме, и вот, что могу сказать, он может быть немного непонятен новичкам в Lua. Я всегда пользовался другим способом создания "классов".

Сначала следует разобраться со значениями. Класс - это набор методов (в нашем случае функций), процедур и переменных которые дальше будут наследоваться объектом или другим классом. Объектами называют сущности, обладающие набором свойств и операций над ними. Объект - это производное класса.

 

В Lua классов как таковых нет, но если пофантазировать, то можно представить функцию как класс, а объект как таблицу.

 

Создание простого псевдо-класса

Спойлер

 

 

Наследование

Спойлер

 

 

Экономия оперативной памяти

Спойлер

 

 

Только читаемые свойства

Спойлер

 

Вывод. Этот способ реализации ООП в Lua будет работать и в OC и в СС. "Классы" очень похожи на классы из других языков программирования. В моём варианте нет двоеточия, которое приносит странную возможность обработки одного объекта методом другого. Использовать ООП в игре можно для удобной разметки интерфейса в вашей программе (например для создания кнопок), и для экономии системных ресурсов в вашем компьютере внутри компьютера.


Сообщение отредактировал Ktlo: 23 Июнь 2015 - 17:06

  • 1Ridav, Krutoy, Fingercomp и 3 другим это нравится

#2 Оффлайн   MineUbuntu

MineUbuntu
  • Пользователи
  • Сообщений: 185
  • Уровень сигнала: 4,72%
  • В игре: 35 час. 33 мин.

Награды

     

Отправлено 09 Июнь 2015 - 21:17

Обычно натыкаюсь на реализацию классов в виде таблиц. Теперь наконец-то смогу избавиться от двоеточия при вызове функций.

#3 Оффлайн   artem211

artem211
  • Пользователи
  • Сообщений: 188
  • Уровень сигнала: 0%
  • В игре: 0 час. 0 мин.
  • ГородСанкт-Петербург

Награды

              

Отправлено 09 Июнь 2015 - 23:23

И зачем так сложно? Не проще либу стандартную написать? и вызывать также require("liba")

local API = {}

 

function API.hello()

 print("Hello!")

end

 

API.var1 = 12345324

API.var2 = true

API.var3 = "aasdp"

...

...

...

return API


Сообщение отредактировал artem211: 09 Июнь 2015 - 23:24


#4 Онлайн   LeshaInc

LeshaInc
  • Пользователи
  • Сообщений: 1 206
  • Уровень сигнала: 15,55%
  • В игре: 117 час. 1 мин.
  • ГородЛуна

Награды

                       

Отправлено 09 Июнь 2015 - 23:26

И зачем так сложно? Не проще либу стандартную написать? и вызывать также require("liba")
local API = {}
 
function API.hello()
 print("Hello!")
end
 
API.var1 = 12345324
API.var2 = true
API.var3 = "aasdp"
...
...
...
return API


Понты, понты.
  • Ktlo это нравится

#5 Оффлайн   Krutoy

Krutoy
  • Гуру
  • Сообщений: 499
  • Уровень сигнала: 0%
  • В игре: 0 час. 0 мин.

Награды

              

Отправлено 10 Июнь 2015 - 06:16

Ну вот, другое дело. Вот это уже "правильный" "ооп" на луа. good.gif



#6 Оффлайн   Zer0Galaxy

Zer0Galaxy
  • Гуру
  • Сообщений: 1 228
  • Уровень сигнала: 0%
  • В игре: 0 час. 0 мин.

Награды

   5                              

Отправлено 10 Июнь 2015 - 09:46

И зачем так сложно? Не проще либу стандартную написать? и вызывать также require("liba")

local API = {}

 

function API.hello()

 print("Hello!")

end

 

API.var1 = 12345324

API.var2 = true

API.var3 = "aasdp"

...

...

...

return API

В твоем случае либа всегда будет возвращать указатель на один и тот же объект

Obj1=require("liba")
Obj2=require("liba")

Obj1.var1=54321
print(Obj2.var1)
-->54321

т.е. при изменении поля одного объекта изменятся соответствующие поля других объектов этого же класса.

В случае же, который предложил Ktlo, создаются новые переменные для полей нового объекта и новые функции для методов.

 

 

Я видел статью про ООП (Объектно-Ориентированного Программирования), написанную на этом форуме, и вот, что могу сказать, он может быть немного непонятен новичкам в Lua. Я всегда пользовался другим способом создания "классов".

Я тоже начинал рассмотрение ООП в Луа именно с такого способа, но мне не понравилось в нем то, что для каждого экземпляра объекта создаются свои экземпляры функций-методов. Т.е сколько ты объектов создашь, столько идентичных функций будет храниться в памяти компьютера.

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

С нетерпением жду описания наследования.

 

PS: далось вам это двоеточие. Вы гоняетесь за внешней похожестью, а ООП на самом деле это не синтаксис компилятора, а уровень мышления программиста.


Сообщение отредактировал Zer0Galaxy: 10 Июнь 2015 - 09:50


#7 Онлайн   LeshaInc

LeshaInc
  • Пользователи
  • Сообщений: 1 206
  • Уровень сигнала: 15,55%
  • В игре: 117 час. 1 мин.
  • ГородЛуна

Награды

                       

Отправлено 10 Июнь 2015 - 10:35

94НН03 С006Щ3НN3 П0К4ЗЫ8437, К4КN3 У9N8N73ЛЬНЫ3 83ЩN М0Ж37 93Л47Ь Н4Ш Р4ЗУМ! 8П3Ч47ЛЯЮЩN3 83ЩN! СН4Ч4Л4 Э70 6ЫЛ0 7РУ9Н0, Н0 С3ЙЧ4С Н4 Э70Й С7Р0К3 84Ш Р4ЗУМ ЧN7437 Э70 4870М47NЧ3СКN, Н3 З49УМЫ84ЯСЬ 06 Э70М. Г0Р9NСЬ. ЛNШЬ 0ПР393Л3ННЫ3 ЛЮ9N М0ГУ7 ПР0ЧN747Ь Э70.


Любой программист это прочитает.
  • Krutoy, MineUbuntu и Metall_East это нравится

#8 Оффлайн   SergOmarov

SergOmarov
  • Пользователи
  • Сообщений: 564
  • Уровень сигнала: 0,35%
  • В игре: 2 час. 36 мин.

Награды

     

Отправлено 15 Июнь 2015 - 16:19

В твоем случае либа всегда будет возвращать указатель на один и тот же объект

Obj1=require("liba")
Obj2=require("liba")

Obj1.var1=54321
print(Obj2.var1)
-->54321

т.е. при изменении поля одного объекта изменятся соответствующие поля других объектов этого же класса.

В случае же, который предложил Ktlo, создаются новые переменные для полей нового объекта и новые функции для методов.

 

 

Я тоже начинал рассмотрение ООП в Луа именно с такого способа, но мне не понравилось в нем то, что для каждого экземпляра объекта создаются свои экземпляры функций-методов. Т.е сколько ты объектов создашь, столько идентичных функций будет храниться в памяти компьютера.

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

С нетерпением жду описания наследования.

 

PS: далось вам это двоеточие. Вы гоняетесь за внешней похожестью, а ООП на самом деле это не синтаксис компилятора, а уровень мышления программиста.

а почему бы не вызвать функцию прямо из класса? или после инициализции удалять объект импортированной функции...



#9 Оффлайн   Zer0Galaxy

Zer0Galaxy
  • Гуру
  • Сообщений: 1 228
  • Уровень сигнала: 0%
  • В игре: 0 час. 0 мин.

Награды

   5                              

Отправлено 15 Июнь 2015 - 18:07

а почему бы не вызвать функцию прямо из класса? или после инициализции удалять объект импортированной функции...

Приведи пример, пожалуйста. А то не понятно какую функцию из какого класса? Зачем удалять какой объект?



#10 Оффлайн   SergOmarov

SergOmarov
  • Пользователи
  • Сообщений: 564
  • Уровень сигнала: 0,35%
  • В игре: 2 час. 36 мин.

Награды

     

Отправлено 16 Июнь 2015 - 04:38

Приведи пример, пожалуйста. А то не понятно какую функцию из какого класса? Зачем удалять какой объект?
 

 

local class = require("имя_библиотеки").class

Эту переменную надо очистить после создания всех экземпляров класса

Либо, дергать так:

require("имя_библиотеки").class()


Сообщение отредактировал SergOmarov: 16 Июнь 2015 - 04:39


#11 Оффлайн   Zer0Galaxy

Zer0Galaxy
  • Гуру
  • Сообщений: 1 228
  • Уровень сигнала: 0%
  • В игре: 0 час. 0 мин.

Награды

   5                              

Отправлено 16 Июнь 2015 - 10:59

Нет. Ты не путай функцию библиотеки и функцию класса.

local class = require("имя_библиотеки").class

В данном случае переменная class будет содержать указатель на уже существующую функцию и занимать в памяти аж восемь байт. Но это, к сожалению, не имеет никакого отношения к ООП поскольку класс это не тоже самое, что и библиотека.

Способ же, который упомянул топикстартер, действительно создает объект. Но для каждого экземпляра объекта такой способ создает новые экземпляры функций-методов, а функции могут занимать в памяти килобайты.


  • Alex это нравится

#12 Онлайн   LeshaInc

LeshaInc
  • Пользователи
  • Сообщений: 1 206
  • Уровень сигнала: 15,55%
  • В игре: 117 час. 1 мин.
  • ГородЛуна

Награды

                       

Отправлено 16 Июнь 2015 - 11:14

Если нужно наследование, можно сделать простейший класс, но с двоеточием (не знаю что вы его пугаетесь (?)):
local class = {a,b}   -- классом может послужить обычная таблица
function class:method(a,b) -- это метод класса, например, пусть меняет значения 
   self.a = a.            -- self указывает на нашу таблицу "class", это синтаксический сахар из-за двоеточия.  
   self.b = b
end
function class:new() -- этим методом мы будем создавать новый объект. Вот тут и начинается магия наследования. 
   local answ = {}  -- таблица которую в конце функции мы возвратим.
   self.__index = self -- теперь при обращении к методом класса будут обращаться именно к его методам. (Сам не до конца понял)
   setmetatable(answ, self) --  магия, указываем метатаблицу answ к классу class 
   return answ  -- возвращаем результат.
end
Сдалось вам это двоеточие.

Сообщение отредактировал LeshaInc: 16 Июнь 2015 - 11:39


#13 Оффлайн   Zer0Galaxy

Zer0Galaxy
  • Гуру
  • Сообщений: 1 228
  • Уровень сигнала: 0%
  • В игре: 0 час. 0 мин.

Награды

   5                              

Отправлено 16 Июнь 2015 - 12:21

Если нужно наследование, можно сделать простейший класс, но с двоеточием (не знаю что вы его пугаетесь (?)):
Хм. Тема называется "ДРУГОЙ способ реализации ...". То о чем пишешь ты это, надо полагать, способ первый.

#14 Оффлайн   SergOmarov

SergOmarov
  • Пользователи
  • Сообщений: 564
  • Уровень сигнала: 0,35%
  • В игре: 2 час. 36 мин.

Награды

     

Отправлено 16 Июнь 2015 - 17:34

Нет. Ты не путай функцию библиотеки и функцию класса.

local class = require("имя_библиотеки").class

В данном случае переменная class будет содержать указатель на уже существующую функцию и занимать в памяти аж восемь байт. Но это, к сожалению, не имеет никакого отношения к ООП поскольку класс это не тоже самое, что и библиотека.

Способ же, который упомянул топикстартер, действительно создает объект. Но для каждого экземпляра объекта такой способ создает новые экземпляры функций-методов, а функции могут занимать в памяти килобайты.

А, вот ты о чем, а я не понял) Да, на счет этого ты прав, но ведь функции можно хранить и внешним образом, просто вне объекта Me. А ссылаться так как показано в "сложном способе" ооп.


Сообщение отредактировал SergOmarov: 16 Июнь 2015 - 17:35


#15 Оффлайн   Ktlo

Ktlo
  • Автор темы
  • Пользователи
  • Сообщений: 115
  • Уровень сигнала: 0%
  • В игре: 0 час. 0 мин.

Награды

                 

Отправлено 22 Июнь 2015 - 23:43

Всё я закончил с ООП, если есть вопросы, спрашивайте.



#16 Оффлайн   Zer0Galaxy

Zer0Galaxy
  • Гуру
  • Сообщений: 1 228
  • Уровень сигнала: 0%
  • В игре: 0 час. 0 мин.

Награды

   5                              

Отправлено 23 Июнь 2015 - 10:05

В моём варианте нет двоеточия, которое приносит странную возможность обработки одного объекта методом другого.
Это как? Так что ли?
Obj1:Metod1()     --Вызов метода объекта 1 (нормальный)
Obj2:Metod2()     --Вызов метода объекта 2 (нормальный)
Obj1.Metod1(Obj2) --Вызов метода объекта 1 для объекта 2 (странный) 

Был несколько разочарован реализацией readonly-полей. Я надеялся на:

print(Obj.ReadOnlyVar)  -- читаем поле
--> 123
Obj.ReadOnlyVar=456     -- пытаемся изменить
print(Obj.ReadOnlyVar)  -- повторно читаем поле
--> 123



#17 Оффлайн   Ktlo

Ktlo
  • Автор темы
  • Пользователи
  • Сообщений: 115
  • Уровень сигнала: 0%
  • В игре: 0 час. 0 мин.

Награды

                 

Отправлено 23 Июнь 2015 - 14:08

Это как? Так что ли?

Obj1:Metod1()     --Вызов метода объекта 1 (нормальный)
Obj2:Metod2()     --Вызов метода объекта 2 (нормальный)
Obj1.Metod1(Obj2) --Вызов метода объекта 1 для объекта 2 (странный) 

Да, так.

 

Был несколько разочарован реализацией readonly-полей. Я надеялся на:

print(Obj.ReadOnlyVar)  -- читаем поле
--> 123
Obj.ReadOnlyVar=456     -- пытаемся изменить
print(Obj.ReadOnlyVar)  -- повторно читаем поле
--> 123

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



#18 Оффлайн   SergOmarov

SergOmarov
  • Пользователи
  • Сообщений: 564
  • Уровень сигнала: 0,35%
  • В игре: 2 час. 36 мин.

Награды

     

Отправлено 23 Июнь 2015 - 15:25

Можно)


переопределить оператор присваивания



#19 Оффлайн   Ktlo

Ktlo
  • Автор темы
  • Пользователи
  • Сообщений: 115
  • Уровень сигнала: 0%
  • В игре: 0 час. 0 мин.

Награды

                 

Отправлено 23 Июнь 2015 - 16:32

Можно)


переопределить оператор присваивания

Как?



#20 Онлайн   LeshaInc

LeshaInc
  • Пользователи
  • Сообщений: 1 206
  • Уровень сигнала: 15,55%
  • В игре: 117 час. 1 мин.
  • ГородЛуна

Награды

                       

Отправлено 23 Июнь 2015 - 16:36

Метатаблицы Карл...

#21 Оффлайн   Zer0Galaxy

Zer0Galaxy
  • Гуру
  • Сообщений: 1 228
  • Уровень сигнала: 0%
  • В игре: 0 час. 0 мин.

Награды

   5                              

Отправлено 23 Июнь 2015 - 16:42

Приз - компьютер или робот любой комплектации (кроме креатива) тому, кто реализует readonly-поле объекта через переопределение оператора присваивания.



#22 Оффлайн   Totoro

Totoro
  • Хранители Кода
  • Сообщений: 1 735
  • Уровень сигнала: 0,29%
  • В игре: 2 час. 13 мин.

Награды

                                      

Отправлено 23 Июнь 2015 - 16:47

Метатаблицы Карл...

 

Как ты это сделаешь для "примитивных" типов?



#23 Оффлайн   Krutoy

Krutoy
  • Гуру
  • Сообщений: 499
  • Уровень сигнала: 0%
  • В игре: 0 час. 0 мин.

Награды

              

Отправлено 23 Июнь 2015 - 16:48

Приз - компьютер или робот любой комплектации (кроме креатива) тому, кто реализует readonly-поле объекта через переопределение оператора присваивания.

Всё уже реализовано. Вот тут например вообще можно устанавливать свои функции для get и set переменной.

 

Как ты это сделаешь для "примитивных" типов?

Можно можно. Даже цифры, и даже нил. 



#24 Оффлайн   Totoro

Totoro
  • Хранители Кода
  • Сообщений: 1 735
  • Уровень сигнала: 0,29%
  • В игре: 2 час. 13 мин.

Награды

                                      

Отправлено 23 Июнь 2015 - 16:52

Можно можно. Даже цифры, и даже нил. 

Узнаю Луа.



#25 Оффлайн   Ktlo

Ktlo
  • Автор темы
  • Пользователи
  • Сообщений: 115
  • Уровень сигнала: 0%
  • В игре: 0 час. 0 мин.

Награды

                 

Отправлено 23 Июнь 2015 - 17:11

Можно)


переопределить оператор присваивания

 

Метатаблицы Карл...

Я почитал про метатаблицы поподробнее и всё исправил. Оказывается запретить изменение ключей можно.



#26 Оффлайн   swg2you

swg2you
  • Пользователи
  • Сообщений: 268
  • Уровень сигнала: 0%
  • В игре: 0 час. 0 мин.

Награды

              

Отправлено 23 Июнь 2015 - 17:22

ro = {a=1, b=2}
obj = {b=3, c=4}
setmetatable(obj,{__index=ro,__newindex=function(_,_,_)end})

prn(obj.a, obj.b, obj.c) -->1 3 4 (значение obj.b=3 установленное до объявления метаметодов перекрывает ro.b=2, и __index=ro не вызывается.
obj.a=5
obj.b=6
obj.c=7
prn(obj.a, obj.b, obj.c) -->1 6 7 (obj.a=5 выполнил function(_,_,_)end ничего не сделав)
 
Эта строка setmetatable(obj, {__index=ro, __newindex=function(_,_,_)end}) говорит следующее:
Для несуществующих индексов таблицы obj, читать значения из ro, а при попытке записи выполнять ничего function(_,_,_)end
 
Или так: setmetatable(obj,{__index=ro,__newindex=function(t,k,v) print('Иди в пень! '..k..' ' - ридонли!')  end})
 
--===--

При попытке добавления нового значения (например obj.d=8) выполнится ничего и значение добавлено не будет.
Чтобы мы могли добавлять новые индексы в obj нужно в нашем ничего проверять, есть ли добавляемый индекс в ro, и если такого индекса нет, то добавлять его в obj.
 
Это можно сделать так: setmetatable(obj, {__index=ro,__newindex=function(t,k,v) local _=rawget(ro,k) or rawset(t,k,v) end})
При попытке записи obj.d=8, наше ничего, сделает: если не ro.k то rawset(t,k,v) наплевав на всякие там метаметамета.
 
Теперь мы можем:
ro = {a=1, b=2}
obj = {b=3, c=4}
setmetatable(obj,{__index=ro,__newindex=function(t,k,v) local _=rawget(ro,k) or rawset(t,k,v) end})

prn(obj.a, obj.b, obj.c, obj.d)
obj.a=5
obj.b=6
obj.c=7
obj.d=8
prn(obj.a, obj.b, obj.c, obj.d)
upd:
Прочел следующие посты и понял, как я далек от объектно-ориентированного програмирования )

Сообщение отредактировал swg2you: 23 Июнь 2015 - 19:52


#27 Оффлайн   Zer0Galaxy

Zer0Galaxy
  • Гуру
  • Сообщений: 1 228
  • Уровень сигнала: 0%
  • В игре: 0 час. 0 мин.

Награды

   5                              

Отправлено 23 Июнь 2015 - 18:08

Уже ближе. Вот пример реализации класса с двумя обычными полями (c,d) и двумя readonly-полями (a,b)

Спойлер

RO-поля можно менять при помощи методов класса. Одна проблема - RO-поля общие для всех экземпляров класса

И можно ли будет от такого класса сделать наследника если невозможно добавлять новые поля?


Сообщение отредактировал Zer0Galaxy: 23 Июнь 2015 - 18:11


#28 Оффлайн   Ktlo

Ktlo
  • Автор темы
  • Пользователи
  • Сообщений: 115
  • Уровень сигнала: 0%
  • В игре: 0 час. 0 мин.

Награды

                 

Отправлено 23 Июнь 2015 - 18:22

И можно ли будет от такого класса сделать наследника если невозможно добавлять новые поля?

Можно:

function newClass()
      local object, private = class()
      setmetatable(object, {}) --Удаляем метатаблицу
      object.foo(...) --Добавляем метод
            --<Что-нибудь>
      end
      setmetatable(object, private) --Возвращаем нашу метатаблицу назад
      return object, private
end


#29 Оффлайн   Zer0Galaxy

Zer0Galaxy
  • Гуру
  • Сообщений: 1 228
  • Уровень сигнала: 0%
  • В игре: 0 час. 0 мин.

Награды

   5                              

Отправлено 23 Июнь 2015 - 18:39

При попытке добавления нового значения (например obj.d=8) выполнится ничего и значение добавлено не будет. Чтобы мы могли добавлять новые индексы в obj нужно в нашем ничего проверять, есть ли добавляемый индекс в ro, и если такого индекса нет, то добавлять его в obj.   Это можно сделать так: setmetatable(obj, {__index=ro,__newindex=function(t,k,v) local _=rawget(ro,k) or rawset(t,k,v) end}) При попытке записи obj.d=8, наше ничего, сделает если не ro.k то rawset(t,k,v) наплевав на всякие там метаметамета.
Так, а как сделать что бы таблица ro была не одна на всех, а своя для каждого экземпляра класса?

#30 Оффлайн   swg2you

swg2you
  • Пользователи
  • Сообщений: 268
  • Уровень сигнала: 0%
  • В игре: 0 час. 0 мин.

Награды

              

Отправлено 23 Июнь 2015 - 19:18

Так, а как сделать что бы таблица ro была не одна на всех, а своя для каждого экземпляра класса?

Я далек от ООП, но думаю, что как-то так.
setmetatable(obj1, {__index=ro1,__newindex=function(t,k,v) local _=rawget(ro1,k) or rawset(t,k,v) end})
setmetatable(obj2, {__index=ro2,__newindex=function(t,k,v) local _=rawget(ro2,k) or rawset(t,k,v) end})

Или по твоему коду:
Class={a=1, b=2}

function Class:new()
  local obj={c=3, d=4}
  local nc={}
  for k,v in pairs(self) do nc[k]=v end
  setmetatable(obj,{__index=nc,__newindex=function(t,k,v) local _=rawget(nc,k) or rawset(t,k,v) end})
  return obj
end

function Class:ShowFields()
  prn(self.a, self.b, self.c, self.d)
end

function Class:SetA(Val)
  rawset(self,'a',Val)
end

function Class:SetB(Val)
  rawset(self,'b',Val)
end

obj = Class:new() 
obj2 = Class:new()

obj:ShowFields()
obj.a=5
obj.b=6
obj.c=7
obj.d=8
obj:ShowFields()
obj:SetA(5)
obj:SetB(6)
obj:ShowFields()
obj2:ShowFields()
Если я правильно понял, что тебе от этого кода нужно.

Сообщение отредактировал swg2you: 23 Июнь 2015 - 19:45






Темы с аналогичным тегами ООП, Lua

Количество пользователей, читающих эту тему: 0

0 пользователей, 0 гостей, 0 анонимных