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

Java 序列化复合-(设计模式)的最佳方法,java,serialization,design-patterns,composite,Java,Serialization,Design Patterns,Composite,我有以下实现复合设计模式的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;

我有以下实现复合设计模式的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);
  }
}
现在,如果我使用访问者模式进行序列化,我会得到如下结果:

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

                                                  
我猜这里更喜欢visitor,因为当我们添加新的
组件时,我们还需要实现
对象接受(ComponentVisitor)
方法,所以我们不能忘记我们需要一个用于序列化这个新组件的代码。如果我们在使用
instanceof
时也这样做,我们可能会忘记将其添加到该检查中


现在-我的问题是-我们有没有办法摆脱
对象accept(ComponentVisitor)
方法签名中丑陋的
对象
返回类型?我想到的唯一其他选择是使用一些标记接口(例如,
接口序列化组件{}
)然后让所有序列化程序类实现这个空接口,就像这样
类wholecompositerialized实现SerializedComponent
,但它似乎仍然不正确。

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

e、 g

公共类主{
公共静态void main(字符串[]args){
ComponentVisitor序列化程序=新的ComponentSerializer();
组件A=新组件A();
SerializedComponent serializedA=componentA.accept(序列化程序);
System.out.println(序列化DA);
组件=新组件B();
SerializedComponent serializedB=component.accept(序列化程序);
System.out.println(序列化数据库);
}
静态接口组件{
公共V接受(组件访客);
}
静态类ComponentA实现组件{
公共V接受(组件访问者){
回访者。参观(本);
}
}
静态类ComponentB实现组件{
公共V接受(组件访问者){
回访者。参观(本);
}
}
静态接口序列化组件{}
静态类SerializedComponent实现SerializedComponent{
}
静态类SerializedComponentB实现SerializedComponent{
}
静态接口组件访问者{
公众V访问(组件A组件);
公众V访问(组件B组件);
}
静态类ComponentSerializer实现ComponentVisitor{
公共序列化组件访问(组件A组件){
返回新的SerializedComponentA();
}
公共序列化组件访问(组件B组件){
返回新的SerializedComponentB();
}
}
}

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

这里的解决方案是将所有特定于
ComponentA
(或您可能将其转换为的任何特定于A的类型)的逻辑移动到
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);
}

我在这里看到的问题是,visit必须返回相同的
SerializedComponent
对象。当我想将
组件a
序列化为
序列化组件a
组件b
序列化为
序列化组件b
时会发生什么情况?这很好,因为Composite实现了
V accept()
而不是
Object accept()
,但我仍然需要创建
ComponentVisitor
?因此肯定比在Composite中包含
Object
要好,但是有可能完全摆脱
对象
/Marker接口吗?@MarkoKraljevic不知道你第一句话的意思。示例代码已返回SerializedA和SerializedDB。它返回
SerializedComponent
。因此,如果我想将它转发到方法
printSerializedComponent(SerializedComponent a)
@MarkoKraljevic,我需要手动强制转换该类型。您必须返回一个基类型,否则调用方必须事先知道它处理的对象的类型。您仍然可以使用
SerializedComponentVisitor
打印这些内容,而不是强制转换。序列化组件的模式相同。以这种方式使用时有什么问题?对我来说,这似乎是一个不错的解决方案->我只是将SerializationMarkerInterface返回类型重写为将由代码生成的具体序列化类型。Visitor是一种为本机不支持它的语言实现模式匹配的方法。@plalx模式匹配将在下一个Java版本中出现,但它仍然会在
instanceof
和隐式cast下面使用-我在某个地方读到,根据Liskov替换原理,使用它是不好的。。。现在我不知道缺点是什么,但请记住阅读…@jaco0646你能在线修改我的示例吗gdb.com/H1gxCWsEwv,因为我不明白你的意思吗?@plalx,是的:模式匹配是一种功能设计模式,访问者可以被描述为从OO到FP的桥梁。虽然封装对OO设计至关重要,但FP对此却更加矛盾。我可以说,“如果你不想封装OO,那么一个不同的范例可能更合适,比如FP。”我认为这些模式代表了它们各自的范例。
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();
        }
     }
}
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);
}