Шаблон фабричного метода в Java с использованием обобщений, как? - PullRequest
5 голосов
/ 13 мая 2009

У меня есть код, который выглядит следующим образом:

public interface BaseDAO{
// marker interface
}

public interface CustomerDAO extends BaseDAO{
public void createCustomer();
public void deleteCustomer();
public Customer getCustomer(int id);
// etc
}

public abstract class DAOFactory {
public BaseDAO getCustomerDAO();
public static DAOFactory getInstance(){
  if(system.getProperty("allowtest").equals("yes")) {
  return new TestDAOFactory();
  }
  else return new ProdDAOFactory();
}

public class TestDAOFactory extends DAOFactory{
public BaseDAO getCustomerDAO() {
  return new TestCustomerDAO(); // this is a concrete implementation
  //that extends CustomerDAO
  //and this implementation has dummy code on methods
}

public class ProdDAOFactory extends DAOFactory {
public BaseDAO getCustomerDAO() {
  return new ProdCustomerDAO(); // this implementation would have 
  // code that would connect to the database and do some stuff..
}
}

Теперь я знаю, что этот код пахнет .. по многим причинам. Тем не менее, этот код также здесь: http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html, см. 9,8

Что я собираюсь сделать, это: 1) Переключить мои реализации DAOs во время выполнения на основе среды (свойства системы). 2) Используйте дженерики Java, чтобы я мог избежать приведения типов ... например делает что-то вроде этого:

CustomerDAO dao = factory.getCustomerDAO();
dao.getCustomer();

В отличие от:

CustomerDAO dao = (CustomerDAO) factory.getCustomerDAO();
dao.getCustomer();

Ваши мысли и предложения, пожалуйста.

Ответы [ 4 ]

10 голосов
/ 13 мая 2009

Вы должны определить фабрику так:

public abstract class DAOFactory<DAO extends BaseDAO> {
public DAO getCustomerDAO();
public static <DAO extends BaseDAO> DAOFactory<DAO> getInstance(Class<DAO> typeToken){
  // instantiate the the proper factory by using the typeToken.
  if(system.getProperty("allowtest").equals("yes")) {
  return new TestDAOFactory();
  }
  else return new ProdDAOFactory();
}

getInstance должен возвращать правильный тип DAOFactory.

Заводская переменная будет иметь тип:

DAOFactory<CustomerDAO> factory = DAOFactory<CustomerDAO>.getInstance(CustomerDAO.class);

и использование будет правильно напечатано:

CustomerDAO dao = factory.getCustomerDAO();
dao.getCustomer();

единственной проблемой, вероятно, будет приведение типов внутри методов getInstance.

6 голосов
/ 13 мая 2009

Есть куча статей, подробно описывающих, что вам нужно:

Обратите внимание, что, в отличие от вашего примера, нет причин, по которым методы DAOFactory не должны возвращать фактические подклассы (т.е. CustomerDAO getCustomerDAO()). Кроме того, основное преимущество использования универсальных DAO заключается в том, что тип сущности «обобщен», поэтому вам не нужно приводить из load()/get()/find() и аналогичных методов.

5 голосов
/ 13 мая 2009

Ваш пример не демонстрирует необходимость в BaseDAO, и нет никаких причин, по которым DAOFactory.getCustomerDAO() не должен быть объявлен для возврата CustomerDAO. Так что я не вижу там необходимости в дженериках. Однако учтите следующее:

interface DataAccess<T> {
  void store(T entity);
  T lookup(Serialiable identifier);
  void delete(Serializable identifier);
  Collection<? extends T> find(Criteria query);
}

abstract class DataAccessFactory {
  abstract DataAccess<T> getDataAccess(Class<T> clz);
  static DataAccessFactory getInstance() {
    ...
  }
}

Я использовал такой подход в нескольких проектах, и очень приятно написать один DAO, который работает для каждого объекта в модели. Слабость заключается в «искателях» методов. Есть несколько опрятных подходов, и предстоящая работа в JPA стандартизирует API «Критерии», но на данный момент зачастую проще всего выявить критерии базового механизма персистентности.

0 голосов
/ 13 мая 2009

Когда я использовал фабрики, я обычно использовал instanceof для определения истинного типа объекта. Например:

CustomerDAO dao;
if (factory.getCustomerDAO() instanceof CustomerDAO) {
   dao = factory.getCustomerDAO();
}
dao.getCustomer();

Мне это кажется более понятным, особенно если factory.getCustomerDAO () не возвращает ничего похожего на CustomerDAO (из-за изменений в реализации).

Только мои два цента.

...