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

Posted

技术标签:

【中文标题】哪种设计模式更适合保存/删除数据,为啥?【英文标题】: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 代码),但在我看来,这个问题可能是一个非常普遍的问题。

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

【问题讨论】:

【参考方案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);

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

【讨论】:

以上是关于哪种设计模式更适合保存/删除数据,为啥?的主要内容,如果未能解决你的问题,请参考以下文章

为啥如果我得到计算对象中的对象属性未定义而不是对象本身?哪种方法更适合这种情况?

哪种方法更适合此目的 commandLink 或 outputLink 或 Link [重复]

哪种数据类型最适合将图像保存在数据库中?

哪种设计模式适合从多个来源获取相同类型的数据,将它们组合成一个单元并应用多个过滤器

目前是不是就哪种 api 设计适合 react/redux 应用程序达成共识?

在这种情况下哪种数据库设计更有效?