Какой шаблон дизайна лучше для сохранения / удаления данных и почему? - PullRequest
2 голосов
/ 08 ноября 2011

Я не могу выбрать между следующими двумя образцами, например.сохранение dataObject (бин в моем случае).Два варианта:

1-й

abstract class DataService {
    protected void save(Object data){
        //persist the data
    }
}
//the service for Project objects
class ProjectService extends DataService {
    public void saveProject(Project prj, Object... args /*other save options*/ ){
        // some preprocessing, checking, validation
        save(prj); //call the method in DataService
        // do postprocessing
    }
}

//calling save
projectService.saveProjec(project, /*some args*/);

2-й

abstract class DataService {
    public void save(Object data){
        if(beforeSave(data)){
            // persist the data
            afterSave(data);
        }
    }
    protected boolean beforeSave(){
        return true;
    }
    protected void afterSave(){
    }
}

//the service for Project objects
class ProjectService extends DataService {
    public initSave(Object... args /*other save options*/ ){
        // store these options in class properties
    }
    @Override
    protected bool beforeSave(Project objectAboutToBeSaved){
        // some preprocessing, checking, validation
        // use class properties set by initSave if needed
        return true;//if we want to continue with the saving procedure
    }

    @Override
    protected bool afterSave(Project savedObject){
        // do postprocessing
        // use class properties set by initSave if needed
    }
}

//calling save
projectService.initSave(/*some args*/);
projectService.save(project);

В настоящее время мы используемпервый шаблон, но я начал думать о переходе ко второму, потому что:

(PROS)

  • лучшее логическое разделение
  • унифицированный метод именования нескольких объектовтипы (позволят создать общие модульные тесты: например, инициализировать каждый объект и его службу и сохранить вызов)

(CONS)

  • сложнее в настройке (initSave) - возможно, даже придется включить метод разрыва

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

Сейчас я занимаюсь разработкой на Java - Spring + Spring Data Graph - (таким образом, Java-код), но, на мой взгляд, этот вопрос может быть довольно общим.

Примечание: пример был приведен для сохранения, но то же самое будет и для процесса удаления.

Ответы [ 4 ]

4 голосов
/ 08 ноября 2011

Другое решение - использовать шаблон стратегии и сделать что-то вроде следующего.Мы используем этот подход, чтобы выполнить предварительную проверку и иногда даже вычислять (на основе других полей) и устанавливать некоторые поля объекта данных, которые должны быть сохранены (например, у нас есть флаг «завершить», который обновляется на основе другихполя всякий раз, когда сохраняются или обновляются один из наших объектов).

Ваша стратегия:

interface SaveStrategy<T> {
  boolean beforeSave(T data);
  void afterSave(T data);
}

class SomeFancyProjectSaveStrategy implements SaveStrategy<Project> {

  public SomeFancyProjectSaveStrategy( /*parameters*/) {
  } 

  public boolean beforeSave(Project data) {
     //whatever you like
  }

  public void afterSave(Project data) {
     //whatever you like
  }
}

Ваша служба данных:

class DataService {
  public <T> void save(T data, SaveStrategy<? super T> strategy ){
    if(strategy.beforeSave(data)){
        // persist the data
        strategy.afterSave(data);
    }
  }
}

Затем используйте их следующим образом:

SaveStrategy<Project> saveStrategy = new SomeFancyProjectSaveStrategy(someParameters); //could reuse that
dataService.save( project, saveStrategy); //the service might even be shared for different data objects

Плюсы:

  • Действия до и после сохранения отделены от сохраняющихся
  • Вы можете повторно использовать стратегии, если они содержат повторно используемые данные (например, проверку)правила, но без состояния).
  • Вы можете использовать общий DataService

Минусы

  • Если вам нужна специальная логика сохранения, возможно, вам придется поддерживать как минимумдва класса: стратегия и служба специальных данных
1 голос
/ 08 ноября 2011

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

Простой интерфейс CRUD должен сделать:

public interface GenericRepository<K extends Serializable, T> {
    Collection<T> find();
    T find(K id);
    K save(T value);
    void update(T value);
    void delete(T value);
}

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

Транзакции принадлежат уровню обслуживания.Уровень постоянства не имеет возможности узнать, просят ли его принять участие в транзакции.Что, если есть и другие?

Обе ваши идеи слишком сложны.Я бы отверг оба.

1 голос
/ 08 ноября 2011

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

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

Вы можете получить лучшее из обоих миров.

bool beforeSave(Project objectAboutToBeSaved);
void saveProject(Project prj, Object... args /*other save options*/ );
bool afterSave(Project savedObject);

Это можно использовать в двух режимах, подобных этому.

void saveProject(Project prj, Object... args /*other save options*/ ) {
    boolean inBatch = inBatch(prj);
    if (!inBatch) beforeSave(prj);

    saveProject0(prj, args);

    if (!inBatch) afterSave(prj);
}

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

1 голос
/ 08 ноября 2011

Я бы предпочел второй.

Основное отличие состоит в том, что он сообщает пользователю класса, как его следует использовать.Это большой плюс (более четкий) и маленький недостаток (меньшая гибкость).Также второй метод учитывает лучшее расширение подклассов (подкласс ProjectService может повторно использовать метод before() и расширять after()).Следует иметь в виду, что подкласс может на самом деле отказаться от одного из методов (переопределив его и не вызывая его в суперклассе).Обязательно документируйте для каждого подкласса, если это разрешено или нет.

...