Реклама

понедельник, 12 августа 2013 г.

Порождающие шаблоны проектирования. Абстрактная фабрика (Abstract Factory)


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

  Паттерн «Абстрактная фабрика», как и паттерн «Фабричный метод» используется для создания объектов. Но их основное различие в том, что он разрешает создание объектов классов, связанных между собой.

Где на диаграмме:
  • AbstractFactory - абстрактная фабрика:
    - объявляет интерфейс для операций, создающих абстрактные объекты-продукты;
  • ConcreteFactory - конкретная фабрика:
    - реализует операции, создающие конкретные объекты-продукты;
  • AbstractProduct - абстрактный продукт:
    - объявляет интерфейс для типа объекта-продукта;
  • ConcreteProduct - конкретный продукт:
    - определяет объект-продукт, создаваемый соответствующей конкретной фабрикой;
    - реализует интерфейс Abstract Product;
  • Client - клиент:
    - пользуется исключительно интерфейсами, которые объявлены в классах AbstractFactory и AbstractProduct.

Реализация


   Для примера рассмотрим библиотеку, которая будет содержать в себе классы для работы с разными СУБД. Проектировщик библиотеки возможно захочет держать ее классы слабо связанными с клиентскими приложениями. Таким образом новые классы для работы с СУБД можно будет легко встроить в приложения.
   Давайте предположим, что для работы с СУБД нам необходимо два класса — Connection и Command. Это могут быть абстрактные классы, с реализациями для каждого типа СУБД. К примеру, для mySql это может быть MySqlCommand и MySqlConnection. По такому же принципу, для Oracle это будут OracleCommand и OracleConnection.

   Клиентское приложение не использует библиотеки СУБД напрямую (чтобы сохранить принцип слабого связывания). Вместо этого используются абстрактные классы. Для этого потребуется:
  1. Механизм, который позволит получать экземпляры классов для необходимых СУБД
  2. Гарантировать, что классы могут использоваться СУБД только одного типа в единицу времени.
  Это может быть сделано путем создания иерархии фабрик. Каждая фабрика будет иметь фабричный метод который создаст экземпляр класса для СУБД. Это ограничит создание объектов только для определенной СУБД в единицу времени.
  Давайте посмотрим, как это будет выглядеть на Java.
Command.java
 package ua.dp.allmycircuits.abstractfactory;  
 /**  
  * Абстрактный класс для SQL команд  
  * @author winkiller  
  */  
 public abstract class Command {  
   public abstract ResultSet execute();  
 }  

Connection.java
 package ua.dp.allmycircuits.abstractfactory;  
 /**  
  * Абстрактный класс для соединения с СУБД  
  * @author winkiller  
  */  
 public abstract class Connection {  
   public abstract void connect();  
 }  

MySqlCommand.java
package ua.dp.allmycircuits.abstractfactory;  
 /**  
  * Реализация Command для mySQL  
  * @author winkiller  
  */  
 public class MySqlCommand extends Command{  
   @Override  
   public ResultSet execute() {  
     System.out.println("MySqlCommand.execute");  
     return new MySqlResultSet();  
   }  
 }  

OracleCommand.java
 package ua.dp.allmycircuits.abstractfactory;  
 /**  
  * Реализация Command для Oracle  
  * @author winkiller  
  */  
 public class OracleCommand extends Command{  
   @Override  
   public ResultSet execute() {  
     System.out.println("OracleCommand.execute");  
     return new OracleResultSet();  
   }  
 } 

 MySqlConnection.java
 package ua.dp.allmycircuits.abstractfactory;  
 /**  
  * Реализация класса для соединения с mySQL  
  * @author winkiller  
  */  
 public class MySqlConnection extends Connection{  
   @Override  
   public void connect() {  
     System.out.println("MySqlConnection.connect");  
   }  
 }  

OracleConnection.java
 package ua.dp.allmycircuits.abstractfactory;  
 /**  
  * Реализация класса для соединения с Oracle  
  * @author winkiller  
  */  
 public class OracleConnection extends Connection{  
   @Override  
   public void connect() {  
     System.out.println("OracleConnection.connect");  
   }  
 } 

 DbFactory.java
 package ua.dp.allmycircuits.abstractfactory;  
 /**  
  * Абстрактный класс фабрики  
  * @author winkiller  
  */  
 public abstract class DbFactory {  
   public abstract Connection createConnection();  
   public abstract Command createCommand();  
 }  

MySqlFactory.java
 package ua.dp.allmycircuits.abstractfactory;  
 /**  
  * Реализация фабрики для mySQL  
  * @author winkiller  
  */  
 public class MySqlFactory extends DbFactory{  
   @Override  
   public Connection createConnection() {  
     return new MySqlConnection();  
   }  
   @Override  
   public Command createCommand() {  
     return new MySqlCommand();  
   }  
 }  

OracleFactory.java
 package ua.dp.allmycircuits.abstractfactory;  
 /**  
  * Реализация фабрики для Oracle  
  * @author winkiller  
  */  
 public class OracleFactory extends DbFactory{  
   @Override  
   public Connection createConnection() {  
     return new OracleConnection();  
   }  
   @Override  
   public Command createCommand() {  
     return new OracleCommand();  
   }  
 }  

 ResultSet.java
 package ua.dp.allmycircuits.abstractfactory;  
 /**  
  * Абстрактный класс для результата  
  * @author winkiller  
  */  
 public abstract class ResultSet {  
   public abstract void hasNext();  
 }

MySqlResultSet.java
 package ua.dp.allmycircuits.abstractfactory;  
 /**  
  * Реализация класса для результата mySQL  
  * @author winkiller  
  */  
 public class MySqlResultSet extends ResultSet{  
   @Override  
   public void hasNext() {  
     System.out.println("MySqlResultSet.hasNext");  
   }  
 }  

 OracleResultSet.java
 package ua.dp.allmycircuits.abstractfactory;  
 /**  
  * Реализация класса для результата Oracle  
  * @author winkiller  
  */  
 public class OracleResultSet extends ResultSet{  
   @Override  
   public void hasNext() {  
     System.out.println("OracleResultSet.hasNext");  
   }  
 }  

 AbstractFactory.java
 package ua.dp.allmycircuits.abstractfactory;  
 /**  
  * Проверка абстрактной фабрики  
  * @author winkiller  
  */  
 public class AbstractFactory {  
   public static void main(String[] args) {  
     transact(new MySqlFactory());  
     transact(new OracleFactory());  
   }  
   private static void transact(DbFactory db){  
     Connection con = db.createConnection();  
     con.connect();  
     Command cmd = db.createCommand();  
     ResultSet set = cmd.execute();  
     set.hasNext();  
   }  
 }  

 Плюсы

  1. Позволяет придерживаться принципа слабого связывания
  2. Ограничивает использование классов из одного семейства/конфигурации в одну единицу времени
  3. Легкое изменение конфигурации

Минусы

  1. Количество классов, которое может быть проинициализировано, ограничено интерфейсом абстрактной фабрики
  2. Не совсем удобное добавление новых классов
Исходники примера можно скачать с Github

Комментариев нет:

Отправить комментарий