序列化组合的最佳方法-设计模式

问题描述

我有以下Java代码,可以实现Composite Design模式:

//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方法签名中难看的Object accept(ComponentVisitor visitor)返回类型?我想到的唯一其他选择是使用某些标记器接口(例如interface SerializedComponent {}),然后让所有序列化程序类都实现像class WholeCompositeASerialized implements SerializedComponent这样的空接口,但它似乎仍然不正确。 / p>

解决方法

我认为正确的方法可能是在此处使用泛型。

例如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();
        }
     }
}
,

您正在尝试从访问者返回具体类型信息。这不是该模式的目的。访客在内部封装(并处理)具体类型。

这里的解决方案是将ComponentA(或您可能将其转换为任何A的特定类型)的 all 逻辑移到visit(ComponentA)方法中,对于ComponentB

如果您不希望访问者进行类型封装,那么更适合使用其他设计,例如模式匹配。


对评论的评论...

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

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...