الگوی متد قالبی

در برنامه‌نویسی شئ‌گرا، الگوی متد قالبی یکی از الگوهای طراحی رفتاری است که توسط اریک گاما و همکارانش تعریف شده‌است. در کتاب Design Patterns، متد قالبی، یک متد در یک ابرکلاس (Superclass) (معمولاً از نوع انتزاعی Abstract)، می‌باشد. این متد اسکلت یک عملیات را بر اساس تعدادی از مراحل سطح بالا تعریف می‌کند. این مراحل خود توسط متدهای کمکی اضافی در همان کلاس متد قالبی پیاده‌سازی می‌شوند.

متدهای کمکی ممکن است از نوع انتزاعی باشند، که در این حالت برای پیاده‌سازی تجریدی (Concrete) و واقعی آن‌ها نیاز است به یک زیرکلاس (Subclass) یا به متدهای قلاب (Hook methods)، که متدهایی با بدنهٔ خالی در کلاس پدر هستند. زیرکلاس‌ها می‌توانند با Override کردن متدهای Hook، عملیات مختص خود را شخصی‌سازی کنند. هدف متد قالبی تعریف ساختار کلی عملیات است، در عین این که به زیرکلاس‌ها اجازه بدهد مراحل خاصی از عملیات را اصلاح یا دوباره تعریف کنند.

بررسی اجمالی 
این الگو دارای دو قسمت اصلی است:
 «متد قالبی» به عنوان یک متد در یک کلاس پایه (Base Class) (معمولاً یک کلاس انتزاعی) پیاده‌سازی می‌شود. این متد شامل کدی برای قسمت‌هایی از الگوریتم کلی برنامه است که ثابت هستند. این الگو تضمین می‌کند که الگوریتم اصلی برنامه همیشه دنبال می‌شود. در متد قالبی، بخش‌هایی از الگوریتم که ممکن است متفاوت باشند با ارسال پیام‌های شخصی که درخواست اجرای متدهای کمکی اضافی را دارند، پیاده‌سازی می‌شوند. در کلاس پایه، این متدهای کمکی یا به‌طور پیش‌فرض دارای پیاده‌سازی هستند یا اصلاً پیاده‌سازی ندارند (به عبارت دیگر متدهای انتزاعی هستند).
 زیرکلاس‌های (Subclasses) کلاس پایه بخش‌های خالی یا «متغیر» متد «قالب» را با الگوریتم‌هایی مختص خود «پر می‌کنند». این نکته که زیرکلاس‌ها خود متد قالبی را Override نمی‌کنند، حائز اهمیت است.

در زمان اجرا، الگوریتم متد قالبی با ارسال پیام قالب به شئ‌ای از یکی از زیرکلاس‌های مجرد (Concrete - پیاده‌سازی شده) اجرا می‌شود. از طریق وراثت، متد قالبی در کلاس پایه شروع به اجرا شدن می‌کند. هنگامی که متد قالبی پیامی ر به خودا برای درخواست اجرای یکی از متد کمکی بفرستد، پیام توسط شئ‌های آن Subclass دریافت می‌شود. اگر متد کمکی Override شده باشد، پیاده‌سازی Override شده در آن زیرکلاس اجرا می‌شود. اما اگر Override نشده باشد، پیاده‌سازی به ارث رسیده از کلاس پایه اجرا می‌شود. این مکانیزم اطمینان حاصل می‌کند که الگوریتم کلی هر بار مراحل یکسانی را دنبال می‌کند، در حالی که اجازه می‌دهد جزئیات برخی مراحل بستگی به این دارد که درخواست اصلی اجرای الگوریتم را دریافت کرده‌است.

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

برخی از پیام‌های متد قالبی که به خود ارسال شده‌اند، ممکن است برای قلاب کردن متدها (Hooking Methods) باشد. این متدها در همان کلاس پایهٔ متد قالبی پیاده‌سازی می‌شوند، اما با بدنهٔ خالی (به عبارت دیگر هیچ کاری نمی‌کنند). متدهای هوک وجود دارند تا زیرکلاس‌ها بتوانند آنها را Override کنند و بنابراین می‌توانند عملکرد الگوریتم برنامه را بدون نیاز به Override کردن خود متد قالبی، تنظیم کنند (و تغییر دهند). به عبارت دیگر، آنها یک «قلاب» فراهم می‌کنند که روی آن می‌توان پیاده‌سازی‌های مختلف را «آویزان» کرد.

ساختار

نمودار کلاس UML 

در نمودار کلاس UML فوق، کلاس ()AbstractClass متد templateMethod را تعریف می‌کند که این متد، اسکلت (قالب) یک رفتار را با موارد زیر تعریف می‌کند.
 پیاده‌سازی قسمت‌های ثابت رفتار، و
 ارسال پیام‌های ()primitive1 و ()primitive2، به خود، که از آن جایی که این متدها در SubClass1 پیاده‌سازی شده‌اند، این موضوع اجازه می‌دهد که آن زیرکلاس یک پیاده‌سازی متفاوت از آن قسمت‌های الگوریتم داشته باشد.

استفاده 
الگوی متد قالبی در فریم‌ورک‌ها استفاده می‌شود، به طوری که هر کدام یکی از قسمت‌های ثابت معماری دامنه را پیاده‌سازی می‌کند، و متدهای hook را برای شخصی‌سازی ارائه می‌دهد. این نمونه ای از وارونگی کنترل است. از الگوی متد قالبی به دلایل زیر استفاده می‌شود.
 این الگو اجازه می‌دهد تا هر یک زیرکلاس‌ها رفتار متفاوتی را پیاده‌سازی کنند (از طریق Override کردن متدهای hook).
 این الگو از تکرار در کد جلوگیری می‌کند: گردش کار کلی الگوریتم برنامه یک بار در متد قالبی کلاس انتزاعی (Abstract Class) اجرا شده و در زیر کلاس‌ها تنها تغییرات لازم اعمال می‌شوند.
 این الگو قسمت (های) مجاز به Specialization را کنترل می‌کند. اگر زیرکلاس‌ها اجازه داشتند که به راحتی متد قالبی را Override کنند، می‌توانستند تغییرات اساسی و دلخواهی را در گردش کار برنامه ایجاد کنند. اما در مقابل، فقط با Override کردن متدهای hook، تنها جزئیات خاصی از گردش کار برنامه را می‌توان تغییر داد، و گردش کار کلی برنامه دست‌نخورده باقی خواهد ماند.

استفاده به همراه مولدهای کد (Code Generators) 
الگوی قالبی هنگام کار با کدهای خودکار تولید شده مفید است. چالش کار با کد تولید شده این است که تغییر در کد منبع (Source Code) منجر به تغییر در کد تولید شده می‌شود. اگر تغییراتی دستی در کد تولید شده ایجاد شده باشد، این موارد از بین می‌روند؛ بنابراین، کد تولید شده چگونه باید شخصی‌سازی شود و تغییر یابد؟

الگوی متد قالبی یک راه حل ارائه می‌دهد. اگر کد تولید شده از الگوی متد قالبی پیروی کند، کد تولید شده همه یک ابرکلاس انتزاعی (Abstract Superclass) خواهد بود. در صورتی که تغییرات دستی ایجاد شده به یک زیرکلاس (Subclass) محدود شوند، می‌توان مولد کد را مجدداً اجرا کرد بدون این که خطر از بین رفتن تغییرات وجود داشته باشد. از این الگو زمانی که به همراه تولید کد خودکار (Code Generators) استفاده می‌شود، بعضاً به عنوان الگوی شکاف تولید (Generaton Gap Pattern) یاد می‌شود.

مثال PHP 
abstract class Game
{
    abstract protected function initialize();
    abstract protected function startPlay();
    abstract protected function endPlay();

    /** Template method */
    public final function play()
    {
        /** Primitive */
        $this->initialize();

        /** Primitive */
        $this->startPlay();

        /** Primitive */
        $this->endPlay();
    }
}

class Mario extends Game
{
    protected function initialize()
    {
        echo "Mario Game Initialized! Start playing.", PHP_EOL;
    }

    protected function startPlay()
    {
        echo "Mario Game Started. Enjoy the game!", PHP_EOL;
    }

    protected function endPlay()
    {
        echo "Mario Game Finished!", PHP_EOL;
    }

}

class Tankfight extends Game
{
    protected function initialize()
    {
        echo "Tankfight Game Initialized! Start playing.", PHP_EOL;
    }

    protected function startPlay()
    {
        echo "Tankfight Game Started. Enjoy the game!", PHP_EOL;
    }

    protected function endPlay()
    {
        echo "Tankfight Game Finished!", PHP_EOL;
    }

}

$game = new Tankfight();
$game->play();

$game = new Mario();
$game->play();

جستارهای وابسته 
 وراثت (علوم کامپیوتر)
 غلبه بر روش (برنامه‌نویسی)
 GRASP (طراح شی گرا)
 الگوی آداپتور
 الگوی استراتژی

منابع

پیوند به بیرون 
 شش کاربرد معمول از الگوی قالبی
 کار با کلاس‌های قالبی در PHP 5 
 الگوی طراحی متد قالبی

الگوهای طراحی نرم‌افزار
شگرد (برنامه‌نویسی رایانه‌ای)