Использование шаблона-посредника дает вам возможность разрабатывать JSF-страницы в качестве детализированных компонентов в вашем приложении, позволяя легко и просто связывать их вместе.
Образец посредника
Обычно веб-приложение состоит из (иногда большого) количества страниц с несколькими базовыми классами (вспомогательные компоненты и т. Д.). Таким образом, различные функции распределены между этими страницами. Однако по мере того, как в программе разрабатывается больше страниц, особенно во время обслуживания и / или рефакторинга, проблема связи между этими страницами и базовыми классами может стать более сложной. Это затрудняет чтение и обслуживание программы. Кроме того, может быть сложно изменить программу, поскольку любое изменение может повлиять на код в нескольких других классах.
С помощью шаблона посредника связь между объектами инкапсулируется с объектом-посредником. Объекты больше не общаются напрямую друг с другом, а вместо этого общаются через посредника. Это уменьшает зависимости между взаимодействующими объектами, тем самым снижая связь.
Образец заявки
Пример приложения состоит из нескольких страниц. Первый представляет список клиентов. Клиент может быть частным лицом или компанией.
Клиента можно добавить, нажав кнопку «Добавить человека» или ссылку «Добавить компанию». На следующем шаге открывается страница с редактируемыми деталями:
или же
Далее пользователь должен выбрать адрес для клиента в отдельной форме со списком адресов:
После того, как он или она выбрал адрес, необходимо добавить другие детали, а клиента можно добавить с помощью кнопки «Добавить».
Пример приложения довольно прост, и его единственная цель — показать использование шаблона.
Применение шаблона
Сессия пользователя
Существует два обычно используемых способа организации взаимодействия между страницами в JSF: через область сеанса или запрос. Пример, представленный в статье, основан на первом подходе и использует сессионный компонент в качестве сеанса:
public class UserSession {
private Map<String, Object> sessionMap = new HashMap<String, Object>();
public synchronized void setAttribute(String key, Object value){
if(value == null) {
sessionMap.remove(key);
} else {
sessionMap.put(key, value);
}
}
public synchronized Object getAttribute(String key){
return sessionMap.get(key);
}
}
Это определяется в файле конфигурации JSF следующим образом:
<managed-bean>
<managed-bean-name>userSession</managed-bean-name>
<managed-bean-class>example.session.UserSession</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
Служба страниц
Компонент поддержки страницы использует Сервис, который извлекает все необходимые данные для страницы. Давайте рассмотрим страницу с списком клиентов в качестве примера. Вот часть страницы JSP, которая отображает таблицу клиентов:
<h:dataTable value="#{clientsBB.clients}" var="client" style="border:solid 1px gray">
<h:column >
<f:facet name="header"><h:outputText value="Client Name"/></f:facet>
<h:outputText value="#{client.name}"/>
</h:column>
<h:column>
<f:facet name="header"><h:outputText value="Client Address"/></f:facet>
<h:outputText value="#{client.address}"/>
</h:column>
</h:dataTable>
Страница получает данные из getClientsMethod своего компонента поддержки :
public class ClientsBB {
private ClientsService clientsService;
public List<ClientListItem> getClients() {
return clientsService.getClients();
}
public void setClientsService(final ClientsService clientsService) {
this.clientsService = clientsService;
}
}
ClientsService реализует бизнес-логику страницы. Он извлекает данные из источника данных (или из пользовательского сеанса) и создает объекты для отображения:
public class ClientsService {
public List<ClientListItem> getClients() {
List<ClientListItem> result = new ArrayList<ClientListItem>();
List<Company> companies = DataSource.getCompanies();
for(Company company: companies) {
result.add(ClientListItem.valueOf(company));
}
List<Person> persons = DataSource.getPersons();
for(Person person: persons) {
result.add(ClientListItem.valueOf(person));
}
return result;
}
}
Управляемые компоненты определяются с областью сеанса:
<managed-bean>
<managed-bean-name>clientsBB</managed-bean-name>
<managed-bean-class>example.backingbean.ClientsBB</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>clientsService</property-name>
<value>#{clientsService}</value>
</managed-property>
</managed-bean>
<managed-bean>
<managed-bean-name>clientsService</managed-bean-name>
<managed-bean-class>example.service.ClientsService</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
Все остальные страницы имеют похожее поведение.
медиатор
Чтобы использовать страницу «Выбор адреса» для страниц «Добавить человека» и «Добавить компанию», желательно, чтобы она оставалась независимой от последних. Также, если вы предполагаете использовать другую страницу (стратегию) для выбора адреса, желательно сохранить страницы «Добавить человека» и «Добавить компанию» независимыми на этой странице «Выбор адреса». Вот здесь и возникает модель Посредника. Следующие диаграммы показывают роль объекта-посредника:
Объект-посредник реализован как управляемый компонент. Кнопка «Выбрать адрес» имеет прикрепленный слушатель действия, который указывает на метод объекта-посредника. Этот метод настраивает поведение страницы «Выбор адреса» с помощью Сервиса, который сохраняет данные о предпочтениях в сеансе. Посредник также добавляет слушателя, который должен быть запущен при выборе адреса. Код ниже поможет лучше понять последовательность действий.
Кнопка «выбрать адрес» определяется внутри страницы следующим образом:
<h:commandButton immediate="true"
action="#{selectAddressForPersonBB.selectAddress}"
value="Select Address"/>
Ниже приведен код класса посредника:
public class SelectAddressForPersonBB {
private SelectAddressService selectAddressService;
public String selectAddress() {
// Reset selection for "select address page"
selectAddressService.setSelectedAddress(null);
// Set navigation outcome
selectAddressService.setSuccessNavigationOutcome("personAddressSelected");
// Add listener
selectAddressService.setAddressSelectedListener(new Listener() {
public void fire(FacesContext facesContext) {
Application app = facesContext.getApplication();
SelectAddressService selectAddressService =
(SelectAddressService) app.evaluateExpressionGet(facesContext, "#{selectAddressService}", SelectAddressService.class);
PersonService personService =
(PersonService) app.evaluateExpressionGet(facesContext, "#{personService}", PersonService.class);
Address address = selectAddressService.getSelectedAddress();
// Set person's address
personService.setPersonAddress(address);
// Remove listener
selectAddressService.setAddressSelectedListener(null);
}
});
return "selectAddress";
}
public void setSelectAddressService(final SelectAddressService selectAddressService) {
this.selectAddressService = selectAddressService;
}
}
Он настроен как резервный компонент (примечание: все управляемые компоненты, кроме userSession , определены с областью запроса):
<managed-bean>
<managed-bean-name>selectAddressForPersonBB</managed-bean-name>
<managed-bean-class>example.backingbean.SelectAddressForPersonBB</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>selectAddressService</property-name>
<value>#{selectAddressService}</value>
</managed-property>
</managed-bean>
Служба страницы «Выбор адреса» сохраняет настройки и слушателя в сеансе. Сам сервис определяет интерфейс для настройки поведения страницы (например, он дает возможность установить результат навигации успеха).
public class SelectAddressService {
private static final String SELECTED_ADDRESS = "selected_address";
private static final String SUCCESS_OUTCOME = "success_outcome";
private static final String SELECTED_ LISTENER = "address_selected_listener";
private UserSession userSession;
public List<Address> getAddresses() {
return DataSource.getAddresses();
}
public void setAddressSelectedListener(Listener listener) {
userSession.setAttribute(SELECTED_LISTENER, listener);
}
public void setSelectedAddress(Address address) {
userSession.setAttribute(SELECTED_ADDRESS, address);
Object listener = userSession.getAttribute(SELECTED_LISTENER);
if(listener != null) {
((Listener) listener).fire(FacesContext.getCurrentInstance());
}
}
public Address getSelectedAddress() {
Object address = userSession.getAttribute(SELECTED_ADDRESS);
return address == null ? null : (Address) address;
}
public void setSuccessNavigationOutcome(String navigationOutcome) {
userSession.setAttribute(SUCCESS_OUTCOME, navigationOutcome);
}
public String getSuccessNavigationOutcome() {
Object navigationOutcome = userSession.getAttribute(SUCCESS_OUTCOME);
return navigationOutcome == null ? null : (String) navigationOutcome;
}
public void setUserSession(final UserSession userSession) {
this.userSession = userSession;
}
}
На диаграмме ниже объясняется, что происходит, когда пользователь выбирает адрес на странице «Выбор адреса»:
После выбора адреса он сохраняется и вызывается прослушиватель, добавленный посредником. Этот слушатель извлекает выбранный адрес и устанавливает его человеку, использующему соответствующий Сервис:
public void fire(FacesContext facesContext) {
Application app = facesContext.getApplication();
SelectAddressService selectAddressService =
(SelectAddressService) app.evaluateExpressionGet(facesContext, "#{selectAddressService}", SelectAddressService.class);
CompanyService companyService =
(CompanyService) app.evaluateExpressionGet(facesContext, "#{companyService}", CompanyService.class);
Address address = selectAddressService.getSelectedAddress();
// Set company's address
companyService.setCompanyAddress(address);
// Remove listener
selectAddressService.setAddressSelectedListener(null);
}
Вывод
В статье рассмотрен пример применения известного шаблона Mediator для разработки веб-приложений. Приведенные в этой статье примеры вместе с исходным кодом могут помочь вам реализовать этот шаблон практически в любом приложении на основе JSF.
Вы можете свободно загружать источники http://java.dzone.com/sites/all/files/mediator_in_jsf_app-src.zip