These isn't an answer, but is too long for a comment.
At first sight, it looks like you've run into the multiple dispatch problem.
In non-object-oriented languages, you have functions and procedures, each of which can have multiple parameters. There is no "this object" because there are no objects.
In OOP as implemented in C++, Java etc, classes have methods. Excluding static methods, when you call a method, you specify the object that you're calling it for. The object is really a parameter, like the others - writing it before the method name and dot instead of within parentheses doesn't change that. However, it's specified in a special way because it has special properties...
It's often implicit within the method. You don't include a
this parameter in the method signature, and you don't have to use
this (or any other name) to reference the object.
This one special parameter is used for "dispatch" - to decide which implementation of the method to use in late-binding calls.
This approach is called "single dispatch" because there is one special parameter which is used for the dispatch decision. There is also something called "multiple dispatch" where the dispatch decision can be based on any/all parameters.
EDIT - sorry, I wrote "multiple inheritance" below earlier, that's a different thing.
With multiple dispatch, you don't need to nominate one particular parameter to be special - at least not for dispatch. All parameters are "special" in that way, therefore none of them are special. You might still want a special "this object" for reasons of abstraction, but you don't need it just to support calling.
Multiple dispatch is rare to non-existent in OOP, but it exists in a few languages - I believe it's in Common Lisp, though I don't know for sure. The way I know about it is more for domain-specific languages such as treecc - actually I've written a tool based on treecc, but designed for working with C++.
Without multiple dispatch as a language feature, you have two issues...
- Which parameter to make into the special "this object" parameter for a method.
- How to implement multiple dispatch as a design pattern where needed.
The first issue is all about abstractions, and doesn't always have a good answer. In C++, it's commonly pointed out that sometimes using non-member functions can even improve encapsulation. Maybe your method shouldn't be a method at all?
I'd add that just because you're defining a function separate from one class doesn't mean you can't put it in another class - you could define a third class with "tools" that manipulate objects of the other two class types. There's a common claim in OOP that "the shape draws itself", due to the teaching example, but it's wrong to infer that a class must always encapsulate the data it works with. It encapsulates the implementation of its own abstraction, but the nail is a separate object - not a part of the hammer implementation. Some kinds of objects are designed to work together - to interact via a shared interface - and are therefore somewhat coupled. That's not a goal to seek, but it's an unavoidable fact of life, and you get better encapsulation by having hammer and nail as separate classes than by having one combined hammer-and-nails class.
The other issue is how to handle a multiple-dispatch decision. There are lots of options because it is, after all, just a run-time decision you're making - the compiler didn't handle it for you so you have to code it yourself. However, for maintainability, a particularly good solution is "redispatch" - using a sequence of single-dispatch decisions to implement multiple dispatch. Each method calls another method (with a different parameter taken as special) until the dispatch is fully resolved. I don't have a good link for this. I got the term from the Ada95 reference manual, but Ada wrongly claims to support multiple dispatch just because redispatch is possible (a design pattern is not a built-in language feature).
This is a heavy-weight pattern - there's a fair amount of code to write. But it normally gives good error messages. Not perfect - it can fail to see a problem if you have non-abstract base classes - but it's still a lot better than e.g. a
One place that redispatch is used is in the visitor pattern. Some people say that the visitor pattern is only needed because OOP languages lack multiple dispatch. I'm not entirely convinced. Certainly, a big part of the visitor is doing redispatch to solve the double-dispatch (two special parameters) case of multiple dispatch, but there's also an abstraction with clear roles - visitor and visitee - and other details that are separate from multiple dispatch.