الگوی آذینگر

شرح مسئله

به عنوان مثال، یک پنجره را در سامانهٔ پنجره‌ای در نظر بگیرید. برای ایجاد قابلیت اسکرول کردن نوار نورد برای پنجره‌ها ممکن است بخواهیم نوار نورد افقی یا عمودی ایجاد کنیم. فرض کنید که به ازای هر پنجره یک شئ از کلاس Window داریم (این کلاس موجبات به نمایش درآمدن و عمل کردن پنجره را در برنامه فراهم می‌کند) و فرض کنید که این کلاس عملکردی برای افزودن نوار نورد ندارد. ممکن است برنامه‌نویسی برای افزودن این قابلیت به برنامه کلاسی با نام ScrollingWindow به وجود آورد که از کلاس Windows فرزند این کلاس است و از این کلاس ارث می‌برد، یا اینکه فرض کنید برنامه‌نویس به جای اینکار کلاسی با نام
ScrollingWindowDecorator می‌سازد که به اشیاء کلاس Window قابلیت جدید مربوط به نوار نبرد را می‌افزاید. همان‌طور که می‌بینیم، تا اینجای کار هر دو روش، صحیح و کارآمد هستند.
حالا، فرض کنید می‌خواهیم به پنجره‌ها حاشیه اضافه کنیم، در این موارد هم فرض کنید که کلاس Windows قابلیت اینکار را ندارد. حالا با طرح این قابلیت جدید روش ارث‌بری دچار مشکل می‌شود چرا که فرض کنید کسی می‌خواهد بعضی پنجره‌ها دارای حاشیه و دارای قابلیت اسکرول باشند و بعضی فقط دارای قابلیت اسکرول باشند و بعضی دیگر فقط دارای حاشیه باشند در اینصورت اگر بخواهیم از روش ارث‌بری استفاده کنیم باید کلاس‌های زیر را به بسازیم: WindowWithBorder و ScrollingWindowsWithBorder (هر دوی این کلاس‌ها فرزند کلاس Window هستند) در این صورت می‌توان پیش‌بینی کرد که با افزودن هر قابلیت جدید باید چندین زیر کلاس از کلاس Window بسازیم. برای حل این مشکل می‌توان به جای روش ارث‌بری از الگوی طراحی آذینگر استفاده کرد. در این روش به ازای هر قابلیت جدید (قابلیت اسکرول، قابلیت داشتن حاشیه و …) فقط لازم است، یک کلاس آذینگر درست کنیم. مثلاً در این مثال می‌توانیم دو کلاس آذینگر با نام‌های BorderedWindowDecorator و ScrollingWindowDecorator بسازیم و با استفاده از این دو کلاس در زماان اجرا اشیاء از کلاس Widnow را آذین کنیم. دقت کنید کنید که در صورتی که این قابلیت‌ها باید به تمام پنجره‌ها افزوده شود می‌توان، کلاس والد یعنی Window را تغییر داد تا با نیازهای ما همخوانی داشته باشد. از طرف دیگر گاهی (مثلاً زمانی که از چارچوب‌های (Framework) خارجی استفاده می‌کنیم)، تغییر و دست بردن در کلاس والد قانونی و راحت نیست، بنابراین در این مواقع استفاده از این الگوی طراحی می‌تواند کمک بزرگی به برنامه‌نویس باشد.
توجه داشته باشید که در مثال پنجره که آورده شد، کلاس SimpleWindow و دو کلاس آذینگر رابط window را که دارای دو اسلوب (متد) draw() و getDescription() است، پیاده‌سازی می‌کنند.

مثال اول (پنجره/نوار نبرد)

کلاس زیر نحوهٔ استفاده از آذینگر را در مثال پنجره/نوار نبرد، نشان می‌دهد.
// The Window interface classpublic interface Window {

    public void draw(); // برای کشیدن پنجره

    public String getDescription(); // رشته‌ای حاوی توضیح در مورد قابلیت‌ها پنجره را برمی‌گرداند}

    // پیاده‌سازی رابط فوق به صورت یک پنجرهٔ بدون نوار نبردclass SimpleWindow implements Window {

    public void draw() {

    // کدهای مربوط به کشیدن پنجره

    }

    public String getDescription() {

        return "simple window";

    }}

کلاس‌های زیر آذینگرهای کلاس Window و کلاس‌های آذینگر دیگر، هستند. 
// کلاس تجریدی آذینگر، دقت کنید که رابط ویندو را یپاده‌سازی می‌کندabstract class WindowDecorator implements Window {

    protected Window windowToBeDecorated; // the Window being decorated

    public WindowDecorator (Window windowToBeDecorated) {

        this.windowToBeDecorated = windowToBeDecorated;

    }

    public void draw() {

        windowToBeDecorated.draw(); //Delegation

    }

    public String getDescription() {

      return windowToBeDecorated.getDescription(); //Delegation

    }}// The first concrete decorator which adds vertical scrollbar functionalityclass VerticalScrollBarDecorator extends WindowDecorator {

    public VerticalScrollBarDecorator (Window windowToBeDecorated) {

        super(windowToBeDecorated);

    }

    @Override
    public void draw() {

        super.draw();

        drawVerticalScrollBar();

    }

    private void drawVerticalScrollBar() {

    // Draw the vertical scrollbar

    }

    @Override
    public String getDescription() {

        return super.getDescription() + ", including vertical scrollbars";

    }}// The second concrete decorator which adds horizontal scrollbar functionalityclass HorizontalScrollBarDecorator extends WindowDecorator {

    public HorizontalScrollBarDecorator (Window windowToBeDecorated) {

    super(windowToBeDecorated);

    }

    @Override
    public void draw() {

        super.draw();
        drawHorizontalScrollBar();

    }

    private void drawHorizontalScrollBar() {

    // Draw the horizontal scrollbar

    }

    @Override
    public String getDescription() {

        return super.getDescription() + ", including horizontal scrollbars";

    }}
در ذیل این مطلب برنامه‌ای آورده شده‌است که در آن یک نمونه (شئ) از کلاس SimpleWindow ساخته می‌شود و این کلاس با استفاده از کلاس‌های آذینگر با نام‌های HorizontalScrollBarDecorator و VerticalScrollBarDecorator آذین‌می‌شود.
public class DecoratedWindowTest {public static void main(String[] args) {// Create a decorated Window with horizontal and vertical scrollbarsWindow decoratedWindow = new HorizontalScrollBarDecorator (new VerticalScrollBarDecorator (new SimpleWindow()));// Print the Window's descriptionSystem.out.println(decoratedWindow.getDescription());}}

در زیر هم کلاس بررسی صحت با استفاده از Junit آورده شده‌است:
import static org.junit.Assert.assertEquals;import org.junit.Test;public class WindowDecoratorTest {@Testpublic void testWindowDecoratorTest() {Window decoratedWindow = new HorizontalScrollBarDecorator(new VerticalScrollbarDecorator(new SimpleWindow()));// assert that the description indeed includes horizontal + vertical scrollbarsassertEquals("simple window, including vertical scrollbars, including horizontal scrollbars", decoratedWindow.getDescription())}}

خروجی برنامه متن زیر است:
"simple window, including vertical scrollbars, including horizontal scrollbars"
به این نکته توجه کنید که متد (اسلوب) getDescription() از دو کلاس آذینگر که در مثال آمده‌اند، ابتدا getDescription() پنجرهٔ اصلی یعنی همان شئ از کلاس SimpleWindow را صدا می‌زند و پس از آن Description خود را پس از آن اضافه می‌کنند.