Visitor Pattern – operații noi fără a modifica structura obiectelor

  • Doru Bulubasa
  • 26 January 2026

În aplicațiile mature, modelele de domeniu tind să fie stabile, dar cerințele de business evoluează constant. Apar nevoi noi: rapoarte, validări, exporturi, calcule sau loguri suplimentare.

Visitor Pattern rezolvă elegant această problemă permițând adăugarea de operații noi peste o structură de obiecte fără a modifica clasele existente.

Este un pattern din categoria Behavioral și este extrem de util atunci când:

  • ai o structură de obiecte complexă (ex: AST, documente, elemente UI, modele de domeniu),

  • vrei să eviți modificarea claselor existente,

  • operațiile aplicate peste obiecte se schimbă mai des decât structura lor.


Problema

Să presupunem că avem o aplicație care lucrează cu diferite tipuri de documente:

  • Invoice

  • Contract

Inițial, vrem să:

  • afișăm informații

  • calculăm valori

  • exportăm date

Dacă adăugăm metode direct în fiecare clasă (Print(), Export(), Validate()), ajungem rapid la:

  • clase suprasolicitate

  • încălcarea Open/Closed Principle

  • cod greu de întreținut


Soluția oferită de Visitor Pattern

Visitor Pattern separă:

  • structura obiectelor (modelele)

  • operațiile aplicate asupra lor

👉 Obiectele acceptă un visitor

👉 Visitor-ul știe ce să facă pentru fiecare tip de obiect


Structura pattern-ului

Componentele principale:

  1. Element – interfață comună pentru obiectele vizitate

  2. ConcreteElement – implementările reale

  3. Visitor – interfață pentru operații

  4. ConcreteVisitor – implementări concrete ale operațiilor

  5. Object Structure – colecția de elemente


Exemplu concret în C#

1️⃣ Interfața IVisitable

public interface IVisitable
{
    void Accept(IVisitor visitor);
}


2️⃣ Interfața IVisitor

public interface IVisitor
{
    void VisitInvoice(Invoice invoice);
    void VisitContract(Contract contract);
}


3️⃣ Elementele concrete

public class Invoice : IVisitable
{
    public string Number { get; set; }
    public decimal Total { get; set; }

    public void Accept(IVisitor visitor)
    {
        visitor.VisitInvoice(this);
    }
}

public class Contract : IVisitable
{
    public string Client { get; set; }
    public DateTime StartDate { get; set; }

    public void Accept(IVisitor visitor)
    {
        visitor.VisitContract(this);
    }
}


4️⃣ Visitor concret – afișare date

public class PrintVisitor : IVisitor
{
    public void VisitInvoice(Invoice invoice)
    {
        Console.WriteLine($"Invoice {invoice.Number}, Total: {invoice.Total} RON");
    }

    public void VisitContract(Contract contract)
    {
        Console.WriteLine($"Contract with {contract.Client}, Start: {contract.StartDate:d}");
    }
}


5️⃣ Visitor concret – export date

public class ExportVisitor : IVisitor
{
    public void VisitInvoice(Invoice invoice)
    {
        Console.WriteLine($"Exporting invoice {invoice.Number}...");
    }

    public void VisitContract(Contract contract)
    {
        Console.WriteLine($"Exporting contract for {contract.Client}...");
    }
}


6️⃣ Utilizare

var documents = new List<IVisitable>
{
    new Invoice { Number = "INV-001", Total = 1200 },
    new Contract { Client = "ACME SRL", StartDate = DateTime.Today }
};

var printVisitor = new PrintVisitor();
var exportVisitor = new ExportVisitor();

foreach (var doc in documents)
{
    doc.Accept(printVisitor);
    doc.Accept(exportVisitor);
}


Avantaje

✅ Respectă Open/Closed Principle

✅ Operațiile sunt centralizate și ușor de extins

✅ Cod mai curat pentru modele

✅ Ideal pentru rapoarte, validări, exporturi, audit


Dezavantaje

⚠️ Dificil de extins structura (adăugarea unui nou element necesită modificarea tuturor visitor-ilor)

⚠️ Pattern mai complex, greu de înțeles la început

⚠️ Nu este ideal pentru structuri simple


Când să folosești Visitor Pattern

✔ Structură de obiecte stabilă

✔ Operații care se schimbă des

✔ Ai nevoie de separare clară între date și logică

✔ Lucrezi cu ierarhii complexe (ex: interpretoare, procesare documente, DDD)


Concluzie

Visitor Pattern este un instrument puternic atunci când vrei să extinzi comportamentul aplicației fără a „atinge” modelele existente.

Folosit corect, poate transforma un cod rigid într-unul extrem de flexibil — exact genul de pattern care își arată valoarea în proiecte mari și pe termen lung.

Scrie un comentariu

Adresa de mail nu va fi publicata. Campurile obligatorii sunt marcate cu *