بررسی الگوی Chain of Responsibility در جاوا اسکریپت
اندازه‌ی قلم متن
تخمین مدت زمان مطالعه‌ی مطلب: شش دقیقه

الگوی Chain of Responsibility، یک زنجیر، از اشیاء متصل شده‌ی به هم را فراهم می‌کند که یکی از آنها می‌تواند درخواست رسیده را راضی کند؛ به عبارتی دیگر به محض دریافت درخواست، آن را پردازش می‌کند. این الگو اساسا یک جستجوی خطی ( linear search )، برای یک شیء می‌باشد که می‌تواند یک درخواست ویژه را handle کند. 

الگوی chain-of-responsibility، ارتباط با الگوی Chaining دارد که به دفعات در جاوا اسکریپت استفاده شده‌است (jQuery  استفاده‌ی گسترده‌ای از این الگو کرده‌است).
این الگو اجازه میدهد که یک درخواست ارسال شده‌ی توسط کلاینت، توسط یک یا بیش از یک object، دریافت شود. یک مثال عمومی از این الگو،  event bubbling در DOM  می‌باشد. یک رویداد از طریق element‌‌‌های تودرتوی متفاوت انتشار پیدا می‌کند؛ تا زمانیکه یکی از آن‌ها، آن را handle کند.


مثال زیر را در نظر بگیرید:
class HandlerChain 
{ 
   setNextObj(nextObjInChain){}
   processMultiple(req){
     console.log("No multiple for: " + req.getMultiple());
   }
} 
  
class Multiple
{
  constructor(multiple){
    this.multiple = multiple;
  }
  
  getMultiple(){ 
    return this.multiple; 
  } 
  
} 
  

class MultipleofTwoHandler extends HandlerChain
{
  constructor(){
    super()
    this.nextObjInChain = new HandlerChain()
  } 
  
  setNextObj(nextObj){ 
    this.nextObjInChain = nextObj; 
  } 
  
  processMultiple(req) { 
    if ((req.getMultiple() % 2) == 0) { 
      console.log("Multiple of 2: " + req.getMultiple()); 
    }else{ 
      this.nextObjInChain.processMultiple(req); 
    } 
  } 
} 
  
class MultipleofThreeHandler extends HandlerChain 
{ 
  constructor(){
    super()
    this.nextObjInChain = new HandlerChain()
  } 
  
   setNextObj(nextObj){ 
    this.nextObjInChain = nextObj; 
  } 
  
  processMultiple(req) 
  { 
    if ((req.getMultiple() % 3) == 0) { 
      console.log("Multiple of 3: " + req.getMultiple()); 
    }else{ 
          this.nextObjInChain.processMultiple(req); 
        } 
    } 
} 


class MultipleofFiveHandler extends HandlerChain
{ 
  constructor(){
    super()
    this.nextObjInChain = new HandlerChain()
  }
  
  setNextObj(nextObj){ 
    this.nextObjInChain = nextObj; 
  } 
  
  processMultiple(req) { 
    if ((req.getMultiple() % 5) == 0) { 
      console.log("Multiple of 5: " + req.getMultiple()); 
    }else{ 
      this.nextObjInChain.processMultiple(req); 
      } 
    } 
} 

//configuring the chain of handler objects
var c1 = new MultipleofTwoHandler(); 
var c2 = new MultipleofThreeHandler(); 
var c3 = new MultipleofFiveHandler(); 
c1.setNextObj(c2); 
c2.setNextObj(c3); 
  
//the chain handling different cases
c1.processMultiple(new Multiple(95));  // Multiple of 5: 95  
c1.processMultiple(new Multiple(50));  // Multiple of 2: 50 
c1.processMultiple(new Multiple(9));   // Multiple of 3: 9 
c1.processMultiple(new Multiple(4));   // Multiple of 2: 4 
c1.processMultiple(new Multiple(21));  // Multiple of 3: 21 
c1.processMultiple(new Multiple(23));  // No multiple for: 23  
مثال بالا، الگوی chain of responsibility را پیاده سازی می‌کند و بررسی می‌کند که آیا عدد وارد شده، مضربی از 2، 3 یا 5 است و یا نه.
چگونه می‌توانیم این قابلیت را پیاده سازی کنیم؟ ما می‌خواهیم که یک عدد داشته باشیم و سپس به handler‌ها در زنجیره اجازه بدهیم که تصمیم گیری کنند که آیا آنها می‌خواهند عدد وارد شده را پردازش کنند، یا به handler بعدی پاس بدهند.

ما 3 نوع handler در این زنجیره داریم:

  • MultipleofTwoHandler: بررسی می‌کند که آیا عدد وارد شده، مضربی از 2 است یا نه. 
  • MultipleofThreeHandler: بررسی می‌کند که آیا عدد وارد شده، مضربی از 3 است یا نه 
  • MultipleofFiveHandler: بررسی می‌کند که آیا عدد وارد شده، مضربی از 5 است یا نه. 

اولین قدم، ایجاد یک زنجیره از handler‌های بالا می‌باشد. در اینجا یک کلاس را به نام HandlerChain، برای همین منظور داریم و این کلاس شامل دو تابع، به نام‌های setNextObj و processMultiple می‌باشد.
class HandlerChain 
{ 
   setNextObj(nextObjInChain){}
   processMultiple(req){
     console.log("No multiple for: " + req.getMultiple());
   }
}

پیاده سازی پیش فرض processMultiple، زمانی اجرا می‌شود که هیچ مضربی برای یک عدد، وجود نداشته باشد. به عبارت دیگر، عدد مورد نظر، مضربی از 2،3 و 5 نباشد ( از بین مضرب‌های تعین شده). 
تمام handler‌ها در این زنجیره، از این کلاس ارث بری می‌کنند. هر handler می‌تواند دو عملیات را انجام دهد:

  1. تنظیم کردن handler بعدی در زنجیره 
  2. پردازش عدد وارد شده به این منظور که آیا در مضرب مورد نظر قرار دارد یا نه. 

class MultipleofTwoHandler extends HandlerChain
{ 
  constructor(){/*code*/}
  setNextObj(nextObj){/*code*/}  
  processMultiple(req){/*code*/}
}
  
class MultipleofThreeHandler extends HandlerChain
{
  constructor(){/*code*/}
  setNextObj(nextObj){/*code*/}  
  processMultiple(req){/*code*/}
}


class MultipleofFiveHandler extends HandlerChain
{
  constructor(){/*code*/}
  setNextObj(nextObj){/*code*/}  
  processMultiple(req){/*code*/}
}

constructor برای هر handler، همانند زیر تعریف شده‌است:
constructor(){
    super()
    this.nextObjInChain = new HandlerChain()
}

در ابتدا توسط  super ،  مقدار دهی اولیه‌ای برای متد‌های setNextObj و processMultiple از کلاس پدر انجام میشود. همچنین در constructor، متغیر nextObjInChain
مقدار دهی اولیه می‌شود که تعیین کننده‌ی شیء بعدی در زنجیره می‌باشد. 
 
 چگونه شیء جاری، شیء بعد را در زنجیره تعیین می‌کند؟ اجازه بدهید برای این منظور نگاهی به تابع setNextObj داشته باشیم. setNextObj در هر handler، همانند زیر تعیین شده‌است: 
setNextObj(nextObj){ 
    this.nextObjInChain = nextObj; 
}

اکنون می‌توانیم زنجیره‌ای از handler‌ها را همانند زیر ایجاد کنیم: 
var c1 = new MultipleofTwoHandler(); 
var c2 = new MultipleofThreeHandler(); 
var c3 = new MultipleofFiveHandler(); 
c1.setNextObj(c2); 
c2.setNextObj(c3);

در اینجا ما handler‌‌‌های c2, c1  و c3 را به منظور پردازش کردن مضرب‌های 2، 3 و 5 ایجاد کرده‌ایم. آن‌ها به یکدیگر به حالت زیر متصل شده‌اند:
 c2 به عنوان handler بعدی برای c1 و c3 به عنوان handler بعدی برای c2. 


اکنون که زنجیره ایجاد شده‌است، زمان آن است که عدد وارد شده را پردازش کنیم. اجازه بدهید که کار را با ایجاد کردن شیء “multiple”  با استفاده از کلاس Multiple، شروع کنیم.

class Multiple
{
  constructor(multiple){
    this.multiple = multiple
  }
  
  getMultiple(){ 
    return this.multiple; 
  }  
}

یک شیء Multiple، شامل یک خصوصیت به نام multiple و یک متد است به نام getMultiple که به منظور برگشت دادن multiple می‌باشد.
 
MultipleofTwoHandler به صورت زیر تعریف شده‌است:
processMultiple(req) { 
    if ((req.getMultiple() % 2) == 0) { 
      console.log("Multiple of 2: " + req.getMultiple()); 
    }else{ 
      this.nextObjInChain.processMultiple(req); 
    } 
}

در اینجا، یک multiple دریافت شده و چک می‌شود که آیا مضربی از 2 می‌باشد یا نه؛ اگر چنین است، سپس عدد دریافت شده را به عنوان مضربی از 2 نمایش می‌دهد. در صورتیکه مضربی از 2 نباشد، handler آن را به شیء بعدی در زنجیره، پاس میدهد و سپس همین روال دوباره تکرار می‌شود و تا زمانیکه یکی از handler ‌ها، جواب را برگشت دهد، این روال ادامه پیدا می‌کند. 
 
تعریف تابع processMultiple  برای هر 3 handler یکسان میباشد؛ با این تفاوت که MultipleofThreeHandler، برای بررسی مضربی از 3 بودن و MultipleofFiveHandler  برای بررسی مضربی از 5 بودن است.

اجازه بدهید یک مثال بزنیم و ببینیم که چه اتفاقی می‌افتد:
c1.processMultiple(new Multiple(95)) // Multiple of 5: 95  

اولین handler در زنجیره که c1 است، یک multiple (95) را دریافت می‌کند و چک می‌کند که آیا مضربی از 2 است یا نه. جواب خیر است؛ بنابراین multiple به دومین handler  در زنجیره پاس داده میشود؛ یعنی به handler بررسی کننده‌ی مضربی از 3 . در اینجا دوباره بررسی میشود که آیا عدد مورد نظر، مضربی از 3 است یا نه؟ که جواب خیر است. در ادامه multiple به handler سوم در زنجیره که بررسی کننده‌ی مضربی از 5 است، پاس داده میشود. در اینجا، شرط درست است و عدد 95 به عنوان مضربی از 5 چاپ میشود. 


چه زمانی از این الگو استفاده کنیم؟
به منظور مدیریت کردن انواع درخواست‌ها، به روش‌های متفاوت، بدون دانستن ترتیب و نوع درخواست از قبل، از این الگو استفاده می‌کنیم. این الگو به ما اجازه می‌دهد که زنجیره‌ای از چند handler را داشته باشیم. تمامی handler‌ها، یک شانس را جهت پردازش درخواست دارند.