DroidScript
DroidScript
разработка мобильных приложений

Разработка от минимальной области видимости

DroidScript  
16.01.2017

В JavaScript не производится проверка типов. Это даёт разработчика не только большие возможности, но и большие проблемы. Например, можно случайно присвоить объектной переменной строку и тем самым потерять все определённые ранее свойства объекта, например, объявим глобальную переменную:

var product = {
	code: 12,
	title: 'Remix Mini',
	cost: '4000'
};
alert( product.title ); // 'Remix Mini' product = "Велосипед"; // забыли, что ранее уже была определена переменная с таким именем alert( product.title ); // undefined

В компилируемых языках уникальность глобальных определений проверяется на уровне компилятора, который не позволит создать два объекта или метода с одинаковыми именами (за некоторым исключением), а в JavaScript нам приходится самим следить за этим, в чём помогает подход к разработке от минимально доступной области видимости переменных. При данном подходе объекты, методы и свойства изначально создаются закрытыми (локальными). Для этого они должны находиться внутри функции и иметь локальное объявление. Практичность данного подхода очевидна - чем меньше происходит взаимодействия с внутренним кодом, тем меньше вероятность нарушить его работу. Пользователь должен взаимодействовать с внутренним кодом не напрямую, а посредством интерфейсных методов.

Рассмотрим стиль оформление кода из справки по DroidScript:

function OnStart()
{
	//+++ внутренний код функции
lay = app.CreateLayout( "linear", "VCenter,FillXY" ); btn = app.CreateButton( "Press Me", 0.3, 0.1 ); btn.SetMargins( 0, 0.05, 0, 0 ); btn.SetOnTouch( btn_OnTouch );
lay.AddChild( btn ); app.AddLayout( lay );
//--- внутренний код функции } function btn_OnTouch()
{ //+++ внутренний код функции
btn.SetText( "Привет,Андроид!" );
//--- внутренний код функции }

В этом примере переменные lay и btn, для удобства обращения к ним за пределами функции OnStart, неявно объявлены глобальными. Такой подход - "закрыть доступ успею всегда, а для удобства разработки дам доступ ко всем переменным" - не рекомендуется использовать при разработке реальных приложений. "Потом" часто забывается и с увеличением объёма кода в нём увеличивается количество "висящих" глобальных переменных, полей и функций, случайное переопределение которых сразу приведёт к нестабильной работе кода, ошибкам и исключениям.. Исправим это, объявив все переменные локальными:

function OnStart()
{
	//+++ внутренний код функции
var _lay = app.CreateLayout( "linear", "VCenter,FillXY" ); var _btn = app.CreateButton( "Press Me", 0.3, 0.1 ); _btn.SetMargins( 0, 0.05, 0, 0 ); _btn.SetOnTouch( btn_OnTouch ); _lay.AddChild( _btn ); app.AddLayout( _lay ); //--- внутренний код функции } function btn_OnTouch()
{ //+++ внутренний код функции
_btn.SetText( "Привет,Андроид!" ); // исключение, _btn не определено
//--- внутренний код функции }

Теперь за пределами функции OnStart невозможно изменить переменные и нажатие на кнопку приведёт к возникновению ошибки в приложении. Для удобства всем названиям локальных переменных присвоен префикс _, что в будущем позволит их легко различать.

В нашем примере код отображения виджетов, который реализован в функции OnStart, отделён от кода реакции на действие - функция btn_OnTouch, которая является глобальной. Для её локализации можно использовать анонимную функцию обратного вызова:

function OnStart()
{
	var _lay = app.CreateLayout( "linear", "VCenter,FillXY" );	

	var _btn = app.CreateButton( "Press Me", 0.3, 0.1 );
	_btn.SetMargins( 0, 0.05, 0, 0 );

	_btn.SetOnTouch( function(){
		app.ShowPopup( "Привет, Андроид!" );
	});

	_lay.AddChild( _btn );

	app.AddLayout( _lay );
}

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

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

var gl_widget = {};

function OnStart()
{
	//+++ закрытый внутренний код

	var _lay = app.CreateLayout( "linear", "VCenter,FillXY" );	

	var _btn = app.CreateButton( "Press Me", 0.3, 0.1 );
	_btn.SetMargins( 0, 0.05, 0, 0 );

	_lay.AddChild( _btn );

	app.AddLayout( _lay );

//--- закрытый внутренний код //+++ интерфейсная часть gl_widget.btnShowVersion = _btn;
//--- интерфейсная часть }

Интерфейсные объекты необходимо определять в самом конце функции, так как их изменение приведёт к изменению внутреннего объекта, присвоенного по ссылке.

var gl_widget = {};

function OnStart()
{
	//+++ закрытый внутренний код
	var _lay = app.CreateLayout( "linear", "VCenter,FillXY" );	

	var _btn = app.CreateButton( "Press Me", 0.3, 0.1 );
	gl_widget.btnShowVersion = _btn;
	_btn.SetMargins( 0, 0.05, 0, 0 );

	_lay.AddChild( _btn );

	app.AddLayout( _lay );
	//--- закрытый внутренний код

	//+++ интерфейсная часть

	gl_widget.btnShowVersion.name = 12; // теперь и _btn.name = 12
	alert(_btn.name); // 12
	
	//--- интерфейсная часть
}

Префикс gl_ в именах глобальных переменных позволит отличать их от остальных переменных.

Интерфейсный объект связан с внутренним объектом по ссылке, о чём нужно помнить. Это позволяет новые свойства и обработчики событий присваивать как внутреннему объекту, так и интерфейсному, но лучше следовать правилу, при котором интерфейсному объекту присваиваются только обработчики событий, а всё остальное добавлять исходному объекту.

Глобальная переменная gl_widget является свойством глобального объекта, который в браузерной среде доступен под именем window:

window.gl_widget;

или

window[ 'gl_widget' ];

Такое обращение позволяет избавиться от глобальной переменной gl_widget. Для справки, в среде Node.js глобальный объект доступен под именем global.

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

DroidScript  
© 2016-2022  Александр Страшко