哪种设计模式更适合保存/删除数据?为什么?

时间:2011-11-08 10:12:58

标签: java design-patterns save

我无法决定以下两种模式。保存dataObject(在我的情况下是bean)。这两个选项是:

第一

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*/);

第二

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)

  • 更好的逻辑分离
  • 跨多个对象类型的统一方法命名(允许创建通用单元测试:例如,初始化每个对象及其服务并调用save)

(CONS)

  • 有点难以设置(initSave) - 甚至可能必须包含拆解方法

这个想法来自CakePHP MVC框架,其中模型和控制器都包含这样的回调方法,使用它我可以真正实现一些明确的业务逻辑。

现在我正在使用Java开发 - Spring + Spring Data Graph - (因此是javaish代码),但在我看来,这个问题可能是一个非常通用的问题。

注意:示例是为了保存,但删除过程也是如此。

4 个答案:

答案 0 :(得分:4)

另一种解决方案是使用策略模式并执行以下操作。 我们使用该方法进行预先持久化验证,有时甚至计算(基于其他字段)并设置要保留的数据对象的一些字段(例如,我们有一个基于其他更新的“完整”标志每当持久化或更新我们的实体时都会出现字段。)

您的策略:

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 :(得分:1)

我更喜欢第二个。

主要区别在于它告诉用户该类应该如何使用。这是一个很大的好处(更明确)和一个小的下行(更少的灵活性)。第二种方法也允许更好地扩展子类(ProjectService的子类可以重用before()方法并扩展after())。要记住的一件事是,子类实际上可以丢弃其中一个方法(通过覆盖它而不是在超类中调用它)。如果允许,请务必记录每个子类。

答案 2 :(得分:1)

第一个例子更简单。如果您需要批量处理数据,第二个示例会更好。即,开始/结束更新的开销很重要。

您仍然可以使用线程本地状态使第二个示例线程安全。

你可以充分利用这两个世界。

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);
}

这允许您混合和匹配并使用一些执行单个更新的方法,但是从某些方法调用时将隐式批处理已保存的数据。

答案 3 :(得分:1)

我不会说。对我来说,两者看起来都不太复杂。

一个简单的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);
}

在进入持久层之前,所有的验证和检查都应该完成。在我看来,它违反了单一责任原则。

交易属于服务层。持久层无法知道是否要求它参与事务。如果还有其他人怎么办?

你的想法都过于复杂。我拒绝这两个。