I have following java code that is implementation of Composite Design pattern:
//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);
}
}
There are 2 possible composite roots here (CompositeA
and CompositeB
) and one leaf component (Leaf
).
Here I define DTOs that will hold serialized data:
class WholeCompositeASerialized {
String content;
List<Object> serializedChildren;
}
class WholeCompositeBSerialized{
String content;
List<Object> serializedChildren;
}
class WholeLeafSerialized{
String content;
}
Now if I use visitor pattern for serialization, I get something like 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);
}
and if I use instanceof
this is the code that does the same thing:
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;
}
}
I guess also that visitor is preferred here because when we add new Component
, we are required to implement Object accept(ComponentVisitor visitor)
method also - so we cannot forget that we need a code for serialization of this new component. If we do the same when we use instanceof
we would possibly forget to add it to that check.
Now - my question is - is there any way that we can get rid of that ugly Object
return type in Object accept(ComponentVisitor visitor)
method signature? The only other option that comes to my mind is to use some marker interface (eg. interface SerializedComponent {}
) and then have all serializer classes implement that empty interface like this class WholeCompositeASerialized implements SerializedComponent
but it still does not seem right.
Aucun commentaire:
Enregistrer un commentaire