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...

On SSL certificate generation

 So, more stuff I always forget... how to properly generate SSL certs. Well, easiest is with openssl (of course) Something like: openssl req -new -newkey rsa:2048 -nodes  -sha256 -subj "/C=NL/ST=Utrecht/L=Utrecht/O=Cooperatieve Rabobank U.A./OU=RASS Groep ICT/CN=my-common-name.host.nl" -keyout somename-prod.key -out somename-prod.csr   That can get you a certificate sign request (csr) and the appropriate key.   Of course, you want to then import those keys into a keystore. The trick to doing that is to convert it to a pkcs12 format where it can have the certificate and the key combined.   openssl pkcs12 -export -inkey somename-prod.key -in somename-prod.rabobank.nl.crt -out somename-prod.p12  Note that the crt is the signed certificate, acquired through getting the csr generated above approved..   This p12 file you can import using something like KeyStore Explorer.   After that, you can also append the root certificates of the original cert, to en...

Using spring's @transactional to only roll back when you really want to

In spring you can use the @Transactional annotation to marcate public methods as transactions. Any exception thrown in such an exception causes a rollback... Any exception? No, spring only does so on runtime exceptions. Checked exceptions are allowed and do not result in a rollback. And even then, you can allow some transactions to rollback, or not, with the proper properties for the transactions. For examle, @Transactional(rollbackFor="MyCheckedException.class") will rollback for a specific checked exception, and similarly, you can use @Transactional(noRollbackFor="MyUncheckedException.class") for unchecked exceptions. Of course, you might want to make it a little simpler on yourself. This article on stackoverflow shows us a different way: we can use our own transaction handler by overriding spring's. Let's have a sample interface which defines whether an exception should perform a rollback: interface ConfigurableRollback shouldRollbackOnE...