воскресенье, 27 сентября 2009 г.

Select object rather then string in JSF

Сначала хотел написать, что пост кардинально отличается от предыдущих, потому что про Java и связанные технологии, а потом подумал и понял, что основное-то предназначение блога - делиться опытом применения паттернов и методологий, а не описывать "фичи" языка, который всего лишь инструмент в данном случае. А потому начну так: Этот пост не сильно отличается от предыдущих, но он про Java и связанные технологии.

Все еще здесь? Тогда продолжим! (отлично, спер клише, молодец)


Проект, на котором я работал, своей целью ставил исследование возможностей платформы Java
в разработке Web-решений для компании, давно занимающейся разработкой на .Net, следовательно, не имеющей такого опыта и таких специалистов. Так вот, нашей задачей было получение такого опыта и становление такими специалистами за максимально короткий срок – сэкономили, короче.

Проект – небольшой сайт с frontend на JSF 1.2, facelets, RichFaces и инфраструктурой с использованием Spring и Hibernate ORM.

Так вот заметка моя касается представления списков выбора в JSF, которая сделана, на мой взгляд, весьма коряво. Вкратце – сущности, из которых ведется выбор, вы должны представить в виде строк, завернув в класс SelectItem. Назад вместо объекта вы, естественно получите строку, а вовсе не объект, т.к. последний не путешествовал от сервера к клиенту и обратно. Так вот мое решение призвано обойти данную шероховатость JSF.
Кстати сказать, я не один такой умный и решений этой задачи есть как минимум несколько: http://jsf-comp.sourceforge.net/components/easysi/index.html, http://balusc.blogspot.com/2007/09/objects-in-hselectonemenu.html, - но… Первое не работает с RichFaces, а второе я нашел уже после того как написал свое.

На решение натолкнула меня тоска. Ага, тоска по счастливым временам когда GUI я «творил» на WPF. Я тогда сильно упростил себе жизнь, написав SelectorCollectionPresenter. И я подумал
–почему бы не использовать похожий подход в Java? Сказано – сделано. Я написал класс CollectionPresenter =).

Основная идея – это объект, который хранит карту ассоциаций строковых представлений с объектами и текущую выбранную строку, по которой, собственно, из карты выбирается объект.


Для Generic коллекций в Java я использовал реализацию apache Generic common-collections

Теперь, имея возможность получить ссылку на объект, можно написать facelet,



который получает на вход список SelectItem’ов, которые создал Presenter, и возвращает выбранную строку, по которой Presenter вернет соответствующий объект.
В принципе этот процесс можно отдать на откуп facelet’у целиком:



И вот как выглядит использование первой версии facelet’а в разметке страницы:


Поверх presenter’а я навернул еще generic контроллер, который выбирает список объектов из
Repository и подставляет их presenter’у.


Важно заметить, что работать будет с контроллером, с любым временем жизни (стратегией времени жизни в Spring контейнере).

Объекты, для выбора в моем случае – строки справочника, представляемые наследниками класса DictionaryEntry, что в общем случае необязательно, просто тогда надо будет написать соответствующий контроллер.


Вот и все решение, в общих чертах!

Удачи!

четверг, 3 сентября 2009 г.

Fluent Builder

Fluent Interface и Builder - идеальное сочетание для декларативного создания объектов.
На самом деле примеры такого выгодного симбиоза давно у нас под носом, например: StringBuilder.

Я использовал такой симбиоз для декларативного создания SIP аккаунтов и звонков.

Решение состоит из внутренних Builder-классов, которые предоставляют декларативный интерфейс для создания объектов и скрывают детали типа суффиксов транспорта, схемы и пр., о которых не надо задумываться.








Оба этих класса опираются на вспомогательный Builder – SIPUriBuilder. Этот класс настолько интуитивен, что я удивлен, что я не написал его первым делом.



Результат на лицо:


Было:

string remote = "sip:";

remote += txbURI.Text == "00"

? "127.0.0.1"

: (txbURI.Text + "@" + SIPUserAgent.SIPUserAgent.Instance.AccountManager.DefaultAccount.RegistrarUri.Split(new[]{':'})[1]);

remote += SIPUserAgent.SIPUserAgent.Instance.SIPTransport is TCPTransport

? ":5061;transport=TCP"

: "";

SIPUserAgent.SIPUserAgent.Instance.CallManager.MakeCall(remote);


Стало:

Call c = Call.ConstructCall().SetAccount(acc).SetExtension(txbURI.Text).SetDomain(txbURID.Text).Call();


Было:

var acc = new Account(false);

using (acc.CreateInitializationSession())

{

acc.Credential = new NamePasswordCredential

{

Password = txbPass.Text,

Realm = txbRegistrar.Text,

UserName = txbLogin.Text

};

acc.AccountId = "sip:" + txbLogin.Text + "@" + txbRegistrar.Text;

acc.RegistrarUri = "sip:" + txbRegistrar.Text;

if (ua.SIPTransport is TCPTransport)

{

acc.AccountId += "5061;transport=TCP";

acc.RegistrarUri += "5061;transport=TCP";

}

}

ua.AccountManager.RegisterAccount(acc, true);


Стало:

var acc = Account.ConstructAccount().SetLogin(txbLogin.Text).SetPassword(txbPass.Text)
.SetRegistrarDomain(txbRegistrar.Text).Register();


Комментарии я думаю излишни. Просто сравните число строк.

Удачи!