Оптимальный паттерн для работы с RemoteObject

Posted by - February 20, 2009

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

class ServiceBase
{
	public function ServiceBase(destination : String,
		resultHandler : Function = null, faultHandler : Function = null)
	{
		_resultHandler = resultHandler;
		_faultHandler = faultHandler;

		_remoteObject = new RemoteObject(destination);
	}

	protected function call(name : String, args : Array) : void
	{
		CursorManager.setBusyCursor();

		var operation : AbstractOperation = _remoteObject.getOperation(name);
		operation.addEventListener(ResultEvent.RESULT, onResult);
		operation.addEventListener(FaultEvent.FAULT, onFault);
		operation.send.apply(operation, args);
	}

	protected function onResult(event : ResultEvent) : void
	{
		CursorManager.removeBusyCursor();

		if (_resultHandler != null)
		{
			_resultHandler(event.result);
		}
	}

	protected function onFault(event : FaultEvent) : void
	{
		CursorManager.removeBusyCursor();

		if (_faultHandler != null)
		{
			_faultHandler(event.fault);
		}
	}

	private var _resultHandler : Function;

	private var _faultHandler : Function;

	private var _remoteObject : RemoteObject;
}

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

class GuestbookService extends ServiceBase
{
	public function GuestbookService(resultHandler : Function = null,
		faultHandler : Function = null)
	{
		super("GuestbookAction", resultHandler, faultHandler);
	}

	public function getGuestbookEntries() : void
	{
		call("getGuestbookEntries", arguments);
	}

	public function getEntryById(id : Number) : void
	{
		call("getEntryById", arguments);
	}

	public function updateEntry(id : Number, text : String) : void
	{
		call("updateEntry", arguments);
	}
}

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

new GuestbookService().updateEntry(5, "Nice solution!");

или

new GuestbookService(onGetEntryById).getEntryById(6);

function onGetEntryById(result : GuestbookEntry) : void
{
	_list.addItem(result);
}

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

Мне этот подход нравится больше чем низкоуровневое конструирование запроса на основе RemoteObject своей прозрачностью и простотой. Так же, подобные наследники можно и следует генерировать автоматически на основе серверных классов. Тогда, вы получаете полный контроль над соответствием названий серверных методов, а так же их сигнатур. Компилятор просто не соберёт ваш проект, если вы не исправите ошибку.

Так же, подобное решение может вам помочь при юнит-тестировании, подменяя базовый класс чем-то вроде MockServiceBase.

2 Comments on Оптимальный паттерн для работы с RemoteObject

Respond | Trackback

  1. PIL says:

    Хорошая статья, но немного критики ;)
    1. в агрегированном _remoteObject есть свойство showBusyCursor, поэтому можно убрать пляски с CursorManager.
    2. в данной реализации один инстанс класса может выполнять только один ассинхронный запрос, если во время выполнения одного запроса вызвать другой – перезапишется коллбек.
    А в остальном, все наверное пишут себе – что-то подобное

    • tearaway_Tea says:

      Да, с пунктом 1) это так, лирика, а вот с пунктом 2) это вы очень верно подметили, мне только сейчас в голову пришло, почему у меня ненормально работали множественные вызовы в одном экземпляре сервиса, когда я модернизировал класс ServiceBase, добавив ему в конструктор аргумент concurrency(передавая его RemoteObject-y).

      Бывает же такое.

Respond

Comments

Comments