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.

🤖 Întreabă AI despre acest articol

AI Răspuns generat de AI pe baza acestui articol.
AI scrie răspunsul…

Scrie un comentariu

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