【问题标题】:Which design pattern is better for saving/deleting data and why?哪种设计模式更适合保存/删除数据,为什么?
【发布时间】:2011-11-08 10:12:58
【问题描述】:

我无法在以下两种模式之间做出决定,例如。保存一个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);

目前我们使用的是第一种模式,但我开始考虑改用第二种模式,因为:

(优点)

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

(缺点)

  • 设置起来有点困难 (initSave) - 甚至可能需要包含拆卸方法

我的想法来自CakePHP MVC 框架,其中 Model 和 Controller 都包含这样的回调方法,使用它们我可以真正实现一些清晰的业务逻辑。

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

注意:这个例子是为保存而给出的,但删除过程也是如此。

【问题讨论】:

    标签: java design-patterns save


    【解决方案1】:

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

    你的策略:

    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
    

    优点:

    • 保存前和保存后操作与持久性分开
    • 您可以重复使用策略,只要它们包含可重复使用的数据(如验证规则但没有状态)。
    • 您可以使用通用数据服务

    缺点

    • 如果您需要特殊的保存逻辑,您可能必须至少维护两个类:策略和特殊数据服务

    【讨论】:

      【解决方案2】:

      我都不会说。在我看来,两者都不必要地复杂。

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

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

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

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

      【讨论】:

      • 在我的代码中,我使用存储库来保存/删除数据。第一个示例将在saveProject(...) 上具有@Transactional 注释,而在save(...)ProjectService 上的第二个示例必须在after-beforeDelete 方法中包含自定义业务逻辑。在这种情况下,您还有上述分歧吗?
      • 是的,我愿意。在我看来,你的存储库不应该知道交易。服务了解工作单元。
      • 同意,但是帖子中描述的类在服务层中,它们使用存储库(在保存/删除内部)-因此名称为 ProjectService(处理与项目数据对象相关的操作)
      • 存储库和服务是分开的。您说服务使用存储库;我同意。听起来你仍然很困惑。
      • 我的存储库没有实现任何特殊的东西,只是简单的 CRUD(它们通过 Spring Data Graph 框架通过方面自动填充方法)。我为每种类型的数据bean 都有一个存储库。我想要的只是创建一个有点统一的服务层,问题是指这个问题。此处保存(来自服务)用于创建/更新对象并处理相关的预处理和后处理任务。无论如何,感谢您的帮助 (+1)
      【解决方案3】:

      我更喜欢第二个。

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

      【讨论】:

        【解决方案4】:

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

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

        你可以两全其美。

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

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

        【讨论】:

          猜你喜欢
          • 2012-10-29
          • 2011-02-27
          • 1970-01-01
          • 2023-03-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多