Однажды судьба свела меня с ней. С первого взгляда я был ослеплен и долгое время не мог отвести от нее взгляд. Шло время, но она не переставала меня удивлять, иногда казалось, что я изучил ее вдоль и поперек, но она снова переворачивала все мои представления. Ее гибкости не было предела, а потом я узнал, что она умеет еще и… ООП!
Как-то я всерьез занялся покорением ООП в lua. И все, что я находил в интернете по этой теме, было вырвиглазными нагромождениями кода с обилием нижних подчеркиваний, которые никак не вписывались в элегантность этого языка. Поэтому я решил искать простое решение.
После прочтения множества умных книжек и разбора нескольких ужасных реализаций ООП, я, крупица за крупицей, собирал все самое полезное и простое, пока не выработал свой стиль объектно ориентированного программирования на lua.
Как видите, все очень просто. Если кто-то путается где ставить точку, а где двоеточие, правило следующее: если обращаемся к свойству — ставим точку (object.name), если к методу — ставим двоеточие (object:getName()).
Дальше интереснее.
Как известно, ООП держится на трех китах: наследование, инкапсуляция и полиморфизм. Проведем «разбор полетов» в этом же порядке.
Допустим, нам нужно создать класс унаследованный от предыдущего (Person).
Все работает, но лично мне не нравится такой вариант наследования, некрасиво. Поэтому я просто создаю глобальную функцию extended():
Теперь наследование классов выглядит куда красивее:
Все свойства и методы до этого момента в наших классах были публичные, но мы так же легко можем создавать и приватные:
Видите? Все почти так же как вы и привыкли.
Тут все еще проще.
Итак, что мы тут сделали?
— создали класс Person, с двумя методами: getName() и getName2(), первый из них защищен от переопределения;
— создали класс Woman и унаследовали его от класса Person;
— переопределили оба метода в классе Woman. Первый не переопределился;
— получили профит!
Кстати, открытые методы можно определять так же и вне тела класса:
А что делать, если нужно вызвать метод базового класса, который у нас переопределен? Это тоже делается легко!
Синтаксис таков: РодительскийКласс.Метод(сам_объект, параметры (если есть)).
На этом все, искренне надеюсь, что хоть кому-нибудь эта статья окажется полезной.
Напоследок приведу полный код, можете его скопипастить в IDE и убедиться в работоспособности.
Как-то я всерьез занялся покорением ООП в lua. И все, что я находил в интернете по этой теме, было вырвиглазными нагромождениями кода с обилием нижних подчеркиваний, которые никак не вписывались в элегантность этого языка. Поэтому я решил искать простое решение.
После прочтения множества умных книжек и разбора нескольких ужасных реализаций ООП, я, крупица за крупицей, собирал все самое полезное и простое, пока не выработал свой стиль объектно ориентированного программирования на lua.
Создание класса и экземпляра
class Person
--класс
Person= {}
--тело класса
function Person:new(fName, lName)
-- свойства
local obj= {}
obj.firstName = fName
obj.lastName = lName
-- метод
function obj:getName()
return self.firstName
end
--чистая магия!
setmetatable(obj, self)
self.__index = self; return obj
end
--создаем экземпляр класса
vasya = Person:new("Вася", "Пупкин")
--обращаемся к свойству
print(vasya.firstName) --> результат: Вася
--обращаемся к методу
print(vasya:getName()) --> результат: Вася
Как видите, все очень просто. Если кто-то путается где ставить точку, а где двоеточие, правило следующее: если обращаемся к свойству — ставим точку (object.name), если к методу — ставим двоеточие (object:getName()).
Дальше интереснее.
Как известно, ООП держится на трех китах: наследование, инкапсуляция и полиморфизм. Проведем «разбор полетов» в этом же порядке.
Наследование
Допустим, нам нужно создать класс унаследованный от предыдущего (Person).
class Woman
Woman = {}
--наследуемся
setmetatable(Woman ,{__index = Person})
--проверяем
masha = Woman:new("Марья","Ивановна")
print(masha:getName()) --->результат: Марья
Все работает, но лично мне не нравится такой вариант наследования, некрасиво. Поэтому я просто создаю глобальную функцию extended():
extended()
function extended (child, parent)
setmetatable(child,{__index = parent})
end
Теперь наследование классов выглядит куда красивее:
class Woman
Woman = {};
--наследуемся
extended(Woman, Person)
--проверяем
masha = Woman:new("Марья","Ивановна")
print(masha:getName()) --->результат: Марья
Инкапсуляция
Все свойства и методы до этого момента в наших классах были публичные, но мы так же легко можем создавать и приватные:
class Person
Person = {}
function Person:new(name)
local private = {}
--приватное свойство
private.age = 18
local public = {}
--публичное свойство
public.name = name or "Вася" -- "Вася" - это значение по умолчанию
--публичный метод
function public:getAge()
return private.age
end
setmetatable(public,self)
self.__index = self; return public
end
vasya = Person:new()
print(vasya.name) --> результат: Вася
print(vasya.age) --> результат: nil
print(vasya:getAge()) --> результат: 18
Видите? Все почти так же как вы и привыкли.
Полиморфизм
Тут все еще проще.
полиморфизм
Person = {}
function Person:new(name)
local private = {}
private.age = 18
local public = {}
public.name = name or "Вася"
--это защищенный метод, его нельзя переопределить
function public:getName()
return "Person protected "..self.name
end
--это открытый метод, его можно переопределить
function Person:getName2()
return "Person "..self.name
end
setmetatable(public,self)
self.__index = self; return public
end
--создадим класс, унаследованный от Person
Woman = {}
extended(Woman, Person) --не забываем про эту функцию
--переопределим защищенный метод
function Woman:getName()
return "Woman protected "..self.name
end
--переопределим метод getName2()
function Woman:getName2()
return "Woman "..self.name
end
--проверим
masha = Woman:new()
print(masha:getName()) --> Person protected Вася
print(masha:getName2()) --> Woman Вася
Итак, что мы тут сделали?
— создали класс Person, с двумя методами: getName() и getName2(), первый из них защищен от переопределения;
— создали класс Woman и унаследовали его от класса Person;
— переопределили оба метода в классе Woman. Первый не переопределился;
— получили профит!
Кстати, открытые методы можно определять так же и вне тела класса:
полиморфизм
Person = {}
function Person:new(name)
local private = {}
private.age = 18
local public = {}
public.name = name or "Вася"
--это защищенный метод, его нельзя переопределить
function public:getName()
return "Person protected "..self.name
end
setmetatable(public,self)
self.__index = self; return public
end
--это открытый метод, его можно
function Person:getName2()
return "Person "..self.name
end
А что делать, если нужно вызвать метод базового класса, который у нас переопределен? Это тоже делается легко!
Синтаксис таков: РодительскийКласс.Метод(сам_объект, параметры (если есть)).
class Woman
--создадим класс, унаследованный от Person
Woman = {}
extended(Woman, Person) --не забываем про эту функцию
--переопределим метод setName
function Woman:getName2()
return "Woman "..self.name
end
print(masha:getName2()) --> Woman Вася
--вызываем метод родительского класса
print(Person.getName2(masha)) --> Person Вася
Постскриптум
На этом все, искренне надеюсь, что хоть кому-нибудь эта статья окажется полезной.
Напоследок приведу полный код, можете его скопипастить в IDE и убедиться в работоспособности.
Полный код
function extended (child, parent)
setmetatable(child,{__index = parent})
end
Person = {}
function Person:new(name)
local private = {}
private.age = 18
local public = {}
public.name = name or "Вася"
--это защищенный метод, его нельзя переопределить
function public:getName()
return "Person protected "..self.name
end
--этот метод можно переопределить
function Person:getName2()
return "Person "..self.name
end
setmetatable(public,self)
self.__index = self;
return public
end
--создадим класс, унаследованный от Person
Woman = {}
extended(Woman, Person) --не забываем про эту функцию
--переопределим метод setName
function Woman:getName2()
return "Woman "..self.name
end
masha = Woman:new()
print(masha:getName2()) --> Woman Вася
--вызываем метод родительского класса
print(Person.getName2(masha)) --> Person Вася