Skip to content

Visitor

Homayoon edited this page Aug 15, 2022 · 2 revisions

الگوی طراحی Visitor

الگوی طراحی Visitor زیرمجموعه ی الگوهای نوع Behavioral Pattern است و مانند بقیه ی الگوهای این دسته به بررسی و ساماندهی روابط و الگوریتم های موجود بین object های برنامه ی ما می ‌پردازد. الگوی طراحی Visitor به شما اجازه می ‌دهد که متدها یا الگوریتم هایی را به یک کلاس اضافه کنید، بدون این که تغییری در کد پایه ی آن کلاس اِعمال کنید.

ساختار

مزایا و معایب استفاده از Visitor

مزایا:

  1. اولین مزیتی که می ‌توان برای این الگوی طراحی نام برد، کمک به رعایت قانون Open/Closed Principle از مجموعه قوانین SOLID است. مورد استفاده ی این الگوی طراحی، مواقعی است که نمی ‌توانیم به علت قانون Open/Closed یا نبود دسترسی به کلاس مربوطه، قابلیت مورد نظر را به برنامه یمان اضافه کنیم و با استفاده از الگوی طراحی Visitor این مانع رفع می‌شود.
  2. دومین مزیت، عدم نقض قانون Single Responsibility Principle از مجموعه قوانین SOLID است که به لطف الگو Visitor هر کدام از کلاس‌های ما دقیقا یک وظیفه بر عهده دارند. مثلا کلاس‌های Element تنها وظیفه ی انجام امور مربوط به همان Element را بر عهده دارد مانند: ذخیره کردن نام در متغیر مربوطه یا انجام پردازش روی موقعیت جغرافیایی آن Element، از طرفی کلاس Visitor مربوط به آن Element وظیفه ی انجام اموری را دارد که می ‌تواند روی object های آن اِلِمان اجرا شود اما جزو وظایف Element مورد نظر نیست. پس قانون Single Responsibility را رعایت کرده‌ایم.
  3. مزیت دیگر این است که یک Visitor در تعامل با کلاس‌های مختلف می ‌تواند مجموعه ای از اطلاعات مختلف از این کلاس‌ها را در کنار هم جمع کند و برای مثال پردازشی برروی آن اطلاعات انجام دهد؛ در حالی که این اطلاعات به هم ارتباطی ندارند و از کلاس‌های مختلفی دریافت شده‌اند.
  4. مزیت چهارم این است که شما، عملیات و الگوریتم هایی که در برنامه خود استفاده می ‌کنید را تحت نام متدهای مختلف در یک کلاس به نام Visitor جمع می ‌کنید که این کار از پخش شدن این متدهای مرتبط به هم، در سطح برنامه شما جلوگیری می ‌کند.

معایب:

  1. اولین و مهم‌ترین عیبی که می ‌توان به این الگو وارد نمود این است که اگر شما یک Element به برنامه خود اضافه کنید یا از آن حذف کنید، در این صورت شما باید تمام Visitor هایی که به آن Element مربوط بودند را به‌روزرسانی کنید و متدهایی را به آن اضافه یا کم کنید.
  2. عیب دیگری که می ‌تواند شما را از استفاده از این الگو منصرف کند این است که، وقتی شما object ای را به Visitor ورودی می ‌دهید، این Visitor ممکن است درخواست دسترسی به اطلاعات یا متدهایی از object شما را داشته باشد که به عنوان اطلاعات یا متدهایprivate تعریف شده‌اند. از طرفی چون مورد استفاده ی Visitor عموما در مواردی است که پروژه در وضعیت production قرار دارد و امکان به‌روزرسانی متدها و متغیرهای کلاس object مورد نظر وجود ندارد، پس باید به وضعیت دسترسی اطلاعات و متدهای مورد استفاده از object در Visitor توجه داشته باشیم.

مثال

فرض کنید چند شرکت داریم (Company) و این شرکت‌ها شامل تعدادی دپارتمان (Department) هستند و هر دپارتمان تعدادی کارمند (Employee) دارد. برنامه ی ما برای ساماندهی و مدیریت شرکت‌ها، توسعه داده شده است. در این حالت هر کدام از موجودیت های Company و Department و Employee یک کلاس هستند که از Interface به نام Entity، Implement شده‌اند

شما در کلاس Company عملیات مربوط به شرکت را انجام می ‌دهید و این اتفاق برای کلاس‌های دپارتمان و کارمند هم، مشابه است. حال فرض کنید مدیر پروژه از شما می ‌خواهد کدی به برنامه اضافه کنید که هزینه ی دستمزد شرکت‌ها به همراه هزینه ی تفکیک شده ی هر دپارتمان زیر مجموعه ی این شرکت‌ها را به عنوان یک گزارش خروجی بدهد. در اینجا هم با مشکل نبود دسترسی به کلاس‌های Company و Department و Employee مواجه هستیم و از طرفی قرار دادن کدِ تهیه ی گزارش در این کلاس‌ها از نظر قوانین SOLID صحیح نیست. پس به سراغ الگوی طراحی Visitor می ‌رویم و طبق آنچه که در بخش‌های قبلی توضیح داده شد آن را پیاده سازی می ‌کنیم. شکل کلی کلاس‌های زیر مجموعه ی Entity به شکل زیر در می ‌آید:

همان‌طور که مشاهده می ‌کنید، به کلاس‌های زیر مجموعه Entity، یک متد به نام Accept اضافه کردیم که وظیفه ارسال یک نمونه از object کلاس خود را به یکی از متدهای کلاس Visitor بر عهده دارد. به غیر از این مورد، نیاز به اِعمال تغییر دیگری نیست. از طرفی کلاس SalaryReportVisitor که وظیفه ی انجام عملیات محاسبه ی دستمزد شرکت‌ ها و دپارتمان های زیر مجموعه ی شرکت‌ها به صورت تفکیک شده را بر عهده دارد از Visitor Interface، Implement می ‌شود.

Code Example

interface Entity {
    accept(visitor: Visitor)
}

interface Visitor {
    visitCompany(company: Company)

    visitDepartment(department: Department)

    visitEmployee(employee: Employee)
}

class Employee implements Entity {
    private readonly _name: string
    private readonly _position: string
    private readonly _salary: number
    private readonly _age: number

    constructor(name: string, position: string, salary: number, age: number) {
        this._name = name
        this._position = position
        this._salary = salary
        this._age = age
    }

    get name(): string {
        return this._name
    }

    get position(): string {
        return this._position
    }

    get salary(): number {
        return this._salary
    }

    get age(): number {
        return this._age
    }

    accept(visitor: Visitor) {
        return visitor.visitEmployee(this)
    }
}

class Department implements Entity {
    private readonly _name: string
    private readonly _employees: Array<Employee>

    constructor(name: string, employees: Array<Employee>) {
        this._name = name
        this._employees = employees
    }

    get name(): string {
        return this._name
    }

    get employees(): Array<Employee> {
        return this._employees
    }

    accept(visitor: Visitor) {
        return visitor.visitDepartment(this)
    }

}

class Company implements Entity {
    private readonly _name: string
    private readonly _departments: Array<Department>

    constructor(name: string, departments: Array<Department>) {
        this._name = name
        this._departments = departments
    }

    get name(): string {
        return this._name
    }

    get departments(): Array<Department> {
        return this._departments
    }

    accept(visitor: Visitor) {
        return visitor.visitCompany(this)
    }
}

class SalaryReportVisitor implements Visitor {
    visitCompany(company: Company) {
        return 'Company Salary...'
    }

    visitDepartment(department: Department) {
        return 'Department Salary... '
    }

    visitEmployee(employee: Employee) {
        return 'Employee Salary...'
    }

}
#### Main Block

let MD_employee_1 = new Employee(
    "M_D_Employee 1",
    "designer",
    100000,
    23
);
let TS_employee_1 = new Employee(
    "T_S_Employee 1",
    "supervisor",
    70000,
    24
);

// new departments
let mobileDev = new Department("Mobile Development", [
    MD_employee_1,
// and some other employees …
]);


let techSupport = new Department("Tech Support", [
    TS_employee_1,
]);


// new Company
let company = new Company("SuperStarDevelopment", [
    mobileDev,
    techSupport
]);


// make new visitor
let salary_report = new SalaryReportVisitor();

company.accept(salary_report)
techSupport.accept(salary_report)
MD_employee_1.accept(salary_report)


Is necessary

Design Pattern

Creational

Structural

Behavioral

Template

Clone this wiki locally