Неочевидное в Monkey. Часть первая

В этой статье я постараюсь собрать все неочевидные моменты, с которым мне приходилось сталкиваться во время написания своих приложений. Я не планирую открывать Америку или делать что-то в этом духе. Многие из этих особенностей лежат на поверхности, достаточно лишь взглянуть на задачу под другим углом.

Скорее всего, здесь вы найдете некоторые вещи, о которых уже знали ранее. Тем не менее, я надеюсь, что информация окажется полезной как новичкам, так и уже бывалым Monkey программистам.

Ну что ж, приступим.

Многофункциональный Import

Думаю, все из нас использовали Import для подключения различных модулей. Но наверное не каждый знает, что с помощью этой же команды мы можем импортировать ресурсы в контейнер ресурсов.

Например:

Import “sample.png”

Этот код импортирует в папку .data изображение sample.png, располагающееся в той же директории, что и импортируемый скрипт. Вы также можете задать для импорта любой валидный путь.

У этого метода есть несколько ограничений:

  1. Импортировать можно только в корень контейнера ресурсов, так как при импорте теряется вся иерархия.
  2. Импортировать можно только те файлы, расширения которых определены конфигурацией приложения, то есть указаны в #TEXT_FILES, #BINARY_FILES и так далее.

Где это можно использовать? Лично я использовал подобный импорт для включения в сборку приложения внутренних ресурсов моего фреймворка без участия пользователя. Это позволило упростить процесс подключения модуля, избавив от необходимости ручного копирования файлов. Но я думаю, можно найти и другие, не менее полезные способы применения.

Всеобъемлющее переопределение функций

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

Вот самый простой пример:

Import mojo

Function Main()
    New MyApp()
End Function

Class MyApp Extends App

    Method OnCreate()
        SetUpdateRate(60)
    End Method

    Method OnRender()
        Cls
        DrawCircle(100, 100, 50)
    End Method

End Class

Function DrawCircle(x:Float, y:Float, radius:Float)
    DrawRect(x - radius, y - radius, radius * 2, radius * 2)
End Function

И вот, вместо круга мы уже рисуем квадрат.

Этот пример не несет никакой практической пользы. Но если подойти к делу с умом, то подобному переопределению функций можно найти массу полезных применений. А в связке с Alias, можно творить настоящую магию. Да-да, ту самую — уличную!

Поле превращается…

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

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

Class A

    Field x:Float

End Class

‘очень много строк кода...

Class B Extends A

    Method x:Float() Property
        Return _x + 5
    End Method  

Private
    Field _x:Float

End Class

‘очень много строк кода...

Function Main()
    Local a:A = New A()
    Local b:B = New B()

    Print a.x 'Выведет 0
    Print b.x 'Выведет 5
End Function

И вот, поле класса уже превратилось в свойство. Что интересно — обратное, как и повторное превращения невозможны.

Несмотря на явно запутывающий характер особенности, в некоторых единичных случаях она может быть полезна. Но все же, я бы рекомендовал избегать подобных трюков, дабы не прострелить себе ногу.

Продолжение следует…