Code Smell 289 - Shy Visitor

Maxi ContieriMaxi Contieri
3 min read

TL;DR: Avoid combining the Visitor pattern with instanceof checks.

Problems πŸ˜”

  • Open/Closed principle violation
  • Tight Coupling
  • Maintainability
  • Code duplication
  • Poor readability
  • Brittle design
  • IFs

Solutions πŸ˜ƒ

  1. Implement the Visitor pattern correctly.
  2. Avoid instanceof checks.
  3. Favor polymorphism.
  4. Encapsulate behavior.

Context πŸ’¬

When you use the Visitor pattern, you aim to separate algorithms from the objects they operate on.

Combining it with instanceof checks breaks this separation, leading to a fragile and hard-to-maintain design.

Sample Code πŸ“–

Wrong 🚫

<?php

class SpaceObjectVisitor {
    public function visit(SpaceObject $object) {
        if ($object instanceof NeutronStar) {
            $this->visitNeutronStar($object);
        } elseif ($object instanceof Magnetar) {
            $this->visitMagnetar($object);
        } elseif ($object instanceof BlackHole) {
            $this->visitBlackHole($object);
        } 
        // Not closed for modification
    }

    private function visitNeutronStar(NeutronStar $star) {
        // Handle neutron star observation
    }

    private function visitMagnetar(Magnetar $magnetar) {
        // Handle magnetar observation
    }

    private function visitBlackHole(BlackHole $blackHole) {
        // Handle black hole observation
    }
}

Right πŸ‘‰

<?php

interface SpaceObject {
    public function accept(SpaceObjectVisitor $visitor): void;
}

class NeutronStar implements SpaceObject {
    public function accept(SpaceObjectVisitor $visitor): void {
        $visitor->visitNeutronStar($this);
    }
}

class Magnetar implements SpaceObject {
    public function accept(SpaceObjectVisitor $visitor): void {
        $visitor->visitMagnetar($this);
    }
}

class BlackHole implements SpaceObject {
    public function accept(SpaceObjectVisitor $visitor) {
        $visitor->visitBlackHole($this);
    }
}

class SpaceObjectVisitor {
    // Open for extension 
    // You can also convert it into an interface
    public function visitNeutronStar(NeutronStar $star): void {
        // Handle neutron star
    }

    public function visitMagnetar(Magnetar $magnetar): void {
        // Handle magnetar
    }

    public function visitBlackHole(BlackHole $blackHole): void {
        // Handle black hole
    }
}

Detection πŸ”

You can detect this smell by looking for instanceof checks within the Visitor pattern.

Automated tools can flag these checks, and code reviews can help identify them.

[X] Manual

Tags 🏷️

  • IFs

Level πŸ”‹

[X] Intermediate

Why the Bijection Is Important πŸ—ΊοΈ

When you model real-world objects, you should maintain a one-to-one correspondence between the real-world entities and your code.

Breaking this correspondence using instanceof checks (which do not exist on the model but the metamodel) leads to a mismatch between the real world and your program.

AI Generation πŸ€–

AI generators might create this smell if they are not properly guided.

AI Detection πŸ₯ƒ

AI can help fix this smell by suggesting refactoring steps to remove instanceof checks and use polymorphism instead.

Try Them! πŸ› 

Remember: AI Assistants make lots of mistakes

Conclusion βœ”οΈ

When you use the Visitor pattern, avoid combining it with iskindOf checks.

Using the accept() method, the visitor class doesn't need to explicitly check the object type.

Each object will call the appropriate visitor method honoring the Open/Closed principle.

Favor polymorphism and encapsulation to keep your code clean and maintainable.

Relations πŸ‘©β€β€οΈβ€πŸ’‹β€πŸ‘¨

More Information πŸ“•

Disclaimer πŸ“˜

Code Smells are my opinion.

Credits πŸ™

Photo by Braňo on Unsplash


The Visitor pattern is a way to add new operations to a set of objects without changing their classes.

Erich Gamma


This article is part of the CodeSmell Series.

0
Subscribe to my newsletter

Read articles from Maxi Contieri directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Maxi Contieri
Maxi Contieri

I’m a senior software engineer loving clean code, and declarative designs. S.O.L.I.D. and agile methodologies fan.