The double dispatch can be confusing and make the code harder to understand.
While it is true more indirection can create confusion, the naive implementation (a service relying on instanceof to determine the correct algorithm to use) is very rigid (creates static dependencies between components) and doesn't scale at all (imagine handling hundreds of components this way, good luck 🙄)
The visitor pattern benefits clearly outweights the added complexity and create much cleaner and understandable code IMHO.
The accept() and visit...() methods usually don't return anything, so you need to keep records on the visitor itself.
I don't see why 🤷
All Visitors need every method on the VisitorInterface while it might not have an implementation for it.
You can mitigate that problem by applying the Interface Segregation Sprinciple (ISP) ("no client should be forced to depend on methods it does not use.")
Pro tip: using traits helps keep things clean & reusable.
Using the solution below, we see the Book class now only rely on the BookPageCountVisitorInterface and not the whole visitor. The visitor concrete class doesn't have to implement visitDocument if it's not needed or possible 👍
TL;DR makin small interfaces & traits gives you the flexibility to only implement what you need 😎
interfaceBookPageCountVisitorInterface{publicfunctionvisitBook(Book$book):int;}interfaceDocumentPageCountVisitorInterface{publicfunctionvisitDocument(Document$doc):int;}// you can still assemble them into a single interface if you wishinterfacePageCountVisitorInterfaceextendsBookPageCountVisitorInterface,DocumentPageCountVisitorInterface{// empty}classBook{publicfunctionaccept(BookPageCountVisitorInterface$visitor){return$visitor->visitBook($this);}}traitBookPageCountVisitor{publicfunctionvisitBook(Book$book):int{return1;}}traitDocumentPageCountVisitor{publicfunctionvisitDocument(Document$doc):int{return2;}}classPageCountVisitorimplementsPageCountVisitorInterface{useBookPageCountVisitor;useDocumentPageCountVisitor;}$visitor=newPageCountVisitor();$book=newBook();echo$book->accept($visitor);// 1
Because every visitor has a different reason for being, so they all will have different results. There is no single return type or return value type.
TL;DR makin small interfaces & traits gives you the flexibility to only implement what you need 😎
This will however replace one VisitorInterface with several new interfaces. Which doesn't make much sense to me either. Because now my Book can only receive a BookPageCountVisitorInterface which, by its name only serves one purpose. It needs to have the generic VisitorInterface to be able to receive any visitor.
And those trait BookPageCountVisitor traits will never be reused, because they serve the purpose of one visitor. So they might as well live on that visitor only, right?
I personally would rather have one interface that has a function for every type, and create an abstract base class that implements all those functions with an empty body. Then there would be less overhead in files, and my visitors only need to overwrite the functions that matter.
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
While it is true more indirection can create confusion, the naive implementation (a service relying on instanceof to determine the correct algorithm to use) is very rigid (creates static dependencies between components) and doesn't scale at all (imagine handling hundreds of components this way, good luck 🙄)
The visitor pattern benefits clearly outweights the added complexity and create much cleaner and understandable code IMHO.
I don't see why 🤷
You can mitigate that problem by applying the Interface Segregation Sprinciple (ISP) ("no client should be forced to depend on methods it does not use.")
Pro tip: using traits helps keep things clean & reusable.
Using the solution below, we see the Book class now only rely on the BookPageCountVisitorInterface and not the whole visitor. The visitor concrete class doesn't have to implement visitDocument if it's not needed or possible 👍
TL;DR makin small interfaces & traits gives you the flexibility to only implement what you need 😎
Because every visitor has a different reason for being, so they all will have different results. There is no single return type or return value type.
This will however replace one
VisitorInterface
with several new interfaces. Which doesn't make much sense to me either. Because now my Book can only receive aBookPageCountVisitorInterface
which, by its name only serves one purpose. It needs to have the genericVisitorInterface
to be able to receive any visitor.And those
trait BookPageCountVisitor
traits will never be reused, because they serve the purpose of one visitor. So they might as well live on that visitor only, right?I personally would rather have one interface that has a function for every type, and create an abstract base class that implements all those functions with an empty body. Then there would be less overhead in files, and my visitors only need to overwrite the functions that matter.