序列化复合材料的最佳方式 - (设计模式)

Best way to serialize composite - (design pattern)

我有以下 java 复合设计模式实现代码:

//composite designed for type safety (all Leaf-only operations only in leaf)
interface Component extends Visitable {
  void enable();
  void disable();
}

class CompositeA implements Component {
  private String compositeData;
  private boolean enabled;
  private Set<Component> components = new HashSet<>();

  CompositeA(String compositeData) {
    this.compositeData = compositeData;
  }

  void addChild(Component component){
    this.components.add(component);
  }

  String getCompositeData() {
    return compositeData;
  }

  Set<Component> getComponents() {
    return components;
  }

  @Override
  public void enable() {
    this.enabled = true;
  }

  @Override
  public void disable() {
    this.enabled = false;
  }

  @Override
  public Object accept(ComponentVisitor visitor) {
    return visitor.visit(this);
  }
}

class CompositeB implements Component{
  private int compositeData;
  private boolean enabled;
  private Set<Component> components = new HashSet<>();

  CompositeB(int compositeData) {
    this.compositeData = compositeData;
  }

  void addChild(Component component){
    this.components.add(component);
  }

  int getCompositeData() {
    return compositeData;
  }

  Set<Component> getComponents() {
    return components;
  }

  @Override
  public void enable() {
    this.enabled = true;
  }

  @Override
  public void disable() {
    this.enabled = false;
  }

  @Override
  public Object accept(ComponentVisitor visitor) {
    return visitor.visit(this);
  }
}

class Leaf implements Component {
  private boolean enabled;
  private String[] leafData;

  Leaf(String[] leafData) {
    this.leafData = leafData;
  }

  String[] getLeafData() {
    return leafData;
  }

  @Override
  public void enable() {
    this.enabled = true;
  }

  @Override
  public void disable() {
    this.enabled = false;
  }

  @Override
  public Object accept(ComponentVisitor visitor) {
    return visitor.visit(this);
  }
}

这里有2个可能的复合根(CompositeACompositeB)和一个叶子组件(Leaf)。

这里我定义了将保存序列化数据的 DTO:

class WholeCompositeASerialized {                     
  String content;                                     
  List<Object> serializedChildren;                    
}                                                     
                                                      
class WholeCompositeBSerialized{                      
  String content;                                     
  List<Object> serializedChildren;                    
}                                                     
                                                      
class WholeLeafSerialized{                            
  String content;                                     
}                                                     

现在如果我使用访问者模式进行序列化,我会得到这样的结果:

interface ComponentVisitor {
  WholeCompositeASerialized visit(CompositeA compositeA);
  WholeCompositeBSerialized visit(CompositeB compositeB);
  WholeLeafSerialized visit(Leaf leaf);
}

class SerializableComponentVisitor implements ComponentVisitor{
  @Override
  public WholeCompositeASerialized visit(CompositeA compositeA) {
    WholeCompositeASerialized wcas = new WholeCompositeASerialized();
    wcas.serializedChildren = compositeA
        .getComponents()
        .stream()
        .map(c -> c.accept(this))
        .collect(Collectors.toList());
    wcas.content = compositeA.getCompositeData();
    return wcas;
  }

  @Override
  public WholeCompositeBSerialized visit(CompositeB compositeB) {
    WholeCompositeBSerialized wcbs = new WholeCompositeBSerialized();
    wcbs.serializedChildren = compositeB
        .getComponents()
        .stream()
        .map(c -> c.accept(this))
        .collect(Collectors.toList());
    wcbs.content = String.valueOf(compositeB.getCompositeData());
    return wcbs;
  }

  @Override
  public WholeLeafSerialized visit(Leaf leaf) {
    WholeLeafSerialized wls = new WholeLeafSerialized();
    wls.content = Arrays.toString(leaf.getLeafData());
    return wls;
  }
}

interface Visitable{
  Object accept(ComponentVisitor visitor);
}

如果我使用 instanceof 这是执行相同操作的代码:

class SerializerUsingInstanceOf {
  Object decide(Component component){
    if(component instanceof CompositeA){
      return serialize((CompositeA)component);
    }
    else if(component instanceof CompositeB){
      return serialize((CompositeB)component);
    }
    else{
      return serialize((Leaf)component);
    }
  }

  WholeCompositeASerialized serialize(CompositeA compositeA) {
    WholeCompositeASerialized wcas = new WholeCompositeASerialized();
    wcas.serializedChildren = compositeA
        .getComponents()
        .stream()
        .map(this::decide)
        .collect(Collectors.toList());
    wcas.content = compositeA.getCompositeData();
    return wcas;
  }

  WholeCompositeBSerialized serialize(CompositeB compositeB) {
    WholeCompositeBSerialized wcbs = new WholeCompositeBSerialized();
    wcbs.serializedChildren = compositeB
        .getComponents()
        .stream()
        .map(this::decide)
        .collect(Collectors.toList());
    wcbs.content = String.valueOf(compositeB.getCompositeData());
    return wcbs;
  }

  WholeLeafSerialized serialize(Leaf leaf) {
    WholeLeafSerialized wls = new WholeLeafSerialized();
    wls.content = Arrays.toString(leaf.getLeafData());
    return wls;
  }
}

                                                  

我猜这里首选访问者,因为当我们添加新的 Component 时,我们还需要实现 Object accept(ComponentVisitor visitor) 方法 - 所以我们不能忘记我们需要一个代码来序列化这个新组件。如果我们在使用 instanceof 时也这样做,我们可能会忘记将其添加到该检查中。

现在 - 我的问题是 - 有什么方法可以摆脱丑陋的 Object return 输入 Object accept(ComponentVisitor visitor) 方法签名?我想到的唯一其他选择是使用一些标记接口(例如 interface SerializedComponent {}),然后让所有序列化程序 类 像这样 class WholeCompositeASerialized implements SerializedComponent 实现该空接口,但它仍然没有似乎是对的。

我认为正确的方法是在这里使用泛型。

例如https://onlinegdb.com/r1m5Eg4DP

public class Main {

     public static void main(String []args){
        ComponentVisitor<SerializedComponent> serializer = new ComponentSerializer();
        Component componentA = new ComponentA();
        SerializedComponent serializedA = componentA.accept(serializer);
        System.out.println(serializedA);
        
        Component component = new ComponentB();
        SerializedComponent serializedB = component.accept(serializer);
        System.out.println(serializedB);
     }
     
     static interface Component {
        public <V> V accept(ComponentVisitor<V> visitor);
     }
     
     static class ComponentA implements Component {
        public <V> V accept(ComponentVisitor<V> visitor) {
            return visitor.visit(this);
        }
     }
     
     static class ComponentB implements Component {
         public <V> V accept(ComponentVisitor<V> visitor) {
            return visitor.visit(this);
         }
     }
     
     static interface SerializedComponent {}
     
     static class SerializedComponentA implements SerializedComponent {
     }
     
     static class SerializedComponentB implements SerializedComponent {
     }
     
     static interface ComponentVisitor<V> {
        public V visit(ComponentA component);
        public V visit(ComponentB component);
     }
     
     static class ComponentSerializer implements ComponentVisitor<SerializedComponent> {
        public SerializedComponent visit(ComponentA component) {
            return new SerializedComponentA();
        }
        public SerializedComponent visit(ComponentB component) {
            return new SerializedComponentB();
        }
     }
}

您正在尝试 return 来自访问者的具体类型信息。这不是该模式的目的。访问者在内部封装(和处理)具体类型。

这里的解决方案是将 all 特定于 ComponentA 的逻辑(或您可能将其转换为的任何 A-specific 类型)移动到 visit(ComponentA) 方法,同样适用于 ComponentB.

如果不想要Visitor的类型封装,那么另一种设计更合适,比如模式匹配。


评论评论...

public static void main(String[] args) {
    // Using a concrete type here defeats the purpose of these patterns.
    // Instead, program to an interface:
    // Component c1 = new CompositeA("root");
    CompositeA c1 = new CompositeA("root");
    c1.addChild(new Leaf(new String[]{"leaf11", "leaf12"}));

    CompositeA c2 = new CompositeA("composite1");
    c2.addChild(new Leaf(new String[]{"leaf21", "leaf22"}));
    c1.addChild(c2);

    SerializableComponentVisitor scv = new SerializableComponentVisitor();
    // Clients never invoke visit methods directly,
    //  because they do not have the type information to make these calls.
    // A client would execute, c1.accept(scv)
    WholeCompositeASerialized wcas1 = scv.visit(c1);
}