portaldacalheta.pt
  • Основен
  • Инструменти И Уроци
  • Agile Talent
  • Продукти Хора И Екипи
  • Растеж На Приходите
Технология

Метапрограмирането на Ruby е дори по-хладно, отколкото звучи



Често чувате, че метапрограмирането е нещо, което използват само нинджите Ruby, и че то просто не е за обикновените смъртни. Но истината е, че метапрограмирането изобщо не е нещо страшно. Тази публикация в блога ще служи за оспорване на този тип мислене и за доближаване на метапрограмирането до средния разработчик на Ruby, така че те също да могат да се възползват от предимствата му.

Ruby Metaprogramming: Код за писане на код Tweet

Трябва да се отбележи, че метапрограмирането може да означава много и често може да бъде много злоупотребено и да стигне до крайност, когато става въпрос за използване, така че ще се опитам да добавя някои реални примери, които всеки би могъл да използва в ежедневното програмиране.



Метапрограмиране

Метапрограмиране е техника, чрез която можете да напишете код, който пише код сам по себе си динамично по време на изпълнение. Това означава, че можете да дефинирате методи и класове по време на изпълнение. Луд, нали? Накратко, с помощта на метапрограмирането можете да отворите и модифицирате класове, да улавяте несъществуващи методи и да ги създавате в движение, да създавате код, който е СУХА чрез избягване на повторения и др.



Основите

Преди да се потопим в сериозно метапрограмиране, трябва да проучим основите. И най-добрият начин да направите това е чрез пример. Нека започнем с едно и да разберем Ruby метапрограмирането стъпка по стъпка. Вероятно се досещате какво прави този код:



class Developer def self.backend 'I am backend developer' end def frontend 'I am frontend developer' end end

Дефинирали сме клас с два метода. Първият метод в този клас е метод на класа, а вторият е метод на екземпляр. Това са основни неща в Ruby, но зад този код се случват много повече неща, които трябва да разберем, преди да продължим по-нататък. Струва си да се отбележи, че класът Developer всъщност е обект. В Ruby всичко е обект, включително класове. Тъй като Developer е екземпляр, това е екземпляр на клас Class. Ето как изглежда обектният модел Ruby:

как да изтеглите hls стрийминг

Рубинен обектен модел



p Developer.class # Class p Class.superclass # Module p Module.superclass # Object p Object.superclass # BasicObject

Едно важно нещо, което трябва да разберете тук, е значението на self. frontend method е обикновен метод, който е наличен за екземпляри от клас Developer, но защо е backend метод клас метод? Всяко парче код, изпълнено в Ruby, се изпълнява срещу определено себе си . Когато интерпретаторът на Ruby изпълнява който и да е код, той винаги следи стойността self за дадена линия. self винаги се отнася до някакъв обект, но този обект може да се промени въз основа на изпълнения код. Например, в дефиницията на клас, self се отнася до самия клас, който е екземпляр на клас Class.

class Developer p self end # Developer

Вътрешни методи, self се отнася до екземпляр на класа.



class Developer def frontend self end end p Developer.new.frontend # #

Вътрешни методи на класа, self се отнася до самия клас по начин (който ще бъде разгледан по-подробно по-нататък в тази статия):

class Developer def self.backend self end end p Developer.backend # Developer

Това е добре, но какво все пак е метод на клас? Преди да отговорим на този въпрос, трябва да споменем съществуването на нещо, наречено метаклас, известен също като единичен клас и собствен клас. Метод на класа frontend което дефинирахме по-рано, не е нищо друго освен метод на екземпляр, дефиниран в метакласа за обекта Developer! Метакласът по същество е клас, който Ruby създава и вмъква в йерархията на наследяване, за да съдържа методи на клас, като по този начин не се намесва в екземпляри, създадени от класа.



Метакласи

Всеки обект в Ruby има свой собствен метаклас . Той е някак невидим за разработчика, но е там и можете да го използвате много лесно. Тъй като нашият клас Developer по същество е обект, той има свой собствен метаклас. Като пример нека създадем обект от клас String и манипулирайте неговия метаклас:

example = 'I'm a string object' def example.something self.upcase end p example.something # I'M A STRING OBJECT

Това, което направихме тук, е, че добавихме сингълтън метод something към обект. Разликата между методите на класа и методите на единичен метод е, че методите на класа са достъпни за всички екземпляри на обект на клас, докато методите на единичен клас са достъпни само за този единичен екземпляр. Методите на класове се използват широко, докато единичните методи не толкова, но и двата типа методи се добавят към метаклас на този обект.



Предишният пример може да бъде пренаписан по следния начин:

example = 'I'm a string object' class << example def something self.upcase end end

Синтаксисът е различен, но на практика прави едно и също нещо. Сега да се върнем към предишния пример, където създадохме Developer клас и изследвайте някои други синтаксиси, за да дефинирате метод на клас:



class Developer def self.backend 'I am backend developer' end end

Това е основна дефиниция, която почти всеки използва.

def Developer.backend 'I am backend developer' end

Това е същото нещо, ние определяме backend метод на клас за Developer. Не използвахме self но дефинирането на метод като този ефективно го прави метод на клас.

class Developer class << self def backend 'I am backend developer' end end end

Отново дефинираме метод на клас, но използвайки синтаксис, подобен на този, който използвахме за дефиниране на единичен метод за String обект. Може да забележите, че използвахме self тук се отнася до Developer самият обект. Първо отворихме Developer клас, което прави себе си равно на Developer клас. След това правим class << self, като правим себе си равно на Developer метаклас. След това дефинираме метод backend върху Developer метакласа.

class << Developer def backend 'I am backend developer' end end

Чрез дефиниране на блок като този, ние задаваме self до Developer метаклас за продължителността на блока. В резултат на това backend метод се добавя към Developer метакласа, а не към самия клас.

Нека да видим как се държи този метаклас в дървото за наследяване:

Метаклас в дървото за наследство

Както видяхте в предишните примери, няма реално доказателство, че метакласът дори съществува. Но можем да използваме малък хак, който може да ни покаже съществуването на този невидим клас:

class Object def metaclass_example class << self self end end end

Ако дефинираме метод на екземпляр в Object клас (да, можем да отворим всеки клас по всяко време, това е още една красота на метапрограмирането), ще имаме self позовавайки се на Object обект вътре в него. След това можем да използваме class << self синтаксис за промяна на текущия себе си да сочи към метакласа на текущия обект. Тъй като текущият обект е Object клас, това би бил метакласът на екземпляра. Методът връща self което в този момент е самият метаклас. Така че, като извикаме този метод на всеки обект, можем да получим метаклас на този обект. Нека определим нашите Developer клас отново и започнете да проучвате малко:

class Developer def frontend p 'inside instance method, self is: ' + self.to_s end class << self def backend p 'inside class method, self is: ' + self.to_s end end end developer = Developer.new developer.frontend # 'inside instance method, self is: #' Developer.backend # 'inside class method, self is: Developer' p 'inside metaclass, self is: ' + developer.metaclass_example.to_s # 'inside metaclass, self is: #'

А за крещендото, нека видим доказателството, че frontend е метод на екземпляр на клас и backend е метод на екземпляр на метаклас:

p developer.class.instance_methods false # [:frontend] p developer.class.metaclass_example.instance_methods false # [:backend]

Въпреки това, за да получите метакласа, не е нужно да отваряте отново Object и добавете този хак. Можете да използвате singleton_class които Руби предоставя. Това е същото като metaclass_example добавихме, но с този хак всъщност можете да видите как Ruby работи под капака:

p developer.class.singleton_class.instance_methods false # [:backend]

Дефиниране на методи с помощта на “class_eval” и “instance_eval”

Има още един начин да създадете метод на клас, и това е чрез използване instance_eval :

class Developer end Developer.instance_eval do p 'instance_eval - self is: ' + self.to_s def backend p 'inside a method self is: ' + self.to_s end end # 'instance_eval - self is: Developer' Developer.backend # 'inside a method self is: Developer'

Това парче код, което интерпретаторът на Ruby оценява в контекста на екземпляр, който в случая е Developer обект. И когато дефинирате метод за обект, създавате или метод на клас, или единичен метод. В този случай това е метод на клас - за да бъдем точни, методите на класа са единични методи, но единични методи на клас, докато останалите са единични методи на обект.

От друга страна, class_eval оценява кода в контекста на клас вместо екземпляр. На практика отваря отново класа. Ето как class_eval може да се използва за създаване на метод на екземпляр:

Developer.class_eval do p 'class_eval - self is: ' + self.to_s def frontend p 'inside a method self is: ' + self.to_s end end # 'class_eval - self is: Developer' p developer = Developer.new # # developer.frontend # 'inside a method self is: #'

За да обобщим, когато се обадите class_eval метод, вие променяте self да се позовава на оригиналния клас и когато се обадите instance_eval, self промени, за да се отнасят към метакласа на оригиналния клас.

Дефиниране на липсващи методи на полет

Още едно парче пъзел за метапрограмиране е method_missing . Когато извикате метод за обект, Ruby първо влиза в класа и разглежда неговите методи на екземпляр. Ако не намери метода там, той продължава да търси по веригата на предците. Ако Руби все още не намери метода, той извиква друг метод, наречен method_missing което е метод на екземпляр на Kernel че всеки обект наследява. Тъй като сме сигурни, че Ruby ще извика този метод в крайна сметка за липсващи методи, можем да го използваме, за да приложим някои трикове.

define_method е метод, дефиниран в Module клас, който можете да използвате за динамично създаване на методи. За да използвате define_method, го извиквате с името на новия метод и блок, където параметрите на блока стават параметри на новия метод. Каква е разликата между използването на def за създаване на метод и define_method? Няма голяма разлика, освен че можете да използвате define_method в комбинация с method_missing за да напишете СУХ код. За да бъдете точни, можете да използвате define_method вместо def за манипулиране на обхвата при определяне на клас, но това е съвсем друга история. Нека да разгледаме един прост пример:

class Developer define_method :frontend do |*my_arg| my_arg.inject(1, :*) end class < 100 p Developer.backend # undefined method 'backend' for Developer:Class (NoMethodError) Developer.create_backend p Developer.backend # 'Born from the ashes!'

Това показва как define_method е използван за създаване на метод на екземпляр, без да се използва def. Има обаче много повече неща, които можем да направим с тях. Нека да разгледаме този кодов фрагмент:

class Developer def coding_frontend p 'writing frontend' end def coding_backend p 'writing backend' end end developer = Developer.new developer.coding_frontend # 'writing frontend' developer.coding_backend # 'writing backend'

Този код не е СУХ, но използва define_method можем да го направим СУХО:

class Developer ['frontend', 'backend'].each do |method| define_method 'coding_#{method}' do p 'writing ' + method.to_s end end end developer = Developer.new developer.coding_frontend # 'writing frontend' developer.coding_backend # 'writing backend'

Това е много по-добре, но все още не е перфектно. Защо? Ако искаме да добавим нов метод coding_debug например трябва да сложим това 'debug' в масива. Но използвайки method_missing можем да поправим това:

гещалт законът на ________ казва, че сме склонни да групираме заедно обекти, които са близо един до друг.
class Developer def method_missing method, *args, &block return super method, *args, &block unless method.to_s =~ /^coding_w+/ self.class.send(:define_method, method) do p 'writing ' + method.to_s.gsub(/^coding_/, '').to_s end self.send method, *args, &block end end developer = Developer.new developer.coding_frontend developer.coding_backend developer.coding_debug

Тази част от кода е малко сложна, така че нека я разделим. Извикването на метод, който не съществува, ще се задейства method_missing Тук искаме да създадем нов метод само когато името на метода започва с 'coding_'. В противен случай просто се обаждаме на super, за да свършим работата по докладване на метод, който всъщност липсва. И ние просто използваме define_method за да създадете този нов метод. Това е! С тази част от кода можем да създадем буквално хиляди нови методи, започвайки с 'coding_' и този факт прави нашия код СУХ. Тъй като define_method се случва да е частно за Module, трябва да използваме send за да го извикате.

Обобщавайки

Това е само върхът на айсберга. За да станете Руби джедаи , това е началната точка. След като овладеете тези градивни елементи на метапрограмирането и наистина разберете същността му, можете да преминете към нещо по-сложно, например да създадете свой собствен Език, специфичен за домейн (DSL). DSL е тема сама по себе си, но тези основни понятия са предпоставка за разбиране на напреднали теми. Някои от най-използваните скъпоценни камъни в Релси са построени по този начин и вероятно сте използвали неговия DSL, без дори да го знаете, като RSpec и ActiveRecord.

Надяваме се, че тази статия може да ви приближи една стъпка по-близо до разбирането на метапрограмирането и може би дори да изградите свой собствен DSL, който можете да използвате за по-ефективно кодиране.

Въведение в програмирането, ориентирано към протокола в Swift

Back-End

Въведение в програмирането, ориентирано към протокола в Swift
Урок за Logstash: Използване на Logstash за рационализиране на известия по имейл

Урок за Logstash: Използване на Logstash за рационализиране на известия по имейл

Back-End

Популярни Публикации
Как да изградим безкраен бегач на iOS: Cocos2D, автоматизация и др
Как да изградим безкраен бегач на iOS: Cocos2D, автоматизация и др
Напишете важни тестове: Първо се справете с най-сложния код
Напишете важни тестове: Първо се справете с най-сложния код
Разработване на SmartWatch: Струва ли си проблемът на SmartWatch?
Разработване на SmartWatch: Струва ли си проблемът на SmartWatch?
Polymer.js: Бъдещето на разработването на уеб приложения?
Polymer.js: Бъдещето на разработването на уеб приложения?
Как да създадете персонализирани шрифтове: 7 стъпки и 3 казуса
Как да създадете персонализирани шрифтове: 7 стъпки и 3 казуса
 
Силните страни и предимствата на Micro Frontends
Силните страни и предимствата на Micro Frontends
Как да използваме помощници на релси: Демонстрация на въртележка за Bootstrap
Как да използваме помощници на релси: Демонстрация на въртележка за Bootstrap
Поглед към бъдещето на JavaScript
Поглед към бъдещето на JavaScript
Изграждане на ASP.NET уеб API с ASP.NET Core
Изграждане на ASP.NET уеб API с ASP.NET Core
Урок за OpenCV: Откриване на обекти в реално време с помощта на MSER в iOS
Урок за OpenCV: Откриване на обекти в реално време с помощта на MSER в iOS
Популярни Публикации
  • графичен дизайн срещу дигитален дизайн
  • какво е проектен документ
  • как да използвате шаблони за стартиране
  • html5 index.html след deviceready 'err устройството не е дефинирано'
  • разлика между s corp и c corp llc
  • Най-добри практики за дизайн на уебсайтове 2016
Категории
  • Инструменти И Уроци
  • Agile Talent
  • Продукти Хора И Екипи
  • Растеж На Приходите
  • © 2022 | Всички Права Запазени

    portaldacalheta.pt