زمان تخمینی مطالعه: 12 دقیقه

الگوی طراحی Prototype یک الگوی طراحی Creational است که امکان ایجاد اشیاء(object) جدید را با کپی کردن یک شی موجود فراهم می‌کند. الگوی Prototype به برنامه نویس امکان می‌دهد پیچیدگی ایجاد نمونه‌های جدید را از مشتری پنهان کند. در واقع مفهوم اصلی این الگو، کپی کردن یک شی موجود به جای ایجاد یک نمونه جدید از ابتدا است، فرآیندی که ممکن است بسیار پرهزینه باشد. شی موجود به عنوان یک نمونه اولیه عمل می‌کند و شامل وضعیت object است. این الگوی طراحی با نام کلون(Clone) نیز شناخته می‌شود.

Prototype Design Pattern

بیان مسئله: فرض کنید یک Object دارید و می‌خواهید یک کپی دقیق از آن ایجاد کنید. نحوه انجام این کار چگونه است؟ برای انجام این کار ابتدا باید یک شی جدید از همان کلاس ایجاد کنید. سپس باید تمام فیلدهای شی اصلی را مرور کنید و مقادیر آنها را در شی جدید کپی کنید. با انجام این مراحل شاید تصور شود کار به خوبی پیش رفته است، اماهنوز یک مشکل دیگر وجود دارد. همه اشیاء را نمی‌توان به این روش کپی کرد زیرا برخی از فیلدهای شی ممکن است خصوصی(Private) باشند و از خارج از خود Object قابل مشاهده و دسترسی نباشند.

What can go wrong when copying things “from the outside"?” width=

یک مشکل دیگر در استفاده از رویکرد مستقیم وجود دارد. از آنجایی که برای ایجاد یک کپی باید کلاس شی را بشناسید، کد نوشته شده شما به آن کلاس وابسته می‌شود. اگر بوجود آمدن وابستگی بیشتر ایرادی نداشته باشد، با این حال یک مشکل دیگر باز وجود دارد. گاهی اوقات شما فقط اینترفیسی را می‌شناسید که شی از آن پیروی می‌کند و اطلاعی از کلاس آن ندارید، به عنوان مثال پارامتری در یک متد را در نظر بگیرید، این پارامتر هر Object ای را که از یک اینترفیس پیروی می‌کند می‌پذیرد .

الگوی Prototype فرآیند شبیه‌سازی را به اشیاء واقعی که در حال شبیه‌سازی هستند واگذار می‌کند. این الگو یک اینترفیس مشترک را برای تمامی اشیایی که از شبیه‌سازی پشتیبانی می‌کنند، اعلان می‌کند. این اینترفیس به برنامه نویس امکان می‌دهد یک شی را بدون وابسته کردن کد نوشته شده با کلاس آن شی، کلون کنید. معمولاً چنین اینترفیسی فقط شامل یک متد clone می‌شود. پیاده‌سازی متد clone (الگوی Prototype) در همه کلاس‌ها بسیار شبیه است. در این تکنیک ابتدا متد، یک شی از کلاس فعلی ایجاد می‌کند سپس تمام مقادیر فیلد شی قدیمی را به شی جدید منتقل می‌کند. در طی این فرآیند حتی می‌توانید فیلدهای خصوصی(Private) را کپی کنید زیرا اکثر زبان‌های برنامه‌نویسی به اشیا اجازه می‌دهند به فیلدهای خصوصی دیگر اشیاء متعلق به همان کلاس دسترسی پیدا کنند.

بر اساس این مفاهیم Object ای که از شبیه‌سازی(Cloning) پشتیبانی می‌کند، Prototype نامیده می‌شود. هنگامی که اشیاء شما دارای ده‌ها فیلد و صدها پیکربندی ممکن هستند، کلون کردن آنها ممکن است به عنوان جایگزینی برای طبقه‌بندی فرعی(subclassing) باشد.

Pre-built prototypes

در واقع در این الگو روند به این نحو است که شما مجموعه‌ای از اشیاء را ایجاد می‌کنید که به روش‌های مختلفی پیکربندی شده‌اند. هنگامی که به یک شی مانند آنچه پیکربندی کرده‌اید نیاز دارید، به جای ساختن یک شی جدید از ابتدا، فقط یک Prototype از آن را کلون می‌کنید.

نمونه قابل قیاس در دنیای واقعی

در زندگی واقعی، Prototype ها برای انجام تست‌های مختلف قبل از شروع تولید انبوه یک محصول استفاده می‌شود. با این حال، در این مورد خاص، Prototype در هیچ فرآیند تولید واقعی مشارکت نمی‌کند و یه جای آن نقشی منفعل را ایفا می‌کند.

The cell division

از آنجایی که Prototype های صنعتی واقعاً خود را کپی نمی‌کنند، مثال بسیار نزدیک‌تر و شبیه‌تر به الگوی Prototype، فرآیند تقسیم سلولی میتوزی است (تصویر بالا). پس از تقسیم میتوزی، یک جفت سلول یکسان تشکیل می‌شود. سلول اصلی به عنوان نمونه اولیه(Prototype) عمل کرده و نقش فعالی در ایجاد کپی بر عهده دارد.

ساختار الگوی Prototype

در این بخش به بررسی ساختار الگوی prototype می‌پردازیم و دو نوع پیاده‌سازی آن را به صورت UML خواهیم دید.

– پیاده‌سازی پایه(Basic)

– پیاده‌سازی Prototype رجیستری

شبه کد(Pseudocode)

در مقال ذکر شده در ادامه الگوی Prototype به شما امکان می‌دهد کپی‌های دقیقی از اجسام هندسی تولید کنید، بدون اینکه کد را به کلاس‌های آنها وابسته کنید. برای فهم بیشتر شبه کد به UML زیر دقت کنید:

The structure of the Prototype pattern example

همه کلاس‌های شکل از یک اینترفیس یکسان پیروی می‌کنند که یک متد شبیه‌سازی(Clone) را ارائه می‌دهد. یک زیر کلاس ممکن است قبل از کپی کردن مقادیر فیلد خود در شیء به دست آمده، روش شبیه‌سازی(Cloning) والد را فراخوانی کند. در ادامه به شبه کد توجه کنید:

// Base prototype.
abstract class Shape is
    field X: int
    field Y: int
    field color: string

    // A regular constructor.
    constructor Shape() is
        // ...

    // The prototype constructor. A fresh object is initialized
    // with values from the existing object.
    constructor Shape(source: Shape) is
        this()
        this.X = source.X
        this.Y = source.Y
        this.color = source.color

    // The clone operation returns one of the Shape subclasses.
    abstract method clone():Shape


// Concrete prototype. The cloning method creates a new object
// in one go by calling the constructor of the current class and
// passing the current object as the constructor's argument.
// Performing all the actual copying in the constructor helps to
// keep the result consistent: the constructor will not return a
// result until the new object is fully built; thus, no object
// can have a reference to a partially-built clone.
class Rectangle extends Shape is
    field width: int
    field height: int

    constructor Rectangle(source: Rectangle) is
        // A parent constructor call is needed to copy private
        // fields defined in the parent class.
        super(source)
        this.width = source.width
        this.height = source.height

    method clone():Shape is
        return new Rectangle(this)


class Circle extends Shape is
    field radius: int

    constructor Circle(source: Circle) is
        super(source)
        this.radius = source.radius

    method clone():Shape is
        return new Circle(this)


// Somewhere in the client code.
class Application is
    field shapes: array of Shape

    constructor Application() is
        Circle circle = new Circle()
        circle.X = 10
        circle.Y = 10
        circle.radius = 20
        shapes.add(circle)

        Circle anotherCircle = circle.clone()
        shapes.add(anotherCircle)
        // The `anotherCircle` variable contains an exact copy
        // of the `circle` object.

        Rectangle rectangle = new Rectangle()
        rectangle.width = 10
        rectangle.height = 20
        shapes.add(rectangle)

    method businessLogic() is
        // Prototype rocks because it lets you produce a copy of
        // an object without knowing anything about its type.
        Array shapesCopy = new Array of Shapes.

        // For instance, we don't know the exact elements in the
        // shapes array. All we know is that they are all
        // shapes. But thanks to polymorphism, when we call the
        // `clone` method on a shape the program checks its real
        // class and runs the appropriate clone method defined
        // in that class. That's why we get proper clones
        // instead of a set of simple Shape objects.
        foreach (s in shapes) do
            shapesCopy.add(s.clone())

        // The `shapesCopy` array contains exact copies of the
        // `shape` array's children.

قابلیت‌ها و کاربرد‌ها

نحوه پیاده‌سازی

  1. اینترفیس prototype را ایجاد کنید و متد clone را در آن اعلان کنید. یا فقط متد را به تمام کلاس‌های سلسله مراتب کلاس موجود اضافه کنید، این در حالتی است اگر فقط یک مورد دارید.
  2. یک کلاس prototype باید سازنده جایگزینی را تعریف کند که شیء آن کلاس را به عنوان آرگومان می‌پذیرد. سازنده باید مقادیر تمام فیلدهای تعریف شده در کلاس را از شی ارسال شده متعلق به prototype کپی کند. اگر در حال تغییر یک کلاس فرعی هستید، باید سازنده والد را فراخوانی کنید تا به ابرکلاس(Superclass) اجازه دهید کلونینگ فیلدهای خصوصی خود را انجام دهد. اگر زبان برنامه نویسی شما از overloading متد پشتیبانی نمی‌کند، نمی‌توانید یک سازنده «prototype» جداگانه ایجاد کنید. بنابراین کپی کردن داده‌های شی در کلون جدید ایجاد شده، باید در متد clone انجام شود. با این حال، داشتن این کد در یک سازنده معمولی امن‌تر است، زیرا شی به‌دست‌آمده دقیقاً پس از فراخوانی با اپراتور new، کاملاً پیکربندی شده برگردانده می‌شود.
  3. روش شبیه‌سازی معمولاً از در یک خط واحد نوشته می‌شود: اجرای یک اپراتور new با نسخه prototype سازنده. توجه داشته باشید که هر کلاس باید صراحتاً متد شبیه‌سازی را بازنویسی کند و از نام کلاس خود همراه با پراتور new استفاده کند. در غیر این صورت، روش شبیه‌سازی ممکن است یک شی از یک کلاس والد تولید کند.
  4. به صورت اختیاری، یک رجیستری prototype مرکزی برای ذخیره کاتالوگ نمونه‌های اولیه پر کاربرد ایجاد کنید. می‌توانید رجیستری را به‌عنوان یک کلاس Factory جدید پیاده‌سازی کنید یا آن را با یک متد استاتیک برای واکشی نمونه اولیه در کلاس نمونه اولیه قرار دهید. این روش باید یک نمونه اولیه را بر اساس معیارهای درخواستی جستجو کند که کد مشتری به متد ارسال می‌کند. معیارها ممکن است یک تگ رشته ساده یا مجموعه پیچیده‌ای از پارامترهای جستجو باشد. پس از یافتن نمونه اولیه مناسب، رجیستری باید آن را شبیه‌سازی کند و کپی را به مشتری برگرداند. در نهایت، فراخوانی‌های مستقیم سازنده‌های کلاس‌های فرعی را با فراخوانی به روش factory رجیستری نمونه جایگزین کنید.

مزایا و معایب الگوی Prototype

این الگوی طراحی دارای مزایا و معایبی به شرح زیر است:

– مزایا

– معایب

ارتباط با الگوهای دیگر

نمونه کد الگوی Prototype

2 پاسخ

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *