I'm using visitor pattern to define a set of operations on some classes.
Some operations are commutative, so I end up with duplication in the visitor pattern code.
Let's say I have classes A B C, and the operations: A*A, A*B, A*C, B*A, B*B, B*C, C*A, C*B, C*C.
A*A, B*B, C*C are unique.
A*B, B*A and friends will have code duplication
I could implement A*B, and make B*A call A*B but I will end up asking myself: in which file did I implement the operation between A and B again, in A or in B? (there will be about 6 classes, so I will ask this question a lot. 15 pairs of possible operations)
There is a risk of someone in the future making an infinite loop of A*B calling B*A calling A*B when implementing a new operation.
It's unnatural to have a convention that decides which should be implemented A*B or B*A.
I could make a 3rd file with all the implemented functions which are called by either A*B and B*A, doesn't seem very object oriented.
How would you solve this issue?
Thanks
(I could list some code, but it's long and doesn't illustrate the point easily)

2 Answers
2

You are right, you should definitely refrain from implementing A*B as a call of B*A. In addition to a potential of creating an infinite chain of calls, that approach does not reflect the symmetry of the operation in your code, because the code is not symmetric.

A better approach is to implement a "symmetric" operation in a helper class or as a top-level function, depending on what is supported in your language, and then make both A*B and B*A call that helper implementation.

there is a guideline that it's not exactly ok for class to use the attributes of other classes. I guess there isn't much of a choice
–
titusAug 18 '12 at 15:35

Scott Meyer has an excellent discussion on the topic in his More Effective C++ book (Item 31: Making functions virtual with respect to more than one object.) His example is making a collision function for a game where various objects may collide in space. His example is symmetric too (it does not matter if an asteroid hits a spaceship or a spaceship hits an asteroid, the explosion is the same). He starts with a visitor pattern, and gradually works out a C++ - specific solution that relies on RTTI.
–
dasblinkenlightAug 18 '12 at 15:40

@titus I am pretty sure that it is (I read that book in 1997, and the item was there).
–
dasblinkenlightAug 18 '12 at 16:31

I tried to implement with RTTI, I used typeid().name(), it was very slow, then I tried making a static function for each subclass that returns an int, this is one of the indexes in a function pointer matrix . It's still 3x slower than the visitor pattern. Here is the repository github.com/titusnicolae/comparison/blob/master/rtti.cpp
–
titusAug 23 '12 at 15:45

hmm, I could implement stuff as Object* result = new MultiplyHelper().addOperand("A").addOperand("B").compute();
–
titusAug 18 '12 at 15:54

I also have some non-commutative functions such A/B, using a symmetric construct as the above one for a non-symmetric operation is not ok. Better the other way around.
–
titusAug 18 '12 at 16:00

Parameter builder is just for taking parameters what you do with them is up to you. What your doing is also good. I suggested this solution to keep your methods intact and only change method signature.
–
Amit DeshpandeAug 18 '12 at 16:02