I am in the process of understanding the Visitor design pattern. My understanding is that somewhere this pattern creates a monolithic class with various functions.
If even 1 of the functions changes, then either a new class has to be created (just to change a function and copying the rest of the functions as it is) or one can edit the current function thereby violating open/close principle.
Let me share an example.
below is one of the classes that implement the above interface
Now suppose Necessity's implementation changes, either one has to create another implementation, copying the rest of the code, to just change Necessity or one has to manually change the visit method (used by Necessity), thereby violating open/close principle.
Is this violating the interface segregation principle? Can we improve or change anything in this? Thanks in advance.
1. True, for every new type of taxable item you need to visit, you'll need to add a new overloaded version of the visit() method. That's the disadvantage of the Visitor pattern.
2. Re "this patterns creates a monolithic class with various functions" -- first of all, the pattern doesn't create anything. Maybe you meant "... this pattern results in a monolithic class with potentially many overloaded versions of the visit() method" -- this is more accurate and is in line with #1 above.
3. The "visit" method doesn't always need to be literally named visit(). In this case, I would probably name it "getTaxFor()" or something that more clearly expresses what it's doing in the language of the domain. The name "visit()" as documented in the pattern is just a generalization.
4. Your implementation has smells.
4a. This is smelly: return Double.parseDouble(df.format(. . .)); -- why do this here? Why even do it at all?
4b. The TaxVisitor is also a little smelly to me. Seems like this is more appropriately called something like TaxCalculator. If you really want to retain the "Visitor" part of the name (which like the "visit" name is really not a requirement), I'd name it TaxableItemVisitor instead but that's still not a name that clearly expresses what the visitor does.
5. Your problem does not appear to be something where the Visitor pattern would be appropriately applied.
You should understand the context in which a pattern is meant to be applied. If you apply a pattern in a context that is inconsistent with the intended purpose, you run the risk of turning it into an anti-pattern instead.
Many distinct and unrelated operations need to be performed on node objects in a heterogeneous aggregate structure. You want to avoid "polluting" the node classes with these operations. And, you don't want to have to query the type of each node and cast the pointer to the correct type before performing the desired operation.
The best ideas are the crazy ones. If you have a crazy idea and it works, it's really valuable.—Kent Beck