Обзор
Это четвертая и последняя статья из короткой серии, посвященной шаблонам проектирования в Java , и прямое продолжение предыдущей статьи - Шаблоны проектирования в Java .
Паттерны J2EE
Шаблоны J2EE заботятся о предоставлении решений, касающихся Java EE. Эти шаблоны широко применяются в других фреймворках и проектах. Например: Весна .
В этой статье рассматриваются следующие шаблоны J2EE:
- Шаблон MVC
- Шаблон бизнес-делегата
- Шаблон составного объекта
- Шаблон объекта доступа к данным
- Шаблон переднего контроллера
- Шаблон перехватывающего фильтра
- Шаблон локатора услуг
- Перенести образец объекта
Шаблон MVC
Это один из самых известных и часто используемых паттернов из этой категории. Он основан на идее Модель-Представление-Контроллер , отсюда и происходит аббревиатура.
Модели - это в основном объекты, точнее POJO, используемые в качестве чертежей / моделей для всех объектов, которые будут использоваться в приложении.
Представления представляют собой презентационный аспект данных и информации, находящихся в моделях.
Контроллеры контролируют и то, и другое. Они служат связующим звеном между ними. Контроллеры создают, обновляют и удаляют модели, заполняют их информацией, а затем отправляют данные в представления для представления конечному пользователю.
Выполнение
При этом давайте начнем с первого из трех компонентов этого паттерна - модели:
public class Employee {
private int employeeId;
private String name;
public int getEmployeeId() {
return employeeId;
}
public void setEmployeeId(int id) {
this.employeeId = id;
}
public String getName() {
return name;
}
public void setEmployeeName(String name) {
this.name = name;
}
}
Нам нужен способ представления данных из модели, поэтому мы определяем представление именно для этой цели:
public class EmployeeView {
public void printEmployeeInformation(String employeeName, int employeeId) {
System.out.println("Employee information: ");
System.out.println("ID: " + employeeId);
System.out.println("Name: " + employeeName);
}
}
Представление отвечает за форматирование информации в удобном для пользователя виде.
Как только это будет решено, давайте определим контроллер. Этот контроллер будет использовать как модель, так и представление, чтобы создать экземпляр модели, заполнить его некоторыми данными, а затем передать его в представление, чтобы клиент мог увидеть:
public class EmployeeController {
private Employee employee;
private EmployeeView employeeView;
public EmployeeController(Employee employee, EmployeeView employeeView) {
this.employee = employee;
this.employeeView = employeeView;
}
public String getEmployeeName() {
return employee.getName();
}
public void setEmployeeName(String name) {
employee.setEmployeeName(name);
}
public int getEmployeeId() {
return employee.getEmployeeId();
}
public void setEmployeeId(int id) {
employee.setEmployeeId(id);
}
public void updateView() {
employeeView.printEmployeeInformation(employee.getName(), employee.getEmployeeId());
}
}
Завершив все три компонента этого шаблона, мы можем завершить этот пример.
Чтобы проиллюстрировать суть этого паттерна:
public class Main {
public static void main(String[] args) {
Employee employee = getEmployeeFromDatabase();
EmployeeView view = new EmployeeView();
EmployeeController controller = new EmployeeController(employee, view);
controller.updateView();
controller.setEmployeeId(5);
controller.updateView();
}
// simulating a database
public static Employee getEmployeeFromDatabase() {
Employee employee = new Employee();
employee.setEmployeeName("David");
employee.setEmployeeId(1);
return employee;
}
}
Выполнение этого фрагмента кода даст:
Employee information:
ID: 1
Name: David
Employee information:
ID: 5
Name: David
Шаблон бизнес-делегата
Шаблон Business Delegate используется для отделения уровня представления от бизнес-уровня, чтобы минимизировать количество запросов между клиентом (презентацией) и бизнес-уровнями.
Выполнение
Начнем с определения интерфейса для наших бизнес-сервисов:
public interface BusinessService {
public void process();
}
После этого давайте определим два конкретных класса, реализующих этот интерфейс:
public class EJBService implements BusinessService {
@Override
public void process() {
System.out.println("Processing using the EJB Service.");
}
}
public class JMSService implements BusinessService {
@Override
public void process() {
System.out.println("Processing using the JSM Service.");
}
}
Определим службу поиска. Объект службы поиска должен предоставлять относительные бизнес-реализации и доступ бизнес-объекта к логике бизнес-делегата:
public class BusinessLookUp {
public BusinessService getBusinessService(String type) {
if (type.equalsIgnoreCase("ejb")) {
return new EJBService();
} else if (type.equalsIgnoreCase("JMS")) {
return new JMSService();
} else {
return null;
}
}
}
Теперь мы можем определить нашего бизнес-делегата:
public class BusinessDelegate {
private BusinessLookUp lookupService = new BusinessLookUp();
private BusinessService businessService;
private String type;
public void setServiceType(String type) {
this.type = type;
}
public void process() {
businessService = lookupService.getBusinessService(type);
businessService.process();
}
}
Он действует как точка доступа к бизнес-сервисам для использования
Client
:
public class Client {
BusinessDelegate businessDelegate;
public Client(BusinessDelegate businessDelegate) {
this.businessDelegate = businessDelegate;
}
public void process() {
businessDelegate.process();
}
}
А теперь, чтобы проиллюстрировать суть этого паттерна:
public class Main {
public static void main(String[] args) {
BusinessDelegate businessDelegate = new BusinessDelegate();
businessDelegate.setServiceType("EJB");
Client client = new Client(businessDelegate);
client.process();
businessDelegate.setServiceType("JMS");
client.process();
}
}
Выполнение этого фрагмента кода даст:
Processing using the EJB Service.
Processing using the JSM Service.
Шаблон составного объекта
Шаблон составной сущности представляет собой граф объектов, который при обновлении запускает обновление для всех зависимых сущностей в графе.
Он в основном используется в Enterprise JavaBeans (EJB), который не является очень популярным API, поскольку был заменен другими фреймворками и инструментами, такими как Spring Framework и его многочисленные инструменты.
Выполнение
Давайте определим два класса, данные функций которых потребуются для обновления другого класса:
public class Employee {
private String name;
private String jobSuccess;
public void setJobSuccess(String jobSuccess) {
this.jobSuccess = jobSuccess;
}
public String getJobSuccess() {
return jobSuccess;
}
}
public class Manager {
private String name;
private String satisfaction;
public void setSatisfaction(String satisfaction) {
this.satisfaction = satisfaction;
}
public String getSatisfaction() {
return satisfaction;
}
}
Если Employee
работает хорошо, Manager
доволен, и наоборот.
Так как точка этого шаблона, чтобы не позволить бобы действовать в качестве «мелкозернистый» объекты в одиночку, мы введены с объектом крупнозернистый. Этот объект управляет своими отношениями с другими объектами:
public class CoarseGrainedObject {
Employee employee = new Employee();
Manager manager = new Manager();
public void setData(String jobSuccess, String satisfaction) {
employee.setJobSuccess(jobSuccess);
manager.setSatisfaction(satisfaction);
}
public String[] getData() {
return new String[] {"Employee : " + employee.getJobSuccess(),"Manager: " +
manager.getSatisfaction()};
}
}
После этого нам нужно определить класс CompositeEntity
Этот класс сам
по себе является крупнозернистым объектом и может ссылаться на другой:
public class CompositeEntity {
private CoarseGrainedObject cgo = new CoarseGrainedObject();
public void setData(String jobSuccess, String satisfaction) {
cgo.setData(jobSuccess, satisfaction);
}
public String[] getData() {
return cgo.getData();
}
}
После этого нам просто нужен Client
для использования
CompositeEntity
:
public class Client {
private CompositeEntity compositeEntity = new CompositeEntity();
public void print() {
for (int i = 0; i < compositeEntity.getData().length; i++) {
System.out.println(compositeEntity.getData()[i]);
}
}
public void setData(String jobSuccess, String satisfaction) {
compositeEntity.setData(jobSuccess, satisfaction);
}
}
И чтобы проиллюстрировать суть этого паттерна:
public class Main {
public static void main(String[] args) {
Client client = new Client();
client.setData("Successful", "Satisfied");
client.print();
client.setData("Failed", "Unsatisfied");
client.print();
}
}
Выполнение этого фрагмента кода даст:
Employee : Successful
Manager: Satisfied
Employee : Failed
Manager: Unsatisfied
Шаблон объекта доступа к данным
Шаблон объекта доступа к данным, который чаще всего сокращается до DAO, представляет собой шаблон, в котором объекты предназначены для взаимодействия с уровнем данных.
Эти объекты часто создают для этой цели экземпляры «SessionFactories» и обрабатывают всю логику взаимодействия с базой данных.
Стандартная практика - создать интерфейс DAO, за которым следует конкретный класс, реализующий интерфейс и все методы, определенные в нем.
Выполнение
Следуя стандартной практике, давайте определим наш интерфейс DAO:
public interface EmployeeDAO {
public List<Employee> getAllEmployees();
public Employee getEmployeeById(int id);
public void addEmployee(Employee e);
public void updateEmployee(Employee e);
public void deleteEmployee(Employee e);
}
И наш конкретный класс реализации вместе с ним:
public class EmployeeDAOImpl implements EmployeeDAO {
List<Employee> employeeList;
public EmployeeDAOImpl() {
employeeList = new ArrayList<Employee>();
Employee david = new Employee(5, "David");
Employee scott = new Employee(7, "Scott");
Employee jessica = new Employee(12, "Jessica");
Employee rebecca = new Employee(16, "Rebecca");
employeeList.add(david);
employeeList.add(scott);
employeeList.add(jessica);
employeeList.add(rebecca);
}
@Override
public List<Employee> getAllEmployees() {
return employeeList;
}
@Override
public Employee getEmployeeById(int id) {
return employeeList.get(id);
}
@Override
public void addEmployee(Employee e) {
employeeList.add(e);
System.out.println("Successfully added " + e.getName());
}
@Override
public void updateEmployee(Employee e) {
employeeList.get(e.getEmployeeId()).setEmployeeName(e.getName());
System.out.println("Successfully update name of employee with id: " + e.getEmployeeId());
}
@Override
public void deleteEmployee(Employee e) {
employeeList.remove(e.getEmployeeId());
System.out.println("Successfully removed employee: " + e.getName() + "with the ID: " + e.getEmployeeId());
}
}
Мы будем использовать эти два класса для добавления, извлечения, обновления или удаления пользователей из нашей базы данных:
public class Employee {
private int employeeId;
private String name;
public Employee(int id, String name) {
this.employeeId = id;
this.name = name;
}
public int getEmployeeId() {
return employeeId;
}
public void setEmployeeId(int id) {
this.employeeId = id;
}
public String getName() {
return name;
}
public void setEmployeeName(String name) {
this.name = name;
}
}
И чтобы проиллюстрировать суть этого паттерна:
public class Main {
public static void main(String[] args) {
EmployeeDAO employeeDao = new EmployeeDAOImpl();
for(Employee employee : employeeDao.getAllEmployees()) {
System.out.println("Employee info: |Name: " + employee.getName() + ", ID: " + employee.getEmployeeId() + "|");
}
}
}
Выполнение этого фрагмента кода даст:
Employee info: |Name: David, ID: 5|
Employee info: |Name: Scott, ID: 7|
Employee info: |Name: Jessica, ID: 12|
Employee info: |Name: Rebecca, ID: 16|
Шаблон переднего контроллера
После отправки запроса передний контроллер становится первым контроллером, к которому он обращается. На основе запроса он решает, какой контроллер наиболее подходит для его обработки, после чего передает запрос выбранному контроллеру.
Front Controller чаще всего используется в веб-приложениях в форме сервлета диспетчера .
Выполнение
Для этой реализации мы определим два простых представления,
FrontController
и Dispatcher
:
public class MainView {
public void showView() {
System.out.println("Showing main view.");
}
}
public class EmployeeView {
public void showView() {
System.out.println("Showing Employee view.");
}
}
Запрос на любой из этих двух может появиться в любой момент. Мы
используем Dispatcher
для обработки запроса, указывая на правильное
представление, после того, как FrontController
обработал запрос
изначально:
public class Dispatcher {
private MainView mainView;
private EmployeeView employeeView;
public Dispatcher() {
mainView = new MainView();
employeeView = new EmployeeView();
}
public void dispatch(String request) {
if(request.equalsIgnoreCase("EMPLOYEE")) {
employeeView.showView();
} else {
mainView.showView();
}
}
}
public class FrontController {
private Dispatcher dispatcher;
public FrontController() {
dispatcher = new Dispatcher();
}
private boolean isAuthenticUser() {
System.out.println("User has successfully authenticated.");
return true;
}
private void trackRequest(String request) {
System.out.println("Request: " + request);
}
public void dispatchRequest(String request) {
trackRequest(request);
if(isAuthenticUser()) {
dispatcher.dispatch(request);
}
}
}
И чтобы проиллюстрировать суть паттерна:
public class Main {
public static void main(String[] args) {
FrontController frontController = new FrontController();
frontController.dispatchRequest("MAIN");
frontController.dispatchRequest("EMPLOYEE");
}
}
Выполнение этого фрагмента кода даст:
Request: MAIN
User has successfully authenticated.
Showing main view.
Request: EMPLOYEE
User has successfully authenticated.
Showing Employee view.
Шаблон перехватывающего фильтра
Фильтры используются еще до того, как запрос будет передан соответствующим контроллерам для обработки. Эти фильтры могут существовать в форме цепочки фильтров и включать несколько фильтров или просто существовать как один фильтр.
Тем не менее, они проверяют авторизацию, аутентификацию, поддерживаемые браузеры, не нарушает ли путь запроса какие-либо ограничения и ограничения и т. Д.
Выполнение
Мы сделаем простую цепочку фильтров с парой фильтров для перехвата запроса после достижения цели.
Начнем с определения интерфейса самого Filter
:
public interface Filter {
public void execute(String request);
}
И пара конкретных реализаций:
public class AuthenticationFilter implements Filter {
@Override
public void execute(String request) {
System.out.println("Authentication request: " + request);
}
}
public class DebuggingFilter implements Filter {
@Override
public void execute(String request) {
System.out.println("Logging request: " + request);
}
}
И, наконец, Target
запроса:
public class Target {
public void execute(String request) {
System.out.println("Executing request: " + request);
}
}
Определив FilterChain
, мы можем добавить несколько фильтров для
перехвата запроса. Давайте определим один для наших двух фильтров:
public class FilterChain {
private List<Filter> filters = new ArrayList<>();
private Target target;
public void addFilter(Filter filter) {
filters.add(filter);
}
public void execute(String request) {
for (Filter filter : filters) {
filter.execute(request);
}
target.execute(request);
}
public void setTarget(Target target) {
this.target = target;
}
}
Теперь нам нужен класс менеджера, который поможет управлять этой
FilterChain
:
public class FilterManager {
FilterChain filterChain;
public FilterManager(Target target) {
filterChain = new FilterChain();
filterChain.setTarget(target);
}
public void addFilter(Filter filter) {
filterChain.addFilter(filter);
}
public void filterRequest(String request) {
filterChain.execute(request);
}
}
И, наконец, Client
будет использовать FilterManager
для отправки
запроса приложению:
public class Client {
FilterManager filterManager;
public void setFilterManager(FilterManager filterManager) {
this.filterManager = filterManager;
}
public void sendRequest(String request) {
filterManager.filterRequest(request);
}
}
Теперь, чтобы проиллюстрировать суть этого паттерна:
public class Main {
public static void main(String[] args) {
FilterManager filterManager = new FilterManager(new Target());
filterManager.addFilter(new AuthenticationFilter());
filterManager.addFilter(new DebuggingFilter());
Client client = new Client();
client.setFilterManager(filterManager);
client.sendRequest("Index");
}
}
Выполнение этого фрагмента кода даст:
Authentication request: Index
Logging request: Index
Executing request: Index
FilterChain
через оба фильтра из FilterChain перед переадресацией в
Target
.
Шаблон локатора услуг
Шаблон, часто встречающийся в веб-приложениях , шаблон Service Locator используется для разделения потребителей службы и конкретных классов, таких как реализации DAO.
Шаблон ищет соответствующий сервис, сохраняет его в кеш-хранилище, чтобы уменьшить количество запросов и, следовательно, нагрузку на сервер, и предоставляет приложению их экземпляры.
Выполнение
Давайте начнем эту реализацию с определения общего интерфейса Service
public interface Service {
public String getServiceName();
public void execute();
}
Пара конкретных классов будет реализовывать этот интерфейс:
public class EmployeeService implements Service {
@Override
public String getServiceName() {
return "Employee Service";
}
@Override
public void execute() {
System.out.println("Executing Employee Service...");
}
}
public class CustomerService implements Service {
@Override
public String getServiceName() {
return "Customer Service";
}
@Override
public void execute() {
System.out.println("Executing Customer Service...");
}
}
Согласно шаблону, при поиске этих сервисов мы должны кэшировать их, чтобы снизить нагрузку на сервер:
public class Cache {
private List<Service> services;
public Cache() {
services = new ArrayList<Service>();
}
public Service getService(String serviceName) {
for(Service service : services) {
if(service.getServiceName().equalsIgnoreCase(serviceName)) {
System.out.println("Returning cached " + serviceName);
return service;
}
}
return null;
}
public void addService(Service newService) {
boolean exists = false;
for(Service service : services){
if(service.getServiceName().equalsIgnoreCase(newService.getServiceName())) {
exists = true;
}
}
if(!exists) {
services.add(newService);
}
}
}
Нам также нужен класс для поиска и создания экземпляров наших сервисов:
public class InitialContext {
public Object lookup(String jndiName) {
if(jndiName.equalsIgnoreCase("EmployeeService")) {
System.out.println("Looking up and initializing Employee Service...");
return new EmployeeService();
} else if(jndiName.equalsIgnoreCase("CustomerService")) {
System.out.println("Looking up and initializing Customer Service...");
return new CustomerService();
}
return null;
}
}
И, наконец, мы можем определить Locator
для предоставления клиенту,
который использует InitialContext
для поиска служб и Cache
для их
кэширования для дальнейшего использования.
public class Locator {
private static Cache cache;
static {
cache = new Cache();
}
public static Service getService(String jndiName) {
Service service = cache.getService(jndiName);
if(service != null) {
return service;
}
InitialContext context = new InitialContext();
Service service1 = (Service)context.lookup(jndiName);
cache.addService(service1);
return service1;
}
}
И чтобы проиллюстрировать суть этого паттерна:
public class Main {
public static void main(String[] args) {
Service service = Locator.getService("EmployeeService");
service.execute();
service = Locator.getService("CustomerService");
service.execute();
}
}
Выполнение этого фрагмента кода даст:
Looking up and initializing Employee Service...
Executing Employee Service...
Looking up and initializing Customer Service...
Executing Customer Service...
Перенести образец объекта
Этот шаблон используется для передачи объектов с большим количеством полей и параметров за один раз. В шаблоне Transfer Object используются новые объекты, используемые только для целей передачи, обычно передаваемые в DAO.
Эти объекты являются сериализуемыми POJO . У них есть поля, соответствующие им геттеры и сеттеры и никакой другой логики.
Выполнение
Объект может выглядеть так:
public class EmployeeVO {
private int employeeId;
private String name;
public EmployeeVO(int employeeId, String name) {
this.employeeId = employeeId;
this.name = name;
}
public int getEmployeeId() {
return employeeId;
}
public void setEmployeeId(int id) {
this.employeeId = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Обратите внимание, что для краткости объект содержит только несколько полей.
Пример нового объекта, который используется только для целей передачи:
public class EmployeeBO {
List<EmployeeVO> employees;
public EmployeeBO() {
employees = new ArrayList<>();
EmployeeVO david = new EmployeeVO(1, "David");
EmployeeVO scott = new EmployeeVO(2, "Scott");
EmployeeVO jessica = new EmployeeVO(3, "Jessica");
employees.add(david);
employees.add(scott);
employees.add(jessica);
}
public void deleteEmployee(EmployeeVO employee) {
employees.remove(employee.getEmployeeId());
System.out.println("Employee with ID: " + employee.getEmployeeId() + " was successfully deleted.");
}
public List<EmployeeVO> getAllEmployees() {
return employees;
}
public EmployeeVO getEmployee(int id) {
return employees.get(id);
}
public void updateEmployee(EmployeeVO employee) {
employees.get(employee.getEmployeeId()).setName(employee.getName());
System.out.println("Employee with ID: " + employee.getEmployeeId() + " successfully updated.");
}
}
И чтобы проиллюстрировать суть паттерна:
public class Main {
public static void main(String[] args) {
EmployeeBO employeeBo = new EmployeeBO();
for(EmployeeVO employee : employeeBo.getAllEmployees()) {
System.out.println("Employee: |" + employee.getName() + ", ID: " + employee.getEmployeeId() + "|");
}
EmployeeVO employee = employeeBo.getAllEmployees().get(0);
employee.setName("Andrew");
employeeBo.updateEmployee(employee);
employee = employeeBo.getEmployee(0);
System.out.println("Employee: |" + employee.getName() + ", ID: " + employee.getEmployeeId() + "|");
}
}
Выполнение этого фрагмента кода даст:
Employee: |David, ID: 1|
Employee: |Scott, ID: 2|
Employee: |Jessica, ID: 3|
Employee with ID: 1 successfully updated.
Employee: |Andrew, ID: 1|
Заключение
При этом все шаблоны проектирования J2EE в Java полностью покрыты с рабочими примерами.
На этом мы завершаем нашу короткую серию статей о шаблонах проектирования Java. Если вы нашли это информативным и пропустили какой-либо из предыдущих, не стесняйтесь проверить их тоже: