الگوی Visitor

الگوی Visitor(دیدارگر-بازدید کننده) در دسته الگوهای رفتاری قرار دارد. یک عملیات را بر روی عناصر یک ساختار شی انجام دهید. Visitor به شما اجازه می دهد یک عملیات جدید را بدون تغییر کلاس های عناصر که در آن فعال است تعریف کنید. به عبارت دیگر ساده سازی اجرای یک عملیات بر روی مجموعه ای از اشیاء ناهمگون که مفهوم و ماهیت آن عملیات به طور کلی ثابت است اما هر شی، آن را به شکل متفاوتی انجام می دهد. UML class diagram شركت كنندگان کلاس ها و اشیاء شرکت کننده در این الگو عبارتند از:بازدیدکننده Visitor (بازدیدکننده در مثال دنیای واقعی Visitor):یک عملیات بازدید (Visit operation) برای هر کلاس ConcreteElement در ساختار شی را اعلام می کند. نام و امضای عملیات کلاس را مشخص می کند که درخواست (request)بازدید(Visit) را به بازدید کننده(visitor) ارسال می کند.که اجازه می دهد بازدید کننده تعیین کدام کلاس سفت از عنصر بازدید شده است. سپس بازدید کننده (visitor) می تواند به طور مستقیم از طریق واسط(interface) خاص خود به عناصر دسترسی پیدا کند.به عبارت دیگر کلاسی است که ساختار کلی دیدار گر ها را تعریف می کند. در این کلاس به ازای هر شیء در ساختار داده مورد پردازش(هر ConcreteElement)، یک متون اختصاصی و مجزا تعریف می شود مانند VisitConcreteElementB،VisitConcreteElementAو غیره. بازدیدکننده سفت ConcreteVisitor (بازدید کننده درآمد، بازدید کننده تعطیلاتIncomeVisitor, VacationVisitor): پیاده سازی هر عملیات اعلام شده توسط بازدید کننده. هر عملیات یک قطعه الگوریتم تعریف شده برای کلاس یا شیء مربوطه در ساختار را پیاده سازی میکند.ConcreteVisitor زمینه را برای الگوریتم فراهم می کند و وضعیت محلی آن را ذخیره می کند. این وضعیت اغلب نتایج را در طول گذر از ساختار جمع می کند.به عبارت دیگر کلاس هایی هستند که visitor را پیاده سازی کرده اند. و پیاده سازی هر یک از متدهایVisitConcreteElementX()، بخشی از عملیات مورد نظر پیاده سازی می شود.توجه کنید متدهای VisitConcreteElementX() در کلاس انتزاعی visitor نیز به صورت انتزاعی تعریف شده است و لذا خواص یک الگوریتم یا عملیات به خصوص نیستند.از این رو با نوشتن کConcreteVisitorهای مختلف بر پایه آن ها می توانیم الگوریتم های مختلفی را پیاده سازی کنیم که روی مجموعه ای اشیا اجرا شود .عنصر Element (عنصر Element): یک عملیات Accept را تعریف می کند که یک visitor(بازدیدکننده) را به عنوان یک argument (استدلال) می گیرد.به عبارت دیگر کلاسی است که اشیاء ناهمگون موجود در ساختار داده را تحت یک شکل در آورده و نحوه پذیرش ویزیتور را برای آنها تعریف می کند.بر اساس این واسط است که می‌توانیم نمونه‌های اشیا را با وجود ناهمگون بودنشان در ساختار داده ی ObjectStructure (مانند یک آرایه یا لیست)نگهداری کرده و visitor را در یک حلقه و هر یک از آن‌ها پاس بدهیم. کلاس Element به زیر کلاس هایش اعلام می کند برای این که بتواند Visitor را دریافت کرده و استفاده کنند، متدی به نام Accept() را پیاده سازی کنند. هنگام پردازش اشیا در حلقه ای که روی عنصر موجود در ObjectStructure تکرار میشود، هربار این متن روی هرچه فراخوانده شده و شی Visitor به آن پاس داده می شود. بدین ترتیب هر یک از اشیا به Visitor دسترسی پیدا کرده،متد مخصوص خودش را روی او صدا زده و بدین ترتیب الگوریتم مورد نظر روی این شی اجرا می‌شود. عنصر سفت ConcreteElement (کارمند Employee):پیاده سازی یک عملیات Accept که یک Visitor را به عنوان یک argument می گیرد.به عبارت دیگر کلاسهای اشیاء موجود در ساختار داده هستند که از ٍElement مشتق شده و واسط انتزاعی آن را پیاده سازی کرده اند. ساختار شی ObjectStructure (کارکنان Employees):عناصر آن را می توان شمرد.ممکن است واسط سطح بالا( high-level ) را فراهم کند تا Visitor بتواند از عناصر خود بازدید کند. ممکن است یک کامپوزیت (الگوی Composite) یا یک مجموعه از جمله یک لیست یا یک مجموعه باشدبه عبارت دیگر ساختار داده ای است که حاوی مجموعه‌ای از اشیاء مختلف است مانند یک درخت، کلکسیون، لیست، آرایه، گراف و مانند آن. این ساختار داده باید قابلیت شمارش داشته باشد(الگوی Iterator در آن پیاده سازی شدخ باشد)تا بتوان اشیای موجود در آن را در یک حلقه یکی پس از دیگری پردازش کرد. ساختار کلی کدالگوی Visitor که در آن یک شی از یک ساختار شیء عبور می کند و عمل مشابهی را بر روی هر گره در این ساختار نشان می دهد. اشیاء بازدید کننده مختلف عملیات مختلف را تعریف می کنند. using System; using System.Collections.Generic; namespace DoFactory.GangOfFour.Visitor.Structural { /// <summary> /// MainApp startup class for Structural /// Visitor Design Pattern. /// </summary> class MainApp { static void Main () { // Setup structure ObjectStructure o = new ObjectStructure (); o.Attach (new ConcreteElementA ()); o.Attach (new ConcreteElementB ()); // Create visitor objects ConcreteVisitor1 v1 = new ConcreteVisitor1 (); ConcreteVisitor2 v2 = new ConcreteVisitor2 (); // Structure accepting visitors o.Accept (v1); o.Accept (v2); // Wait for user Console.ReadKey (); } } /// <summary> /// The 'Visitor' abstract class /// </summary> abstract class Visitor { public abstract void VisitConcreteElementA ( ConcreteElementA concreteElementA); public abstract void VisitConcreteElementB ( ConcreteElementB concreteElementB); } /// <summary> /// A 'ConcreteVisitor' class /// </summary> class ConcreteVisitor1 : Visitor { public override void VisitConcreteElementA ( ConcreteElementA concreteElementA) { Console.WriteLine ("{0} visited by {1}", concreteElementA.GetType ().Name, this.GetType ().Name); } public override void VisitConcreteElementB ( ConcreteElementB concreteElementB) { Console.WriteLine ("{0} visited by {1}", concreteElementB.GetType ().Name, this.GetType ().Name); } } /// <summary> /// A 'ConcreteVisitor' class /// </summary> class ConcreteVisitor2 : Visitor { public override void VisitConcreteElementA ( ConcreteElementA concreteElementA) { Console.WriteLine ("{0} visited by {1}", concreteElementA.GetType ().Name, this.GetType ().Name); } public override void VisitConcreteElementB ( ConcreteElementB concreteElementB) { Console.WriteLine ("{0} visited by {1}", concreteElementB.GetType ().Name, this.GetType ().Name); } } /// <summary> /// The 'Element' abstract class /// </summary> abstract class Element { public abstract void Accept (Visitor visitor); } /// <summary> /// A 'ConcreteElement' class /// </summary> class ConcreteElementA : Element { public override void Accept (Visitor visitor) { visitor.VisitConcreteElementA (this); } public void OperationA () { } } /// <summary> /// A 'ConcreteElement' class /// </summary> class ConcreteElementB : Element { public override void Accept (Visitor visitor) { visitor.VisitConcreteElementB (this); } public void OperationB () { } } /// <summary> /// The 'ObjectStructure' class /// </summary> class ObjectStructure { private List<Element> _elements = new List<Element> (); public void Attach (Element element) { _elements.Add (element); } public void Detach (Element element) { _elements.Remove (element); } public void Accept (Visitor visitor) { foreach (Element element in _elements) { element.Accept (visitor); } } } } OutputConcreteElementA visited by ConcreteVisitor1ConcreteElementB visited by ConcreteVisitor1ConcreteElementA visited by ConcreteVisitor2ConcreteElementB visited by ConcreteVisitor2یا abstract class Visitor { public abstract void VisitConcreteElementA(ConcreteElementA element); public abstract void VisitConcreteElementB(ConcreteElementB element); } class ConcreteVisitor1 : Visitor { public override void VisitConcreteElementA(ConcreteElementA element) { Console.WriteLine("{0}: {1} visiting by {2} ...", element.GetType().Name, element.Name, this.GetType().Name); element.OperationA(); } public override void VisitConcreteElementB(ConcreteElementB element) { Console.WriteLine("{0}: {1} visiting by {2} ...", element.GetType().Name, element.ID, this.GetType().Name); element.OperationB(); } } abstract class Element { public abstract void Accept(Visitor visitor); } class ConcreteElementA : Element { private string name; public string Name { get { return name; } } public ConcreteElementA(string name) { this.name = name; } public override void Accept(Visitor visitor) { visitor.VisitConcreteElementA(this); } public void OperationA() { Console.WriteLine("\tOperationA()"); } } class ConcreteElementB: Element { private string id; public string ID { get { return id; } } public ConcreteElementB(string id) { this.id = id; } public override void Accept(Visitor visitor) { visitor.VisitConcreteElementB(this); } public void OperationB() { Console.WriteLine("\tOperationB()"); } } class ObjectStructure { private List<Element> elements = new List<Element>(); public void Add(Element element) { elements.Add(element); } public void Accept(Visitor visitor) { foreach (var element in elements) { element.Accept(visitor); } } } class Test { static void Main() { var o = new ObjectStructure(); o.Add(new ConcreteElementA("A1")); o.Add(new ConcreteElementA("A2")); o.Add(new ConcreteElementB("B1")); o.Add(new ConcreteElementA("A3")); o.Add(new ConcreteElementB("B2")); var visitor1 = new ConcreteVisitor1(); o.Accept(visitor1); Console.ReadKey(); } } /* OUTPUT:ConcreteElementA: A1 visiting by ConcreteVisitor1 ... OperationA()ConcreteElementA: A2 visiting by ConcreteVisitor1 ... OperationA()ConcreteElementB: B1 visiting by ConcreteVisitor1 ... OperationB()ConcreteElementA: A3 visiting by ConcreteVisitor1 ... OperationA()ConcreteElementB: B2 visiting by ConcreteVisitor1 ... OperationB()*/مثال دنیای واقعیالگوی Visitor را نشان می دهد که در آن دو شی یک لیست از کارکنان را می گذرانند و عمل مشابهی را در هر کارمند انجام می دهند. دو شیء بازدید کننده(Visitor) عملیات های مختلف را تعریف می کنند - یک روز تعطیلات و سایر درآمد ها را تنظیم می کند. using System; using System.Collections.Generic; namespace DoFactory.GangOfFour.Visitor.RealWorld { /// <summary> /// MainApp startup class for Real-World /// Visitor Design Pattern. /// </summary> class MainApp { /// <summary> /// Entry point into console application. /// </summary> static void Main () { // Setup employee collection Employees e = new Employees (); e.Attach (new Clerk ()); e.Attach (new Director ()); e.Attach (new President ()); // Employees are 'visited' e.Accept (new IncomeVisitor ()); e.Accept (new VacationVisitor ()); // Wait for user Console.ReadKey (); } } /// <summary> /// The 'Visitor' interface /// </summary> interface IVisitor { void Visit (Element element); } /// <summary> /// A 'ConcreteVisitor' class /// </summary> class IncomeVisitor : IVisitor { public void Visit (Element element) { Employee employee = element as Employee; // Provide 10% pay raise employee.Income *= 1.10; Console.WriteLine ("{0} {1}'s new income: {2:C}", employee.GetType ().Name, employee.Name, employee.Income); } } /// <summary> /// A 'ConcreteVisitor' class /// </summary> class VacationVisitor : IVisitor { public void Visit (Element element) { Employee employee = element as Employee; // Provide 3 extra vacation days employee.VacationDays += 3; Console.WriteLine ("{0} {1}'s new vacation days: {2}", employee.GetType ().Name, employee.Name, employee.VacationDays); } } /// <summary> /// The 'Element' abstract class /// </summary> abstract class Element { public abstract void Accept (IVisitor visitor); } /// <summary> /// The 'ConcreteElement' class /// </summary> class Employee : Element { private string _name; private double _income; private int _vacationDays; // Constructor public Employee (string name, double income, int vacationDays) { this._name = name; this._income = income; this._vacationDays = vacationDays; } // Gets or sets the name public string Name { get { return _name; } set { _name = value; } } // Gets or sets income public double Income { get { return _income; } set { _income = value; } } // Gets or sets number of vacation days public int VacationDays { get { return _vacationDays; } set { _vacationDays = value; } } public override void Accept (IVisitor visitor) { visitor.Visit (this); } } /// <summary> /// The 'ObjectStructure' class /// </summary> class Employees { private List<Employee> _employees = new List<Employee> (); public void Attach (Employee employee) { _employees.Add (employee); } public void Detach (Employee employee) { _employees.Remove (employee); } public void Accept (IVisitor visitor) { foreach (Employee e in _employees) { e.Accept (visitor); } Console.WriteLine (); } } // Three employee types class Clerk : Employee { // Constructor public Clerk () : base ("Hank", 25000.0, 14) { } } class Director : Employee { // Constructor public Director () : base ("Elly", 35000.0, 16) { } } class President : Employee { // Constructor public President () : base ("Dick", 45000.0, 21) { } } } OutputClerk Hank 's new income: $27,500.00Director Elly's new income: $38,500.00President Dick's new income: $49,500.00Clerk Hank's new vacation days : 17Director Elly's new vacation days: 19President Dick's new vacation days : 24منبع