【问题标题】:Modeling circular dependencies while maintaining data integrity在保持数据完整性的同时对循环依赖进行建模
【发布时间】:2021-10-11 14:22:57
【问题描述】:

我正在设计一个音乐信息系统。我有几个相互连接的实体。
以下是部分域代码。

class Album {
    
    private Set<Track> tracks;
    private boolean published;
    
    public Set<Track> getTracks() {
        return this.tracks;
    }
    public boolean isPublished() {
        return this.published;
    }

    public void publish() {
        System.out.println("Album.publish() called");
        this.published = true;
        this.tracks.forEach(track -> track.publish());
    }

}

class Track {

    private boolean published;
    private Album album;

    public boolean isPublished() {
        return this.published;
    }
    public Album getAlbum() {
        return this.album;
    }

    public void publish() {
        // if track is single (this.album == null), set published to true
        // if track is part of an album and the album is NOT published, return;
        // if track is part of an album and the album is published, set published to true
        if(this.album != null && !this.album.isPublished())
            return;
        this.published = true;
    }

}

Track 是一个独立的实体。它可以是单首曲目(即没有专辑)。所以实际上需要album 属性。
一个域规则是,当专辑存档(即未发布)时,其曲目也不能发布,如果专辑已发布,则其任何曲目都可以发布或存档。
问题是当专辑发布时(例如album1.publish()),其曲目的publish()方法也会被调用。但是track1.publish() 根据已有的副本(未发布)检查专辑是否已发布。
我该如何解决这个问题?

【问题讨论】:

  • 如果Album 是一个实体,则不应有“副本”——实体具有唯一标识,所有引用都应指向同一个对象。
  • 正确。我应该如何在 Java 中实现它?据我所知,Java 总是按值传递。 @埃里克森
  • stackoverflow.com/questions/40480/… 很好地了解 pass-by-value 与 pass-by-ref
  • 是的,将对父 Album 的引用传递给 Track(按值,因为所有引用都在 Java 中传递)。强制数据完整性的一种方法是删除直接为Track 设置Album 的任何API,而是仅在Album 上提供addTrack(Track) 方法,该方法更新轨道集合设置Album 通过包私有访问。

标签: java oop design-patterns pass-by-reference pass-by-value


【解决方案1】:

如果您按行为拆分域模型实体,则可以摆脱所描述的限制

让我们为这些实体创建一些接口:

interface AlbumId{
   String asString();
   AlbumId Absent = () -> "NO ALBUM AT ALL";
}

interface Publication{
   void publish() throws Exception;
   void archive() throws Exception;
   boolean published();
}

interface Track{
    TrackId id(); 
    AlbumId albumId(); //smart type (as DDD suggest), therefore, no more nulls
}

现在您可以通过创建类来执行规则,该类将为您提供可以发布的曲目列表:

public class TracksReadyToPublishOf implements Supplier<Map<TrackId, TrackPublication>>{ 
    //this class may access to cache and have dosens of other optimizations
    public TracksReadyToPublishOf(AlbumId id){...}
    @Override public get(){...} 
}

然后您可以在任何地方重复使用您的代码来检查您的规则:

public class TrackPublication implements Publication {
    private final Track track;
    private final Supplier<Map<TrackId, TrackPublication>> allowedTracks;

    //easy for unit testing
    public SmartTrackPublication(Track track, Supplier<Map<TrackId, TrackPublication>> allowedTracks){
        this.track = track;
        this.allowedTracks = allowedTracks;
    }

    public SmartTrackPublication(Track track){
        this(track, new TracksReadyToPublishOf(track.albumId());
    }

    @Override
    public publish() throws AlbumArchivedException{
        if(this.albumId != AlbumId.Absent){
            if(!this.allowedTracks.get().containsKey(this.track.id())){
                throw new AlbumArchivedException();
            }
        }
        this.allowedTracks.get().get(this.id()).publish();
    }

}

对于专辑发布:

public class AlbumPublication implements Publication{

   private final AlbumId id;
   private final Producer<Map<TrackId, TrackPublication>> tracks
   
   private AlbumWithTracks(AlbumId id, Producer<Map<TrackId, TrackPublication>> tracks){
       this.id = id;
       this.tracks = tracks;
   }

   public AlbumWithTracks(AlbumId id){
       this(id, new TracksReadyToPublishOf(id))
   }
   ...
   @Override publish() throws Exception{
      //code for publishing album
         
      for(TrackPublication t : Arrays.asList(
          this.tracks.get() 
      )){
          t.publish(); //track can publish anyway if it presents in list above
      }
   }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-12-01
    • 2018-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多