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