Шаблон DAO (Data Access Object)

Шаблон DAO – это объект, который предоставляет абстрактный интерфейс к какому-либо типу баз данных или механизму хранения (например хранение в файле). Определённые возможности предоставляются независимо от того, какой механизм хранения используется и без необходимости специальным образом соответствовать этому механизму хранения.

Другими словами. Старайтесь писать ваше приложение таким образом, чтобы большая часть его ничего вообще “не знала” о том, какой механизм хранения данных Вы используете. Будь то база данных, файловая система или еще какой-нибудь способ хранения данных. Для этого разработчики Java рекомендуют шаблон DAO.

Используя этот шаблон Ваше приложение получается не завязано на механизм хранения данных. Это дает Вам гибкость в смене этих механизмов хранения. Например, если Вы решили перейти с одной базы данных на другую, то Вам достаточно будет просто реализовать абстрактный интерфейс (предоставляемый DAO) другим способом, а все оставшееся приложение будет без изменений.

Вот пример такого абстрактного интерфейса, о котором мы говорили ранее. Если его реализовать используя JDBC или например API, которое позволяет работать с файлами в Java, то оставшееся приложение будет знать только про этот интерфейс и ничего про его реализации и когда мы например каким-нибудь способом подменим реализацию, то оставшееся приложение будет работать как раньше.

Вот, например, НЕПОЛНАЯ реализация этого интерфейса через JDBC для базы H2 (скорее всего будет работать и для некоторых других баз данных).

Вот пример использования этого интерфейса. Заметьте, что в коде нет ни слова о том, что где-то используется JDBC, SQL или название базы данных (статический блок не в счет. от этого мы научимся избавляться).

Для лучшего запоминания поглядите на небольшую диаграмму классов простейшей реализации этого шаблона.

4

Подробнее

Что такое класс в java?

В этой статье я попробую простым языком и кратко рассказать, что такое класс в общем смысле и применительно к языку java.

В общем смысле класс – это абстракция какого-нибудь предмета или явления. Например, если мы пишем приложение телефонный справочник, то со 100%-й вероятностью можно утверждать, что в нем будет как-то описываться человек. В этом описании, например, будет фигурировать ФИО этого человека, его адрес и его телефонный номер.

Это его описание есть абстракция. Абстракция – это упрощение какого-то понятия или предмета. В данном случае мы упростили человека до ФИО, адреса и телефона. Нам совсем нет необходимости учитывать такие понятия как его рост, возраст или трехмерную модель. От всего этого мы абстрагировались.

Еще можно сказать, что класс – это чертеж объекта или понятия. В этом чертеже мы учитываем только важные характеристики. Эти характеристики могут варьироваться в зависимости от поставленной задачи. Например, для приложения телефонный справочник достаточно ФИО, адреса и телефонного номера. А для приложения, которое высчитывает ваш индекс массы тела нужно знать только ваш рост, вес, пол и возраст. Оно полностью абстрагировалось от таких понятий как ФИО, адрес и телефон.

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

Например, для телефонного справочника он может выглядеть так.

Грубо говоря в java класс – это некая структура для удобства объединяющая в себе несколько переменных (полей класса) и действий (методов). По ней будут создаваться объекты. Если кратко, то объекты – это, что-то конкретное, например, Вы, я, Ваша мама и т.д. Это что-то, что сделано по классу (как по чертежу). Этими объектами будет в дальнейшем манипулировать приложение.

Вот еще пример класса человек, но уже для приложения рассчитывающего индекс массы тела.

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

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

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

Подробнее

Observer – Наблюдатель

Observer – Наблюдатель (Паттерны проектирования)

Для чего он нужен?

Этот шаблон очень популярен потому, что очень часто возникает необходимость выстроить некую коммуникацию между объектами или модулями приложения. Вернее, если происходит какое-то событие – нужно чтобы о нем узнали все кто заинтересованны в его наступлении. Когда мы кликаем по кнопке, то происходит событие о котором узнает обработчик данного события и выполняет некоторые действия.

Пример из жизни:

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

Пример:

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

Перечислю исходные данные:

Класс Magazine(журнал), содержит поле issue, которое хранит порядковый номер выпуска.

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

Интерфейс Magazinable нужен просто для удобного вывода на экран всех полученных журналов подписчика.

Класс Person – конкретный подписчик. Содержит имя и список полученных им журналов. Реализует метод для получения нового журнала.

Класс Publisher – это издатель, который рассылает журналы своим подписчикам. Также содержит методы для удаления и добавления подписчиков в список рассылки. И метод оповещения подписчиков о том, что вышел новый журнал.

 

Ну и метод main, который создает несколько подписчиков. Подписывает их на получения журналов от издателя. Рассылает журналы и выводит список полученных журналов у подписчиков.

Наблюдатель 5

Это результат выполнения кода. Как видно, Маша не оформляла подписку – в результате журналов у нее нет. Саша отписался после получения первого журнала – в результате у него только один журнал. А Паша – получил все журналы, потому что не отписывался. 

Если это все, что от нас требует заказчик, то этот код максимально прост. Это идеальное решение.

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

 

Добавили новый класс Company(компания). По своему содержимому, он идентичен классу Person. Также хранит имя компании + список полученных журналов.

наблюдатель 7

Проблемы начинаются когда мы понимаем, что без модификации класса Publisher нам не обойтись. Во-первых, нам некуда деваться и нужно добавлять еще список компаний подписанных на рассылку, и инициализировать его в конструкторе. Затем в метод notifyAllSubscribers мы добавляем цикл для перебора подписанных юридических лиц. И в-третьих, добавляем еще по два метода для подписки и отписки новой сущности. В итоге мы жесточайше дублируем код:

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

наблюдатель 9

В классе Main мы сделали достаточно допустимые изменения. От них никуда не деться.

наблюдатель 10

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

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

Теперь давайте посмотрим на пример кода из параллельной вселенной, где мы изначально воспользовались данным шаблоном:

 

Классы Magazine и Magazinable ни как не изменились по сравнению с первым примером. Самые большие изменения, это два появившихся интерфейса:

Этот  интерфейс должен реализовывать класс, который рассматривается в качестве подписчика. Метод add обновляет состояние подписчика в тот момент, когда редакция издает новый журнал.

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

От первого примера класс Person отличается только тем, что он теперь реализует интерфейс Subscriber. Внутри этого класса ничего не поменялось, потому что у него и так был метод add.

Данный Publisher тоже реализует новый интерфейс Subscribable. Его основное отличие от первого примера в том, что в данном интерфейсе больше не фигурирует класс Person. Мы абстрагировались от конкретных классов и работаем теперь только с интерфейсом Subscriber.

Это и называется “программировать на уровне интерфейсов”. Таким образом код становится менее связанным и чуть позже Вы увидите преимущества такого подхода.

наблюдатель 18

Класс Main также ничем не отличается от первого примера, как и вывод на экран

Выполняя новые требования заказчика мы видим преимущества архитектуры основанной на интерфейсах. Для того что бы добавить возможность подписываться юридическим лицам, нам достаточно просто создать класс Company. Единственное чем он отличие от первого примера в том, что он тоже реализует интерфейс Subsciber.

Нам не нужно менять класс Publisher. По тому что в нем используется абстракция Subscriber.

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

наблюдатель 20

наблюдатель 21

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

Теперь поговорим о теории данного шаблона.

Скриншот (30.08.2015 14-34-58)

Обратите внимание на схему. Те классы на которые подписываются называются СУБЪЕКТАМИ а те которые подписываются называются НАБЛЮДАТЕЛЯМИ. В нашем примере субъектом был интерфейс Subscribable, а наблюдателями был интерфейс Subscriber. Соответственно их реализации были ConcreteSubject и ConcreteObserver

На схеме Subject может быть как интерфейсом так и классом. Вариант когда он является интерфейсом более гибкий, по крайней мере в java это так. Причина этому то, что в Java невозможно множественное наследование. Если Ваш класс Subject уже является чьим-то потомком, то это накладывает на Вас некоторые ограничения. Таких ограничений нет, если Вы работаете с интерфейсом.

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

Необязательно очень точно следовать UML схеме шаблон. Мы ведь хотим получить его преимущества, а не скопировать схему в коде. Я не оставлял ссылку на subject в конкретных реализациях наблюдателя, как это сделано на схеме. Смотрите пунктир от ConcreteObserver. И не создавал метод get/setState  в ConcreteSubject.

Помните, что субъект ничего не должен знать про реализацию наблюдателей.

Определение

Паттерн наблюдатель определяет отношение “один-ко-многим” между объектами.

Один субъект – много наблюдателей.

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

Зависимые объекты это наблюдатели.

Плюсы и минусы

  1. Единственное, что знает субъект о наблюдателе это то, что он реализует интерфейс Observer. Это позволяет нам добавлять новые типы наблюдателей без модификации субъекта.
  2. Новые наблюдатели могут подписываться и отписываться от получения обновлений в любой момент.
  3. Субъект и наблюдатель могут использоваться повторно.
  4. Изменение одного никак не  влияет другого.

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

В языке Java существует встроенная поддержка данного паттерна

Это класс java.util.Observable, его расширяют субъекты. Я уже говорил про ограничения, которые накладывает на нас наследование в реализации через класс. Еще есть java.util.Observer, это интерфейс для наблюдателей.

Давайте наш последний пример переделаем, чтобы он использовал данные класс и интерфейс.

Первое что мы сделаем, это удалим наши интерфейсы.

Скриншот (30.08.2015 15-22-53)

Скриншот (30.08.2015 15-23-22)

Классы Company и Person теперь реализуют интерфейс Observer. Метод update вторым параметром получает ссылку на объект журнал.

Скриншот (30.08.2015 15-24-41)

Класс publisher больше не отвечает за подписку и отписку. Все эти методы находятся в его предке. Это хорошая новость, т.к. нам теперь не нужно их реализовывать самостоятельно. Тем более, что эти методы синхронизованы. Для того чтобы все заработало, нужно еще помнить про метод setChanged, который переключает состояние класса в режим “изменен”. Если этого не сделать, то наблюдатели не получат обновления.

 

Скриншот (30.08.2015 15-26-09)

Класс Main теперь выглядит так. Обратите внимание на то, как теперь называются методы для работы с подписками.

Вывод на экран ничем не отличается от предыдущих примеров.

В JDK есть еще несколько подобных классов и интерфейсов. Например, класс java.beans.PropertyChangeSupport и интерфейс java.beans.PropertyChangeListener

Если не ленитесь, посмотрите сами.

Еще один тип наблюдателей

Скриншот (30.08.2015 15-28-01)

Если бы наша редакция выпускала еще и газеты, то мы могли бы реализовать шаблон немного по другому. Чтобы придать больше гибкости нашим классам. Наблюдатели могут получать либо газету, либо журнал. Просто они для этого должны сделать запрос через соответствующий getter.

 

Скриншот (30.08.2015 15-29-04)

 

Скриншот (30.08.2015 15-29-41)

 

Например человек выписывает только журналы, а компания только газеты. Этого можно добиться и не используя java-скую реализацию этого шаблона. Например, передавая каждому наблюдателю ссылку на издательство. Полностью код приводить не буду, попробуйте реализовать его сами.

Заключение

Все!

Напишите в комментариях про какой следующий шаблон Вы хотите услышать. Или может Вас интересует что-то другое? Короче пишите в комментарии. Если ленитесь или стесняетесь писать сами, то пролайкайте комментарии других. По количеству лайков я определю какой вопрос победил и сниму сюжет на эту тему.

Читайте книги, пока!

 

 

Подробнее

Инкапсуляция

Что такое инкапсуляция?

Когда я, относительно недавно, начал подыскивать новое место работы, то был удивлен вопросами которые мне задавали. Меня довольно часто спрашивали о полиморфизме, инкапсуляции и наследовании. Тогда я думал, что подобные вопросы только для новичков, но оказывается я был неправ. Я, конечно, не могу залезть в голову тем людям и понять почему они это спрашивали. Но у меня сформировались некоторые соображения на эту тему, которыми собираюсь поделиться с Вами. Я хочу, чтобы Вы кое о чем подумали:

Это два варианта реализации класса TimeInterval:

Думаю, разницу Вам объяснять не нужно. Понятно, что второй вариант, в котором мы спрятали поля переменных внутри класса, а наружу выставили только геттеры и сеттеры – это вариант в котором мы использовали один из ключевых аспектов ООП инкапсуляцию.

А вот фиг вам! Ни черта это не инкапсуляция!
Вот подумайте. Если для нас важно чтобы start <= end, то какая вообще разница, меняем мы значение переменной start через сеттер или напрямую?

Если Вы думаете, что инкапсуляция это просто “спрятать все поля внутри класса от пользователя”, то Вы заблуждаетесь. Это лишь имитация, а инкапсуляция это нечто другое.

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

Так что такое инкапсуляция?

Это механизм позволяющий нам обеспечить согласованность данных объекта или модуля, от приведения их в некорректное состояние. Обычно для этого прячут все поля и предоставляют только простой набор методов. Как я уже говорил, нам не обязательно даже прятать всё и вся. Достаточно просто запретить “контрактом” или документацией использовать такую возможность. Существуют языки в которых нет спецификаторов доступа (private, protected, public), но в них успешно используют инкапсуляцию.

Я вижу некоторое сходство инкапсуляции с синхронизацией в много поточных приложениях. В обоих случаях мы стремимся защитить наши данные от повреждений или рассогласованности, вызванных некорректным использованием. Просто когда пишете какой-то компонент, предусмотрите вариант что им будет пользоваться блондинка-программист с синдромом Дауна и чтобы у нее не было шансов привести его в некорректное состояние.

Чем плох данный пример (Код 2)?

Просто мы можем очень легко привести TimeInterval в некорректное состояние (рассогласовать start и end). Для этого достаточно в любой сеттер передать дату которая не будет соответствовать другой. Мы могли бы, конечно, прикрутить валидацию в сеттеры и это немного улучшило бы ситуацию. Но стало бы неудобно использовать данный класс.

Я бы сделал так:

Теперь нам гораздо сложнее рассогласовать переменные start и end. Приведу еще один пример из жизни:

В классе Car нельзя делать метод removeParkingBrake публичным, потому что машина покатится и врежется в столб, и выйдет из строя. Нельзя разрешать использовать этим методом. А если у пользователя будет возможность вызывать те методы, что находятся в startMoving, но напрямую, то это тоже плохо. Он может забыть прогреть двигатель и в итоге испортит его. Это все утечка внутренней логики работы класса. Нарушение инкапсуляции.

Смотрите метод startMoving. Первоначально задумывалось, что пользователь может использовать только startMoving. В таком случае ничего плохого не произойдет.

Зачем же нам нужна инкапсуляция?

Когда Вы пишете простое приложение у себя дома, то в полной мере не ощутите преимущества такого подхода. Это и не удивительно, ведь инкапсуляция предназначена для упрощения разработки больших приложений с несколькими разработчиками.

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

Когда Вы работаете один на проекте, то Вы свой код обычно держите в голове. И, скорее всего, знаете все нюансы использования Ваших компонентов. Но в большой команде, где каждый занимается своим модулем, для Вас другие модули как черный ящик. Если на Ваш вопрос о инкапсуляции к таким разработчикам, слышите “Инкапсуляция?! Не не слышал”, то будьте уверены – прикрутив новый функционал, Вы сломаете все приложение.

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

Преимущества и недостатки использования инкапсуляции?

Преимущества:

  1. Полный контроль над входящими и исходящими данными.
  2. Можно без боязни сломать все остальное приложение, править реализацию  методов компонентов. Так как во всем остальном приложении фигурирует только интерфейс, а Вы меняете только реализацию логики.

Недостатки:

  1. Если Вы нашли ошибку в библиотеке которую используете, то Вам будет трудно ее исправить.
  2. Снижается скорость работы приложения.

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

Почему же задают такой простой вопрос на собеседовании?

Думаю, все из-за того, что большинство программистов не до конца понимают, что такое инкапсуляция. Если человек претендует на позицию Senior’а, но не знает ответа на такой фундаментальный вопрос, то нужно подумать, стоит ли этот специалист денег, которые он запрашивает.

Это как лакмусовая бумажка. И это лично мое мнение.

На этом все.

Подробнее