Шаблон Template в Java: поведение по умолчанию

Template – это просто абстрактный класс, который задает скелет для расширяющих его классов.

Приведем пример из жизни.

Example

Например, наша жизнь:

Рождаемся -> заканчиваем школу -> заканчиваем университет -> работаем -> умираем. Сплошная скука(((. Эта последовательность (то есть именно сама последовательность без подробностей) и есть скелет.

Абстрактный класс Template просто задает этот скелет(последовательность). А класс реализующий этот абстрактный класс, раскрывает каждый этап.

//Вот тот самый абстрактный класс, который //задает скелет. abstract class Life { //Как видим это действительно просто скелет. //То есть все методы абстрактные, у них нет //реализаций abstract void Burn(); abstract void finishSchool(); abstract void finishColledge(); abstract void Work(); abstract void Die(); //и определяется специальный метод run(), //который задает последовательность //вызовов методов выше. void run() { Burn(); finishSchool(); finishColledge(); Work(); Die(); } } //Давайте же создадим теперь реализацию Template //абстрактного класса. Пускай она будет описывать //жизнь Джона Смита. В этом классе как уже было //сказано должны быть раскрыты этапы, которые задавал //абстрактный класс то есть абстрактные методы должны //быть реализованы. Эти реализации как раз и будут //запускаться методом run в определенной //последовательности. class JohnSmith extends Life { public void Burn() { System.out.println( “Burn in Japan”); } public void finishSchool() { System.out.println( “finished North high school”); } public void finishColledge() { System.out.println( “finished Tokyo University”); } public void Work() { System.out.println( “Worked as AnimeMaker (forgive him lord)”); } public void Die() { System.out.println( “Died in Ogata at 87 yesrs old”); } } public class TemplateExample { public static void main(String args[]) { JohnSmith johnSmith = new JohnSmith(); //запускаем реализованые методы johnSmith.run(); } }

Скомпилируем java файл и запустим программу.

Как видим, Джон Смит успешно прожил свою жизнь в правильной последовательности.  

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

Когда у нас в приложении должно быть много классов, которые по своему выполняют какую-то конкретную последовательность, этот шаблон проектирования может пригодиться.

Шаблон Front Controller в Java: централизованная обработка запросов

Вся суть паттерна Front Controller в том, что все запросы обрабатываются централизовано через одно место.

То есть любой запрос сначала идет в Front Controller, далее Front Controller определяет в какой другой обработчик или View перенаправить запрос и, собственно, перенаправляет.

Приведем пример сервлета, который будет играть роль Front Controller-а, который принимает запросы и форвардит через RequestDispatcher на какую-то вьюху.

import java.util.*; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*;; @WebServlet(“/FrontControllerServlet”) public class FrontControllerServlet extends HttpServlet { protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //Пусть Front Controller проверяет присланные //POST запросом в него параметры и если они //соответствуют тому что в equals то Front Controller //перенаправляет отправившего запрос //на страницу SomePasswordValue, также и сам запрос(req) //туда перенаправляться с помощью forward. if (req.getParameter(“password”).equals(“JohnSmith”) && req.getParameter(“login”).equals(“SomePasswordValue”)) { RequestDispatcher rd= req.getRequestDispatcher(“/welcomePage.jsp”); rd.forward(req, resp); } //в ином случае на страницу errorPage else { RequestDispatcher rd= req.getRequestDispatcher(“/errorPage.jsp”); rd.forward(req, resp); } } }

Шаблон DAO в Java: работа с БД через объект

Data Access Object. Нужен чтобы отделить низкоуровневый доступ к данным от высокоуровневых сервисов.

Низкоуровневый доступ – это, например, взаимодействие с базой с помощью средств JDBC. Мы можем сокрыть прямое использование средств JDBC (то есть, например, подключение к базе, извлечение данных из базы с помощью каких-то сложных цепочек JDBC методов, или сокрыть некоторые SQL запросы) в DAO объекте и уже в высокоуровневых сервисах (в main, например) взаимодействовать с данными базы через методы DAO объекта.

То есть всё низкоуровневое взаимодействие с БД с помощью JDBC происходит в методах DAO объекта, а высокоуровневые сервисы просто используют эти методы.

Для примера в программе ниже лучше пусть ресурсом будет не база, а List коллекция, так будет проще.

import java.util.ArrayList; import java.util.Arrays; import java.util.List; //Создадим простейший класс машины. //Экземпляры класса Car будут храниться //в List. class Car { private String brand; private int speed; Car(String brand, int speed){ this.brand = brand; this.speed = speed; } public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } public int getSpeed() { return speed; } public void setSpeed(int speed) { this.speed = speed; } @Override public String toString() { return “Car{” + “brand=” + brand + “, speed=” + speed + ‘}’; } } //Создадим интерфейс на основе которого //будет создаваться DAO класс на основе //которого будет создаваться DAO объект. interface DAOInterface { public List getAllCars(); public Car getCar(int carNum); public void updateCar(Car car, int carNum); public void deleteCar(Car car); } //Создаем этот DAO класс //реализуя интерфейс выше. class DAOImpl implements DAOInterface { //Вместо сокрытия в DAO классе низкоуровневого //взаимодействия с БД будем скрывать //низкоуровневое взаимодействие //со списком машин listOfCars. List listOfCars; public DAOImpl(){ //Пусть при создании DAO объекта //список инициализируется данными. listOfCars = new ArrayList(Arrays.asList( new Car(“Mazda”,200), new Car(“chevrole”,150))); } // В методах ниже скрываем взаимодействие с List // То есть как можно увидеть ниже все методы // класса List для низкоуровневого взаимодействия // с объектом List (например remove(), get()) // сокрыты в методах DAO класса (в методах // deleteCar(), getCar(), updateCar()) которыми // будут пользоваться высокоуровневые сервисы. @Override public void deleteCar(Car car) { listOfCars.remove(car.getSpeed()); } @Override public List getAllCars() { return listOfCars; } @Override public Car getCar(int carNum) { return listOfCars.get(carNum); } @Override public void updateCar(Car car, int carNum) { Car carfromlist = listOfCars.get(carNum); carfromlist = car; } } //Воспользуемся уже DAO объектом //в высокоуровневом сервисе (в мейн). public class DAOExample { public static void main(String args[]) { //Через DAO объект dao будем //взаимодействовать с ресурсом(списком). DAOInterface dao = new DAOImpl(); //Как видим все непосредственное //взаимодействие со списком сокрыто //в DAO объекте, а в мейн мы //взаимодействуем со списком посредством //методов DAO объекта. Car car = dao.getCar(1); System.out.println(car); car.setBrand(“Toyota”); dao.updateCar(car, 1); System.out.println(dao.getCar(1)); } }

Скомпилируем java файл и запустим программу.

Как видим, объект машины в списке был изменен с помощью методов DAO объекта, а не напрямую.  

О DAO шаблоне будет еще много говориться в разделе посвященном spring.

Шаблон Singleton в Java: обеспечение единственного экземпляра

Singleton – класс, который гарантировано имеет только один экземпляр.

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

//Создадим класс синглтона. В нем сразу же создается //и объект этого класса и никогда больше объект этого //класса не создается и не может быть создан в принципе. //В этом вся суть синглтона. class Singleton { String str = “Singleton String!!!”; //Тем что конструктор private достигается //невозможность создания экземпляра этого класса //где-либо вне определения класса Singleton. //Как мы помним private элементами класса //можно пользоваться исключительно в пределах //определения класса поэтому конструктором вне //класса воспользоваться нельзя, а значит //и создать объект этого класса нельзя так как при //создании объекта всегда вызываеться конструктор private Singleton() { } //В пределах определения класса как уже было //сказано private элементы доступны. //Поэтому можем создать экземпляр здесь. private static Singleton singleton = new Singleton(); //Чтобы получить этот единственный экземпляр //у синглтона должен быть публичный метод, //который возвращает его. public static Singleton getInstance() { return singleton; } } public class SingletonExample { public static void main(String[] args) { //Во время работы приложения в любой его точке //может быть извлечен и использован единственный //экземпляр синглтона созданный внутри класса //синглтона. //Создадим две ссылки с именами singleton //и singleton1. Обе они ссылаются на один и тот //же единственный возможный объект синглтона. Singleton singleton = Singleton.getInstance(); Singleton singleton1 = Singleton.getInstance(); //Чтобы доказать что singleton и singleton1 //ссылаются на один и тот же объект //давайте изменим str в объекте синглтона через //ссылку singleton. singleton.str = “New singleton string!!!!!!!”; //и выведем эту измененную строку в объекте //через ссылку singleton1. System.out.println(singleton1.str); } }

Скомпилируем java файл и запустим программу.

Как видим, строка в единственном объекте синглтона успешно была изменена через одну ссылку, а через другую была выведена на консоль. То есть работать можем с единственным объектом синглтона через разные ссылки в приложении.


Singleton vs Статический класс

Часто у многих возникает вопрос – “Зачем нужен синглтон если есть статический класс, который тоже можно использовать как какой-то глобальный ресурс?”

Дело в том, что сиенглтон является экземпляром, когда статический класс нет, поэтому синглтоном можно пользоваться, как нормальным объектом, например, передать как объект куда-то.

Также синглтон в отличие от стат. класса может реализовать или наследовать что-то.

Что такое шаблоны проектирования? Шаблон Factory в Java

При проектировании того или иного программного продукта разработчик сталкивается с большим количеством проблем. И многие из этих проблем бывают ТИПИЧНЫМИ при разработке.

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

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

Сами подумайте, знание существующего, проверенного временем шаблона для решения типичной при построении приложения проблемы может существенно упростить разработчику жизнь. Без знания шаблонов разработчик может намудрить что-то такое, что скажется и на продуктивности, и на читабельности кода, и на другом.


Шаблон  Factory

Первый паттерн, который мы рассмотрим называется Factory(фабрика).

Factory довольно простой паттерн.

Часто в приложении может понадобиться вспомогательный объект с помощью которого можно создавать другие объекты. То есть это объект, который является фабрикой других объектов.

Важное уточнение, что фабрика создает экземпляры ОДНОТИПНЫХ классов.

В примере ниже фабрика книг производит разные книги (экземпляры этих книг).

//Тип создаваемых объектом фабрики объектов //это книга. Очевидно что все реализующие //этот интерфейс классы будут тоже книгами //только уже конкретными книгами. interface Book { void read(); } //Реализуем этот интерфейс //таким образом создавая конкретную //книгу “Властелин колец” class LordOfTheRings implements Book { public void read() { System.out.println( “Reading Lord of The Rings!”); } } //Реализуем этот интерфейс еще раз //таким образом создавая вторую //конкретную книгу “На западном фронте //без перемен.”. class AllQuietOnTheWesternFront implements Book { public void read(){ System.out.println( “Reading All Quiet On The ” + “Western Front!”); } } //Создаем класс фабрики книг. //Объект класса фабрики книг должен //уметь создавать объект любого //определенного выше класса книги. class BooksFactory { //Метод объекта фабрики ниже как раз будет //создавать объект конкретной книги. //Как можно увидеть аргументом этому методу //передается название книги объект которой //хочет создать тот кто вызывает этот метод. public Book createBook(String bookName) { switch (bookName) { //То есть как видим если аргументом методу //была передана строка “Lord Of The Rings” case “Lord Of The Rings”: //то будет создан и возвращен объект //класса LordOfTheRings. return new LordOfTheRings(); //Если аргументом методу была передана //строка “All Quiet On The Western Front” case “All Quiet On The Western Front”: //то будет создан и возвращен объект //класса AllQuietOnTheWesternFront. return new AllQuietOnTheWesternFront(); default: return null; } } } //Воспользуемся созданными классами в main методе. public class FactoryExample { public static void main(String[] args) { //Создадим объект книги “Властелин колец” //используя объект фабрики книг. Book lordOfTheRings = new BooksFactory() .createBook(“Lord Of The Rings”); //Создадим объект книги “На западном фронте //без перемен” используя объект фабрики книг. Book allQuietOnTheWestFr = new BooksFactory() .createBook(“All Quiet On The ” + “Western Front”); //Проверим успешно ли нам создала фабрика //объектов разных книг вызвав методы этих //этих книг. lordOfTheRings.read(); allQuietOnTheWestFr.read(); } }

Скомпилируем java файл и запустим программу.

Как видим, read у созданных объектов вывел на консоль разные строки. Значит BooksFactory умеет создавать РАЗНЫЕ ОДНОТИПНЫЕ объекты.

Когда применяется Factory?

Данный шаблон применяется по двум основным причинам:

1. Все объекты книг создаются одним методом.

Представьте себе приложение, которое содержит ранее написанные нами классы книг и класс фабрики книг, и которое также имеет пользовательский интерфейс.

Пусть этот интерфейс содержит поле для ввода названия книги и представьте, что то, что введет пользователь в это поле будет передано аргументом в метод фабрики createBook.

Метод фабрики createBook создаст объект той книги, которую хочет пользователь по названию, которое он ввел.

Суть в том, что тому, кто пишет программу заранее не известно, что пользователь напишет в поле для ввода в пользовательском интерфейсе, объект какой именно книги пользователь захочет создать.

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

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