将服务逻辑与数据分离

2022-09-03 15:49:18

我一直在查看我在一个Android项目中的几个类,我意识到我一直在将逻辑与数据混合在一起。在意识到这对我的项目的可读性和测试能力有多糟糕之后,我决定做一些重构,以便将所有服务逻辑抽象出来,以分离服务模块。但是,由于我一直依赖Java的多态性,因此我迷失了方向,需要一些指导。

假设我有一个超级数据类的“待更改”布局,以及两个子类:

public class DataItem {
    /* some variables */ 

    public saveToDB(/* Some Arguments */) {
        /* do some stuff */
    }

    public render() {
        /* render the class */
    }
}

public class ChildDataItemA extends DataItem {
    @Override
    public saveToDB(/* Some Arguments */) {
        super.saveToDB(); 
        /* more specific logic to ChildDataItemA */
    }

    @Override
    public render() {
        /* render logic for ChildDataItemA */
    }
}

public class ChildDataItemB extends DataItem {
    @Override
    public saveToDB(/* Some Arguments */) {
        super.saveToDB(); 
        /* more specific logic to ChildDataItemB */
    }

    @Override
    public render() {
        /* render logic for ChildDataItemB */
    }
}

现在,我想过将 和 方法移动到服务类。但是,有时我需要能够在不知道其运行时类型的情况下将这些方法调用到编译类型的实例中。例如,我可能想进行以下调用:saveToDB()render()DataItem

List<DataItem> dataList; 
for (DataItem item: dataList) {
    item.saveToDB();
    item.render();
}

此外,我还想到了以下操作:

public class ChildDataItemB extends DataItem {
    @Override
    public saveToDB(/* Some Arguments */) {
        super.saveToDB(); 
        /* more specific logic to ChildDataItemB */
         Service.saveToDBB();
    }

    @Override
    public render() {
        /* render logic for ChildDataItemB */
        Service.renderB();
    }
}

我仍然在每个子类中保留“虚拟”方法,这些子类将调用适当的服务方法。但是,我不认为这真的实现了我想要的分离,因为数据类仍然会知道服务(不好!

关于如何解决这个问题的任何想法?

编辑:请注意,并且只是这些方法的通用示例,因此问题实际上并不是选择ORM或SQL相关技术。render()saveToDB()


答案 1

访客模式来救援。创建一个访问者接口,并让每个服务实现此接口:

public interface DataItemVisitor {
  // one method for each subtype you want to handle
  void process(ChildDataItemA item);
  void process(ChildDataItemB item);
}

public class PersistenceService implements DataItemVisitor { ... }
public class RenderService implements DataItemVisitor { ... }

然后让每个实现一个方法:DataItemaccept

public abstract class DataItem {
  public abstract void accept(DataItemVisitor visitor);
}

public class ChildDataItemA extends DataItem {
  @Override
  public void accept(DataItemVisitor visitor) {
    visitor.process(this);
  }
}

public class ChildDataItemB extends DataItem {
  @Override
  public void accept(DataItemVisitor visitor) {
    visitor.process(this);
  }
}

请注意,所有实现看起来都相同,但在每个子类中引用正确的类型。现在,您可以添加新服务,而无需更改类。acceptthisDataItem


答案 2

所以你想做:

List<DataItem> dataList; 
for (DataItem item: dataList) {
    service.saveToDB(item);
    service.render(item);
}

为此,您需要为服务设置一个系统,以便从 DataItem 子类中了解更多详细信息。

ORM和序列化程序通常通过元数据系统解决这个问题,例如,通过查找名称与子类匹配的xml文件,其中包含要保存或序列化的属性。

ChildDataItemA.xml
<metaData>
   <column name="..." property="..."/>
</metaData>

您可以通过反射和注释获得相同的结果。

在您的例子中,Bridge 模式的应用也可以工作:

class DataItem {
    public describeTo(MetaData metaData){
       ...
    }    
}

class Service {
   public void saveToDB(DataItem item) {
      MetaData metaData = new MetaData();
      item.describeTo(metaData);
      ...
   }
}

您的元数据可以与保存或渲染分离,因此两者都可以相同。


推荐