أهلاً! اليوم سوف نتعمق في تفاصيل أنماط التصميم المختلفة، بدءًا من نمط Java Singleton. دعونا نراجع: ماذا نعرف عن أنماط التصميم بشكل عام؟ تعد أنماط التصميم من أفضل الممارسات التي يمكننا تطبيقها لحل عدد من المشكلات المعروفة. لا ترتبط أنماط التصميم بشكل عام بأي لغة برمجة. فكر فيها كمجموعة من التوصيات لمساعدتك على تجنب الأخطاء وتجنب إعادة اختراع العجلة.أنماط التصميم: مفردة - 1

ما هو المفرد في جاوة؟

يعد Singleton واحدًا من أبسط أنماط التصميم على مستوى الفصل الدراسي. في بعض الأحيان يقول الناس "هذه الفئة مفردة"، مما يعني أن الفئة تطبق نمط التصميم المفرد. في بعض الأحيان يكون من الضروري كتابة فئة حيث نقيد إنشاء مثيل لكائن واحد. على سبيل المثال، فئة مسؤولة عن تسجيل الدخول أو الاتصال بـ قاعدة البيانات. يصف نمط التصميم المفرد كيف يمكننا تحقيق ذلك. المفرد هو نمط تصميم يقوم بأمرين:
  1. إنه يضمن أنه لن يكون هناك سوى مثيل واحد للفئة.

  2. فهو يوفر نقطة واحدة للوصول العالمي إلى هذا المثيل.

وبالتالي، هناك ميزتان مميزتان لكل تطبيق للنمط المفرد تقريبًا:
  1. منشئ خاص. وهذا يحد من القدرة على إنشاء كائنات للفئة خارج الفئة نفسها.

  2. طريقة ثابتة عامة تقوم بإرجاع مثيل الفئة. تسمى هذه الطريقة getInstance . هذه هي نقطة الوصول الشامل إلى مثيل الفصل.

خيارات التنفيذ

يتم تطبيق نمط التصميم المفرد بطرق مختلفة. كل خيار جيد وسيئ بطريقته الخاصة. وكما هو الحال دائمًا، لا يوجد خيار مثالي هنا، ولكن يجب علينا أن نسعى جاهدين لتحقيقه. أولاً، دعونا نقرر ما الذي يشكل الجيد والسيئ، وما هي المقاييس التي تؤثر على كيفية تقييمنا للتطبيقات المختلفة لنمط التصميم. لنبدأ بالخير. فيما يلي العوامل التي تجعل التنفيذ أكثر جاذبية وجاذبية:
  • التهيئة البطيئة: لا يتم إنشاء المثيل إلا عند الحاجة إليه.

  • رمز بسيط وشفاف: هذا المقياس، بالطبع، ذاتي، لكنه مهم.

  • سلامة الخيط: التشغيل الصحيح في بيئة متعددة الخيوط.

  • أداء عالٍ في بيئة متعددة الخيوط: حظر بسيط أو معدوم لسلسلة المحادثات عند مشاركة المورد.

الآن السلبيات. سنقوم بإدراج العوامل التي تضع التنفيذ في ضوء سيء:
  • لا توجد تهيئة كسولة: عندما يتم تحميل الفصل عند بدء تشغيل التطبيق، بغض النظر عما إذا كانت هناك حاجة إليه أم لا (من المفارقة أنه من الأفضل أن تكون كسولًا في عالم تكنولوجيا المعلومات)

  • رمز معقد ويصعب قراءته. هذا المقياس هو أيضا ذاتي. إذا بدأت عيناك تنزف، فسنفترض أن التنفيذ ليس هو الأفضل.

  • عدم سلامة الخيط. وبعبارة أخرى، "خطر الخيط". عملية غير صحيحة في بيئة متعددة الخيوط.

  • أداء ضعيف في بيئة متعددة الخيوط: تحظر سلاسل العمليات بعضها البعض طوال الوقت أو في كثير من الأحيان عند مشاركة المورد.

شفرة

نحن الآن جاهزون للنظر في خيارات التنفيذ المختلفة والإشارة إلى الإيجابيات والسلبيات:

بسيط

public class Singleton {
    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {
    }

    public static Singleton getInstance() {
        return INSTANCE;
    }
}
أبسط التنفيذ. الايجابيات:
  • كود بسيط وشفاف

  • سلامة الخيط

  • أداء عالي في بيئة متعددة الخيوط

سلبيات:
  • لا التهيئة البطيئة.
وفي محاولة لإصلاح الخلل السابق، حصلنا على التنفيذ رقم اثنين:

التهيئة البطيئة

public class Singleton {
  private static final Singleton INSTANCE;

  private Singleton() {}

  public static Singleton getInstance() {
    if (INSTANCE == null) {
      INSTANCE = new Singleton();
    }
    return INSTANCE;
  }
}
الايجابيات:
  • التهيئة البطيئة.

سلبيات:
  • ليست آمنة للخيط

هذا التنفيذ مثير للاهتمام. يمكننا التهيئة بتكاسل، لكننا فقدنا سلامة الخيط. لا داعي للقلق، فنحن نقوم بمزامنة كل شيء في التنفيذ رقم ثلاثة.

الوصول المتزامن

public class Singleton {
  private static final Singleton INSTANCE;

  private Singleton() {
  }

  public static synchronized Singleton getInstance() {
    if (INSTANCE == null) {
      INSTANCE = new Singleton();
    }
    return INSTANCE;
  }
}
الايجابيات:
  • التهيئة البطيئة.

  • سلامة الخيط

سلبيات:
  • أداء ضعيف متعدد الخيوط

ممتاز! في التنفيذ رقم ثلاثة، نستعيد سلامة الخيط! بالطبع، إنه بطيء... تتم الآن مزامنة طريقة getInstance ، بحيث يمكن تنفيذها بواسطة مؤشر ترابط واحد فقط في المرة الواحدة. بدلاً من مزامنة الطريقة بأكملها، نحتاج في الواقع فقط إلى مزامنة الجزء الذي يقوم بتهيئة المثيل الجديد. لكن لا يمكننا ببساطة استخدام كتلة متزامنة لتغليف الجزء المسؤول عن إنشاء المثيل الجديد. القيام بذلك لن يضمن سلامة الخيط. الأمر أكثر تعقيدًا بعض الشيء. يمكن رؤية المزامنة المناسبة أدناه:

قفل مزدوج التحقق

public class Singleton {
    private static final Singleton INSTANCE;

  private Singleton() {
  }

    public static Singleton getInstance() {
        if (INSTANCE == null) {
            synchronized (Singleton.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Singleton();
                }
            }
        }
        return INSTANCE;
    }
}
الايجابيات:
  • التهيئة البطيئة.

  • سلامة الخيط

  • أداء عالي في بيئة متعددة الخيوط

سلبيات:
  • غير مدعوم في الإصدارات السابقة من Java الأقل من 1.5 (تم إصلاح استخدام الكلمات الأساسية المتقلبة منذ الإصدار 1.5)

لاحظ أنه لكي يعمل خيار التنفيذ هذا بشكل صحيح، يجب استيفاء أحد الشرطين. يجب أن يكون المتغير INSTANCE نهائيًا أو متقلبًا . التنفيذ الأخير الذي سنناقشه اليوم هو المفرد الخاص بصاحب الفصل .

صاحب الصف

public class Singleton {

   private Singleton() {
   }

   private static class SingletonHolder {
       public static final Singleton HOLDER_INSTANCE = new Singleton();
   }

   public static Singleton getInstance() {
       return SingletonHolder.HOLDER_INSTANCE;
   }
}
الايجابيات:
  • التهيئة البطيئة.

  • سلامة الخيط.

  • أداء عالي في بيئة متعددة الخيوط.

سلبيات:
  • تتطلب العملية الصحيحة ضمانًا بتهيئة الكائن المفرد بدون أخطاء. بخلاف ذلك، سيؤدي الاستدعاء الأول للأسلوب getInstance إلى ظهور الخطأ ExceptionInInitializerError ، وستنتج جميع الاستدعاءات اللاحقة الخطأ NoClassDefFoundError .

هذا التنفيذ يكاد يكون مثاليا. إنه كسول وآمن وسريع. ولكن لديها فارق بسيط، كما هو موضح في قائمة السلبيات. مقارنة التطبيقات المختلفة للنمط المفرد:
تطبيق التهيئة البطيئة سلامة الخيط أداء متعدد الخيوط متى يجب استخدام؟
بسيط - + سريع أبداً. أو ربما عندما لا تكون التهيئة البطيئة مهمة. ولكن لن يكون أفضل أبدا.
التهيئة البطيئة + - غير قابل للتطبيق دائمًا عندما لا تكون هناك حاجة إلى تعدد العمليات
الوصول المتزامن + + بطيء أبداً. أو ربما عندما لا يهم الأداء متعدد الخيوط. ولكن لن يكون أفضل أبدا.
قفل مزدوج التحقق + + سريع في حالات نادرة عندما تحتاج إلى معالجة الاستثناءات عند إنشاء المفرد (عندما لا يكون المفرد لصاحب الفئة قابلاً للتطبيق)
صاحب الصف + + سريع عندما تكون هناك حاجة إلى تعدد مؤشرات الترابط ويكون هناك ضمان بأنه سيتم إنشاء الكائن المفرد دون مشاكل.

إيجابيات وسلبيات النمط المفرد

بشكل عام، يقوم الفرد المنفرد بما هو متوقع منه بالضبط:
  1. إنه يضمن أنه لن يكون هناك سوى مثيل واحد للفئة.

  2. فهو يوفر نقطة واحدة للوصول العالمي إلى هذا المثيل.

ومع ذلك، فإن هذا النمط له عيوب:
  1. تنتهك الفئة الفردية مبدأ المسؤولية الفردية: بالإضافة إلى واجباتها المباشرة، تتحكم الفئة الفردية أيضًا في عدد الحالات.

  2. إن اعتماد الفصل العادي على المفرد غير مرئي في العقد العام للفئة.

  3. المتغيرات العالمية سيئة. في نهاية المطاف، يتحول المفرد إلى متغير عالمي ضخم.

  4. يؤدي وجود المفرد إلى تقليل قابلية اختبار التطبيق ككل والفئات التي تستخدم المفرد على وجه الخصوص.

وهذا كل شيء! :) لقد استكشفنا فئة Java Singleton معك. الآن، لبقية حياتك، عند التحدث مع أصدقائك المبرمجين، يمكنك أن تذكر ليس فقط مدى جودة هذا النمط، ولكن أيضًا بضع كلمات حول ما يجعله سيئًا. حظا سعيدا في إتقان هذه المعرفة الجديدة.

قراءة إضافية: