Lua, ООП и ничего лишнего

от автора

Однажды судьба свела меня с ней. С первого взгляда я был ослеплен и долгое время не мог отвести от нее взгляд. Шло время, но она не переставала меня удивлять, иногда казалось, что я изучил ее вдоль и поперек, но она снова переворачивала все мои представления. Ее гибкости не было предела, а потом я узнал, что она умеет еще и… ООП!

Как-то я всерьез занялся покорением ООП в 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()) 

Как видите, все очень просто. Если кто-то путается где ставить точку, а где двоеточие, правило следующее: если обращаемся к свойству — ставим точку (obj.name), если к методу — ставим двоеточие (obj:getName()).

Дальше интереснее.

Как известно, ООП держится на трех китах: наследование, инкапсуляция и полиморфизм. Проведем «разбор полетов» в этом же порядке.

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

Допустим, нам нужно создать класс унаследованный от предыдущего (Person).

class Wooman

Wooman = {} --наследуемся setmetatable(Wooman ,{__index = Person})  --проверяем masha = Wooman:new("Марья","Ивановна") print(masha:getName())  --->результат: Марья 

Все работает, но лично мне не нравится такой вариант наследования, не красиво. Поэтому я просто создаю глобальную функцию extended():

extended()

function extended (child, parent)     setmetatable(child,{__index = parent})  end 

Теперь наследование классов выглядит куда красивее:

class Wooman

Wooman = {}; --наследуемся  extended(Wooman, Person) --проверяем masha = Wooman:new("Марья","Ивановна") print(masha:getName())  --->результат: Марья 

Инкапсуляция

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

class Person

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;
— получили профит!

А что делать, если нужно вызвать метод базового класса, который у нас переопределен? Это тоже делается легко!
синтаксис таков: РодительскийКласс.Метод(сам_объект, параметры).

class 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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *