Doorgaan naar hoofdcontent

And along comes a visitor

Every time I look at a visitor pattern, I wonder, why.
What is the use of this thing?
Especially, when I consider modularity, I just don't understand.
Let's say I have different objects:
Door
Engine
Wheel
Blinker
Trunk
Chair
(all of them are CarParts, of course)

When there's a cross-cutting concern which needs to be handled, the discussion arises: do you modify all the objects (say, they need a getCustomerValue, getBuildValue, getProductionTime, getSupplier, getRequiredComponents, for example, since we're using these parts in a factory and we need those), or maybe do you want all Supplier code to be in the same spot (since else knowledge of all suppliers gets scattered around, and you'd want it in a single spot)

For the last, the code would get pretty messy.
it would look something like
Suppliers.getSupplierOf(Carpart part) {
if(part instanceOf Door) {
  return "supplierOne";
}
if( part instanceOf Engine) {
   if(((Engine) part)).getCar().getModel() == "mark2")
  return "supplierTwo";
   else { ... }
}
... etc.
}
That has quite a lot of if statements (since it's a matcher-like code). It's a bit of a drag to read since you don't know the order, and determining the code for, say the Trunk is not immediately obvious.
Wouldn't it be nice if we were able to have a 

String supplier = "not found";

visit(Engine engine) {
  if(engine.getCar().getModel() == "markt2") {
    supplier = "supplierTwo";
  } else { ... }
}

The Visitor pattern allows that, but it has some drawbacks.
The visitor is a double dispatch pattern, in which you define a visitor to actually have all those methods, and the implementing classes can then do a callback on them.
This would lead to code to something like this:

Carpart part = ...
SupplierVisitor supplier = new SupplierVisitor();
part.accept(supplier);
String result = supplier.getName();

With this construct, the Carpart has no knowledge of the entire supply chain, and the supplychain knowledge is kept in one location, namely, the supplierVisitor.

Disadvantage:

There is a cost to this, however. It requires the visitor to have complete knowledge of all the carparts.
This means that it is impossible to add carparts to our model without having to modify the visitor.
And thus, this might break the desired modularity of the application: The Visitor knows the domain model, and the domain model knows the visitor.

Advantage

If these are mere interfaces, it might be a fine solution. It then allows a piece of code to work on all the carparts, without the carparts having (actual) knowledge of the differing concern.

Strategy pattern

A different approach is the strategy + repository pattern.
This works when there may be carparts added at compile time and there is no code which knows about all car parts.
For example, a module 'taillight' is added.
When using a visitor, the visitor now fails since it gets unknown objects. 
But now assume that there is a SupplierStrategy, and the taillight supplies a TaillightSupplierStrategy

Our code would then be 
Carpart part = ...
SupplierStrategy strategy = supplierStrategyRepository.find(part);
String result = strategy.getSupplier();  

The disadvantage of this is that there is no guarantee that our repository contains a strategy for each and every part!
For example, what happens when we forget to write the TaillightSupplierStrategy (or add it to the repository?)
Then our find action will fail at runtime. So we get runtime errors. When using a visitor, it will fail at compile time in a similar way.... I think?


Also:
https://dzone.com/articles/visitor-pattern-re-visited

Reacties

Populaire posts van deze blog

Spring's conditional annotation with properties

Spring has a nice @Conditional annotation, to have the option to have beans be available in the context depending a specific condition (Of course, this can also be realized by using @Configuration objects, but that's a different post). Ideally, we'd have the option to have a condition evaluate to true or false depending on a property. Sadly, Spring does not support that out of the box. Googling and looking around gives a partial solution, but the complete one here, so we won't forget: /** * Components annotated with ConditionalOnProperty will be registered in the spring context depending on the value of a * property defined in the propertiesBeanName properties Bean. */ @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Conditional(OnPropertyCondition.class) public @interface ConditionalOnProperty { /** * The name of the property. If not found, it will evaluate to false. */ String value(); /** * if the p...

OSGI insights without sonar

So I was on a project without sonar. Oh my. Well, it was an OSGI project, so the problems couldn't be that bad, right? But how good were they (and what things were bad?) I found Stan4j , a code analysis tool for eclipse, which draws nice graphs and can handle osgi pretty well it seems. Now I can see that dependencies/bundle names aren't properly aligned (even though OSGI doesn't complain), etc.

JPA and transactions

So I was working with JPA and transactions. Consider the following: In bean 1, with implicit TX 1, managed entities are loaded/created,and returned in bean 2, with implicit TX 2, entities are modified in bean 3, with NO TX, bean 1 is called, and the results are passed to bean 2. and bean 4 is similar to bean 3, but with it's own transaction, TX3 What happens when bean 3 finishes: are the entities updated? What happens when bean 4 finishes, are the entities updated? The answer to this is simple; entities are managed through a persistance context. That context is tied to the transaction. So in bean 2, there is a difference. When called from bean 3, it runs in a different transaction then bean 1, and thus a different persistance context, and thus the entities are not managed 'by this transaction'.When called from bean 4, it all runs in the same transaction, TX3, and the results are persisted.