پیاده سازی singleton در جاوااسکریپت

ترجمه و تالیف : محمدرضا مصلی
تاریخ انتشار : 22 شهریور 99
خواندن در 2 دقیقه
دسته بندی ها : جاوا اسکریپت

سوالی که همیشه با دیدن کلمه دیزاین پترن در بین توسعه دهنده‌ها به وجود می‌آید این است که چه زمانی باید از این الگو استفاده کنم؟ چرا باید استفاده کنم!؟ مثال‌های خیلی زیاد در برخی زبان‌ها از جمله: java ، csharp و.... که مفاهیم شی‌گرایی به طور تقریبا یا صد در صد در آن رعایت شده است وجود دارد ولی کمتر مثالی رو می‌توانید با javascript دیده باشید البته به خاطر این است که خیلی از مفاهیم شی‌گرایی اصلا در javascript وجود نداشته و بعدا به آن اضافه شده است.

اگر هنوز تسلط لازم در javascript را ندارید و یا با ویژگی‌های ecmascript 6,7,8 هنوز آشنا نشدید به شما دوره‌های زیر را پیشنهاد می‌کنم:

آموزش جاوا اسکریپت

آموزش جاوا اسکریپت ES6

هدف از این مقاله:

  1. singleton چیست؟
  2. چرا باید استفاده کنیم؟
  3. پیاده سازی singleton در javascript به صورت کاربردی

singleton چیست؟

singleton یکی از ساده‌ترین الگوهای طراحی است. این الگو به ما این تضمین را می‌دهد که تنها و فقط یک object از روی یک کلاس خواهیم داشت و فقط یک راه برای دسترسی به آن object در برنامه وجود دارد به مثال زیر توجه کنید:

new ErrorMessage("Resource not found", "The specified Resource does not exist", 404)

new ErrorMessage("Bad request", "Your request is invalid and the server is unable to respond", 400)

new ErrorMessage("Internal Server Error", "Request could not be carried out", 500)

در این سه خط ما هر بار برای ایجاد کردن یک خطا یک object از کلاس ErrorMessage را به وجود آوردیم تا خطایی جدید به وجود بیاوریم. طبق گفته بالا ما فقط باید یک object از کلاس را داشته باشیم، درسته تکه کدی که نوشتیم کار می‌کند ولی از قاعده و الگو خاصی پیروی نمی‌کند برای حل این مشکل چه باید کرد؟singleton را چگونه باید پیاده سازی کنیم!!؟

اول از همه بیایید یک نگاه دقیق‌تر به کلاس ErrorMessage بیاندازیم.

class ErrorMessage extends Error {
   constructor(name, message, status, properties, internalProperties) {
       super()
       this.name = name
       this.message = message
       this.status = status
       this.properties = properties
       this.internalProperties = internalProperties
   }

   static notFound(properties, internalProperties) {
       return new ErrorMessage(
           "Resource not found",
           "The specified Resource does not exist",
           404,
           properties,
           internalProperties
       )
   }

   static badRequest(properties, internalProperties) {
       return new ErrorMessage(
           "Bad request",
           "Your request is invalid and the server is unable to respond",
           400,
           properties,
           internalProperties
       )
   }

   static serverError(properties, internalProperties) {
       return new ErrorMessage(
           "Internal Server Error",
           "Request could not be carried out",
           500,
           properties,
           internalProperties
       )
   }
}

همانطور که می‌بینید کلاس ErrorMessage از Error که یکی از کلاس‌های داخلی javascript است ارث‌بری می‌کند. در اینجا یک متد سازنده (constructor) با چند متد static که هر کدام در نهایت یک object از کلاس ErrorMessage را بر می‌گردانند‌، وجود دارد.

اگر دقت کنید می‌بینید برای دسترسی به متد‌های static نیازی به new کردن یا ساخت object نیست ولی در هر صورت خود static متد‌ها یک object به ما بر می‌گردانند، چاره چیست!؟ از شر constructor خلاص شوید.

سوال: بدون constructor چگونه object را مقدار‌دهی کنیم؟

به یک متد static دیگر برای مقدار دهی object نیاز داریم به تکه کد زیر توجه کنید:

static setter(name, message, status, properties) {
        instance = new ErrorMessage();
        instance.name = name;
        instance.message = message;
        instance.status = status;
        instance.properties = properties;
        return instance;
}

اینجا برای مقداری دهی object یک متد static به وجود آوردیم تا مقدار دهی در این متد انجام شود ولی باز هم کافی نیست!! بزارید بیاییم از این متد در بقیه متد‌ها استفاده کنیم.

class ErrorMessage extends Error {
    static setter(name, message, status, properties) {
        const instance = new ErrorMessage();
        instance.name = name;
        instance.message = message;
        instance.status = status;
        instance.properties = properties;
        return instance;
    }

    static notFound(properties) {
        return this.setter("Resource not found", "The specified Resource does not exist", 404, properties);
    }

    static badRequest(properties) {
        return this.setter("Bad request", "Your request is invalid and the server is unable to respond", 400, properties);
    }

    static serverError(properties) {
        return this.setter("Internal Server Error", "Request could not be carried out", 500, properties);
    }

}

با وجود حذف constructor درسته همه مقدار‌دهی‌ها در متد setter انجام می‌شود ولی باز هم مشکل ما کامل رفع نشده است :| چرا؟ هنوز هم در متد setter با هر فراخوانی، یک object ساخته می‌شود.

چاره چیست؟ یک static متد دیگر، برای کنترل ساخت و ساز object از کلاس.

static getInstance() {
        if (!ErrorMessage.instance) {
            ErrorMessage.instance = new ErrorMessage();
        }
        return ErrorMessage.instance;
}

هدف از ساخت این متد، ساخت یک object از کلاس ErrorMessage برای همیشه است. وقتی متد getInstance فراخوانی می‌شود چه اتفاقی می‌افتد؟ با یک شرط ساده از ساخت object اضافی جلوگیری می‌کنیم و اگر object از کلاس ErrorMessage وجود نداشته باشد ساخته می‌شود.

 برای آشنایی بیشتر با دیزاین‌ پترن‌ها می‌توانید‌ از دوره آموزشی دیزاین پترنهای راکت استفاده کنید.

حال بیاییم کلاس ErrorMessage را بعد از این همه تغییر ببینیم:

class ErrorMessage extends Error {
    static getInstance() {
        if (!ErrorMessage.instance) {
            ErrorMessage.instance = new ErrorMessage();
        }
        return ErrorMessage.instance;
    }

    static setter(name, message, status, properties) {
        const instance = this.getInstance();
        instance.name = name;
        instance.message = message;
        instance.status = status;
        instance.properties = properties;
        return instance;
    }

    static notFound(properties) {
        return this.setter("Resource not found", "The specified Resource does not exist", 404, properties);
    }

    static badRequest(properties) {
        return this.setter("Bad request", "Your request is invalid and the server is unable to respond", 400, properties);
    }

    static serverError(properties) {
        return this.setter("Internal Server Error", "Request could not be carried out", 500, properties);
    }

}

حال دیگر خبری از ساختن object اضافی نیست و با استفاده از متد getInstance می‌توانیم به object دسترسی داشته باشیم و هم با متد setter آن را مقداردهی کنیم. فراخوانی آنها به صورت زیر انجام می‌شود:

ErrorMessage .setter("Unauthorized", "Authentication failed", 401)

ErrorMessage.notFound()

ErrorMessage.badRequest()
گردآوری و تالیف محمدرضا مصلی
آفلاین
user-avatar

حدود ۶ سالی هست که دارم برنامه نویسی میکنم و به دلیل علاقه زیادی که به زبان جاوا اسکریپت داشتم، به سمت تکنولوژی nodejs و فریم ورک های آن رفتم و همچنان در این حوزه فعالیت میکنم و دوست دارم تجربه خودم را با دیگران به اشتراک بگذارم.

دیدگاه‌ها و پرسش‌ها

برای ارسال نظر لازم است ابتدا وارد سایت شوید
در حال دریافت نظرات از سرور، لطفا منتظر بمانید