yonatankarp/software-design-skills

gof-visitor

Use when adding new operations over an existing object hierarchy without modifying it — phrases like "Visitor pattern", "double dispatch", "I want to add operations to this AST/tree without changing the node classes", "separate algorithm from data structure".

소스 보기

사용 방법

  1. 설치 명령을 복사하고 자신의 에이전트 환경에 설치하기 전에 소스 패키지를 확인하세요.
  2. 필요한 도구, 인증 정보, 지원 환경은 원본 문서에서 확인하세요.
  3. 입력과 원하는 산출물을 설명하고 출처와 결과를 대조하세요.
npx skills add https://github.com/yonatankarp/software-design-skills --skill gof-visitor

시작 전 확인

  • 설치에는 모델 구독, API 한도, 파일 접근 권한이 포함되지 않습니다.
  • 별점 수와 설치 수는 활동 지표이며 독립적인 품질·보안 검증이 아닙니다.
원본 Skill 문서

원본 저장소의 제목, 예시, 코드, 표, 링크, 이미지를 유지해 표시합니다.

One-line summary

Represent an operation to be performed on the elements of a stable object structure — letting you add new operations to the structure without modifying its element classes.

When to use this skill

  • A stable hierarchy of types (AST nodes, file-system entries, document elements) needs new operations added over time and you don't want to keep touching each element class.
  • You need to perform different operations on the same hierarchy: a pretty-printer, a type-checker, a JSON serializer, a metrics-collector. Each is a separate Visitor.
  • The classical GoF "double dispatch" use case — picking which method to call based on two dynamic types (the element type and the operation type).

When NOT to use this skill

  • The hierarchy is itself unstable — every new element type forces every Visitor to be updated. Visitor trades "adding operations is cheap" for "adding element types is expensive".
  • The language already gives you exhaustive pattern matching (Kotlin when over sealed types, Rust match, Scala pattern matching). In those languages, you can add an operation as a function with an exhaustive when — without subclassing each element.
  • You only have one operation to add and there's no reason to expect more. A method on the element is simpler.

Core content

Two parallel pieces:

Element hierarchy. Each element class has an accept(visitor) method that calls back into the visitor with itself: accept(v) { v.visitX(this) }. The element does nothing else for the operation.

Visitor interface. Declares one method per concrete element type: visitCircle(Circle), visitSquare(Square), etc. Each concrete visitor (Pretty-Printer, Type-Checker, JSON-Serializer) implements all of them.

Adding a new operation means adding a new Visitor class. Existing element classes are untouched.

The double-dispatch trick: when client code calls element.accept(visitor), the dispatch first selects which accept to run (based on the element's runtime type), and inside that, the dispatch selects which visitX to run (based on the visitor's runtime type). Two levels of polymorphism cooperate.

In Kotlin, this is usually unnecessary. A sealed hierarchy plus an exhaustive when gives you the same "add new operations without touching element classes" benefit, without the accept ceremony, and the compiler enforces that you handle every element type:

sealed class Shape
data class Circle(...): Shape()
data class Square(...): Shape()

fun area(s: Shape): Double = when (s) {
    is Circle -> ...
    is Square -> ...
}  // compile error if a new Shape variant isn't handled

The trade-off flips: with sealed when, adding an operation is cheap (a new function), and adding an element is loud (every existing when must be updated — but the compiler points each one out).

Decision heuristics

  • Choose Visitor when the language lacks exhaustive pattern matching over closed hierarchies.
  • In Kotlin (and similar languages), prefer sealed + exhaustive when — you get the same benefit with less ceremony and compile-time exhaustiveness checks.
  • Use Visitor when operations need to cooperate across element types (a tree-walker that maintains state as it descends) — visitors hold state across visits more cleanly than ad-hoc functions.

Anti-patterns

  • Visitor on an unstable hierarchy — every new element type forces N visitors to be updated and the compiler doesn't help in languages without explicit declaration coverage.
  • Visitor with default no-op methods to "make it easier to add elements" — silently drops behavior for types nobody remembered to handle.
  • Visitor in a language where exhaustive pattern matching is available — ceremony for no benefit.

See also

  • gof-iterator — Visitor walks a structure; an Iterator that yields elements + an exhaustive when is often the modern alternative.
  • gof-composite — Visitor is often applied over Composites; the accept recursion descends the tree.
  • gof-kotlin-idioms — sealed classes + when are the idiomatic substitute for Visitor in Kotlin.

References

  • Design Patterns (GoF, 1994), Ch. 5 — Visitor. (Not in HFDP 2nd ed.)
  • Kotlin Design Patterns and Best Practices (Alexey Soshin), Ch. 4 — Visitor, with the sealed-class comparison.

관련 작업

같은 저장소의 Skills

더 많은 Skills

모든 Skills
yonatankarp
커뮤니티

arch-adr

Use when recording an architectural decision so future maintainers know why — phrases like "ADR", "architecture decision record", "why did we pick X", "lightweight architecture documentation", "decision log", "should this be an ADR", "tribal knowledge", "we chose this two years ago and now nobody remembers why". Defines the ADR as a short, structured document capturing what was decided, why, alternatives considered, and consequences — written at the time of the decision and kept in the repo.

설치 수
2
GitHub Stars
1
업데이트
5월 19일
yonatankarp
커뮤니티

arch-fitness-functions

Use when enforcing architectural characteristics through automation — phrases like "architecture fitness function", "evolutionary architecture", "architecture tests", "ArchUnit", "dependency rules in CI", "automated architecture checks", "guardrails", "drift detection". Defines fitness functions as automated checks that verify architectural characteristics on every change, catching drift before it accumulates.

설치 수
2
GitHub Stars
1
업데이트
5월 19일
yonatankarp
커뮤니티

arch-microservices

Use when designing or reviewing a microservices architecture — phrases like "microservices", "should we split this service", "service boundaries", "bounded context per service", "API gateway", "backend for frontend", "BFF", "service mesh", "microservices vs monolith", "database per service", "data ownership". Defines microservices as independently deployable services aligned with business capabilities — and the discipline of resisting the style until the monolith proves inadequate.

설치 수
2
GitHub Stars
1
업데이트
5월 19일
yonatankarp
커뮤니티

arch-strangler-fig

Use when migrating a legacy system incrementally — phrases like "strangler fig", "strangler pattern", "incremental migration", "monolith to microservices", "rewrite without big bang", "replace legacy in place", "facade in front of the legacy system", "anticorruption layer". Defines Fowler's Strangler Fig as the migration discipline of gradually replacing legacy functionality behind a stable facade, never doing a big-bang rewrite.

설치 수
2
GitHub Stars
1
업데이트
5월 19일