سؤال

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

يبدو أن Stack Overflow بشكل خاص يفترض أن الجميع متفقون على أن Singletons أشرار.لماذا؟

يرجى دعم إجاباتك بـ "الحقائق أو المراجع أو الخبرة المحددة"

هل كانت مفيدة؟

المحلول

إعادة صياغتها من بريان باتون:

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

  2. إنهم ينتهكون مبدأ المسؤولية الواحدة:بحكم حقيقة أنهم يتحكمون في خلقهم ودورة حياتهم.

  3. إنها تتسبب بطبيعتها في إحكام التعليمات البرمجية مقرونة.وهذا يجعل تزويرها تحت الاختبار أمرًا صعبًا في كثير من الحالات.

  4. أنها تحمل الحالة طوال عمر التطبيق.ضربة أخرى للاختبار حيث قد ينتهي بك الأمر إلى موقف حيث يلزم طلب الاختبارات وهو أمر لا ينطبق على اختبارات الوحدة.لماذا؟لأن كل وحدة اختبارية يجب أن تكون مستقلة عن الأخرى.

نصائح أخرى

تحل المفردات مشكلة واحدة (واحدة فقط).

التنافس على الموارد

إذا كان لديك بعض الموارد التي

(1) يمكن أن يكون له مثيل واحد فقط، و

(2) تحتاج إلى إدارة هذا المثيل الفردي،

أنت بحاجة إلى مفرد.

لا توجد أمثلة كثيرة.ملف السجل هو الملف الكبير.لا تريد التخلي عن ملف سجل واحد فقط.تريد مسحه ومزامنته وإغلاقه بشكل صحيح.هذا مثال لمورد مشترك واحد يجب إدارته.

من النادر أن تحتاج إلى مفردة.السبب في كونهم سيئين هو أنهم يشعرون وكأنهم عالمي وهم أعضاء مدفوع الأجر بالكامل في GoF أنماط التصميم كتاب.

عندما تعتقد أنك بحاجة إلى عالمي، فمن المحتمل أنك ترتكب خطأً فادحًا في التصميم.

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

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

لدى ميسكو هيفيري، من Google، بعض المقالات المثيرة للاهتمام حول هذا الموضوع بالضبط...

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

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

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

أعتقد أن سبب الارتباك هو حقيقة أن الناس لا يعرفون التطبيق الحقيقي لنمط سينجلتون.لا أستطيع أن أؤكد هذا بما فيه الكفاية.سينجلتون هو لا نمط للالتفاف الكرات العالمية.يجب استخدام نمط Singleton فقط لضمان ذلك مثيل واحد فقط من فئة معينة موجود أثناء وقت التشغيل.

يعتقد الناس أن سينجلتون شرير لأنهم يستخدمونه من أجل العوالم.وبسبب هذا الارتباك يتم النظر إلى سينجلتون بازدراء.من فضلك، لا تخلط بين Singletons وGlobals.إذا تم استخدامه للغرض المخصص له، فسوف تحصل على فوائد كبيرة من نمط Singleton.

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

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

لذلك في الأساس:

public MyConstructor(Singleton singleton) {
    this.singleton = singleton;
}

بدلا من:

public MyConstructor() {
    this.singleton = Singleton.getInstance();
}

وأعتقد أن هذا النوع من النمط يسمى حقن التبعية ويعتبر بشكل عام أمرا جيدا.

مثل أي نمط على الرغم من ...فكر في الأمر وفكر فيما إذا كان استخدامه في الموقف المحدد غير مناسب أم لا...القواعد وضعت ليتم كسرها عادة، و أنماط لا ينبغي أن تطبق طوعا أو كرها دون تفكير.

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

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

في تم جمع القمامة يمكن أن تصبح مفردات البيئة مشكلة بسرعة فيما يتعلق بإدارة الذاكرة.

يوجد أيضًا سيناريو متعدد الخيوط حيث يمكن أن تصبح المفردات عنق الزجاجة بالإضافة إلى مشكلة المزامنة.

يتم تنفيذ المفردة باستخدام طريقة ثابتة.يتم تجنب الأساليب الثابتة من قبل الأشخاص الذين يقومون باختبار الوحدة لأنه لا يمكن الاستهزاء بهم أو طعنهم.معظم الأشخاص في هذا الموقع هم من كبار المؤيدين لاختبار الوحدة.الاصطلاح الأكثر قبولًا بشكل عام لتجنبها هو استخدام قلب السيطرة نمط.

المفردات سيئة أيضًا عندما يتعلق الأمر بذلك تجمع.لأنه بعد ذلك، لم يعد لديك "مفرد واحد بالضبط" في طلبك بعد الآن.

خذ بعين الاعتبار الحالة التالية:كمطور، عليك إنشاء تطبيق ويب يمكنه الوصول إلى قاعدة البيانات.للتأكد من أن استدعاءات قاعدة البيانات المتزامنة لا تتعارض مع بعضها البعض، يمكنك إنشاء ملف حفظ SingletonDao:

public class SingletonDao {
    // songleton's static variable and getInstance() method etc. omitted
    public void writeXYZ(...){
        synchronized(...){
            // some database writing operations...
        }
    }
}

لذلك أنت متأكد من وجود مفردة واحدة فقط في تطبيقك وأن جميع قواعد البيانات تمر عبر هذه الوحدة فقط SingletonDao.تبدو بيئة الإنتاج الخاصة بك الآن كما يلي:Single Singleton

كل شيء على ما يرام حتى الآن.

الآن، ضع في اعتبارك أنك تريد إعداد مثيلات متعددة لتطبيق الويب الخاص بك في مجموعة.الآن، لديك فجأة شيء من هذا القبيل:

Many singletons

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

بالطبع هذا مثال على الاستخدام السيئ للمفردة.لكن رسالة هذا المثال هي:لا يمكنك الاعتماد على وجود مثيل واحد بالضبط للفرد في تطبيقك - خاصةً عندما يتعلق الأمر بالتجميع.

  1. يتم استخدامه بسهولة (ab) كمتغير عالمي.
  2. من الصعب نسبيًا اختبار الوحدات بشكل منفصل عن الفصول التي تعتمد على المفردات.

الاحتكار هو الشيطان والمفردات ذات الحالة غير القابلة للقراءة/القابلة للتغيير هي المشكلة "الحقيقية" ...

بعد القراءة المفردون هم كذابون مرضيون كما هو مقترح في إجابة جيسون لقد صادفت هذه المعلومة الصغيرة التي تقدم أفضل مثال على ذلك كيف غالبًا ما يتم إساءة استخدام المفردات.

العالمية سيئة للأسباب التالية:

  • أ.يتسبب في تعارض مساحة الاسم
  • ب.إنه يفضح الدولة بطريقة غير مبررة

عندما يتعلق الأمر بالعزاب

  • أ.الطريقة الصريحة OO للاتصال بهم تمنع التعارضات، لذا أشر إلى a.ليست مشكلة
  • ب.المفردون الذين ليس لديهم دولة (مثل المصانع) لا يمثلون مشكلة.يمكن أن تندرج المفردات ذات الحالة مرة أخرى في فئتين، تلك التي تكون غير قابلة للتغيير أو تكتب مرة واحدة وتقرأ العديد من الملفات (ملفات التكوين/الخاصية).هذه ليست سيئة.المفردات القابلة للتغيير، والتي تعد نوعًا من حاملي المراجع هي تلك التي تتحدث عنها.

في البيان الأخير يشير إلى مفهوم المدونة المتمثل في "الأفراد الكاذبون".

كيف ينطبق هذا على الاحتكار؟

لبدء لعبة الاحتكار، أولاً:

  • نحن نضع القواعد أولاً حتى يكون الجميع على نفس الصفحة
  • يتم منح الجميع بداية متساوية في بداية اللعبة
  • يتم تقديم مجموعة واحدة فقط من القواعد لتجنب الارتباك
  • لا يُسمح للقواعد بالتغيير طوال اللعبة

الآن، لمن لم يفعل ذلك حقًا لعبت الاحتكار، وهذه المعايير مثالية في أحسن الأحوال.من الصعب تقبل الهزيمة في الاحتكار، لأن الاحتكار يتعلق بالمال، فإذا خسرت، عليك أن تراقب بعناية بقية اللاعبين وهم ينهون اللعبة، وعادة ما تكون الخسائر سريعة وساحقة.لذا، عادة ما يتم تحريف القواعد في مرحلة ما لخدمة المصلحة الذاتية لبعض اللاعبين على حساب الآخرين.

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

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

وبعد ذلك، بدلاً من فوز أحدهم، تبدأ العملية من جديد.فجأة، تصبح مجموعة محدودة من القواعد هدفًا متحركًا وتتدهور اللعبة إلى نوع من التفاعلات الاجتماعية التي من شأنها أن تشكل أساس كل برنامج تلفزيوني واقعي عالي التصنيف منذ Survivor.لماذا، لأن القواعد تتغير وليس هناك إجماع حول كيف/لماذا/ما الذي من المفترض أن تمثله، والأهم من ذلك، لا يوجد شخص واحد يتخذ القرارات.يقوم كل لاعب في اللعبة، في تلك المرحلة، بوضع قواعده الخاصة، وتحدث الفوضى حتى يشعر اثنان من اللاعبين بالتعب الشديد بحيث لا يتمكنان من الاستمرار في التمثيلية ويستسلمان ببطء.

لذا، إذا كان كتاب قواعد لعبة ما يمثل بدقة لعبة فردية، فإن كتاب قواعد الاحتكار سيكون مثالاً على سوء الاستخدام.

كيف ينطبق هذا على البرمجة؟

بصرف النظر عن جميع مشكلات سلامة الخيط والمزامنة الواضحة التي تقدمها المفردات القابلة للتغيير ...إذا كانت لديك مجموعة واحدة من البيانات، والتي يمكن قراءتها/معالجتها بواسطة عدة مصادر مختلفة بشكل متزامن وموجودة أثناء فترة تنفيذ التطبيق، فمن المحتمل أن يكون الوقت مناسبًا للتراجع والسؤال "هل أستخدم النوع الصحيح من البيانات" البنية هنا".

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

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

Singleton لا يتعلق بمثيل واحد!

على عكس الإجابات الأخرى، لا أريد أن أتحدث عن الخطأ في Singletons ولكن لأوضح لك مدى قوتها وروعتها عند استخدامها بشكل صحيح!

  • مشكلة:يمكن أن يشكل Singleton تحديًا في بيئة متعددة الخيوط
    حل:استخدم عملية تمهيد مترابطة واحدة لتهيئة جميع تبعيات المفردة الخاصة بك.
  • مشكلة:من الصعب الاستهزاء بالأفراد المنفردين.
    حل:استخدم الطريقة مصنع نمط للسخرية
    MyModel myModel = Factory.inject(MyModel.class); يمكنك الخريطة MyModel ل TestMyModel الطبقة التي ترثها، في كل مكان ومتى MyModel سيتم حقنك سوف تحصل عليه TestMyModel instread.
  • مشكلة:يمكن أن تتسبب المفردات في حدوث كراث في الذاكرة حيث لا يتم التخلص منها أبدًا.
    حل:حسنًا، تخلص منهم!قم بتنفيذ رد اتصال في تطبيقك للتخلص من المفردات بشكل صحيح، ويجب عليك إزالة أي بيانات مرتبطة بها وأخيرًا:إزالتها من المصنع.

كما ذكرت في العنوان، فإن المفردة لا تتعلق بمثيل واحد.

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

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

أكبر خطأين أراهما هما:التعامل معها على أنها عالمية والفشل في تحديد إغلاق Singleton.

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

سياق Singleton مهم حقًا أيضًا.السمة المميزة لـ Singleton هي أن هناك "واحدًا فقط"، ولكن الحقيقة هي أنه "واحد فقط" ضمن نوع ما من السياق/مساحة الاسم.هم عادة واحد من:واحد لكل مؤشر ترابط أو عملية أو عنوان IP أو مجموعة، ولكن يمكن أيضًا أن يكون واحدًا لكل معالج أو جهاز أو مساحة اسم اللغة/محمل الفئة/أيًا كان، أو شبكة فرعية، أو إنترنت، وما إلى ذلك.

الخطأ الآخر الأقل شيوعًا هو تجاهل أسلوب حياة سينجلتون.لا يعني مجرد وجود كائن واحد فقط أن Singleton له القدرة المطلقة "كان دائمًا وسيظل كذلك دائمًا"، كما أنه ليس مرغوبًا بشكل عام (الكائنات التي ليس لها بداية ونهاية تنتهك جميع أنواع الافتراضات المفيدة في التعليمات البرمجية، ويجب استخدامها فقط في أحلك الظروف.

إذا تجنبت هذه الأخطاء، فلا يزال بإمكان Singletons أن تكون بمثابة PITA، ولكنها على استعداد لرؤية الكثير من أسوأ المشكلات يتم تخفيفها بشكل كبير.تخيل Java Singleton، التي تم تعريفها بشكل صريح على أنها مرة واحدة لكل أداة تحميل فئة (مما يعني أنها تحتاج إلى سياسة أمان لسلسلة المحادثات)، مع طرق إنشاء وتدمير محددة ودورة حياة تملي متى وكيف يتم استدعاؤها، وطريقة "المثيل" الخاصة بها لها حماية الحزمة بحيث يتم الوصول إليها بشكل عام من خلال كائنات أخرى غير عامة.لا يزال مصدرًا محتملاً للمشاكل، لكنه بالتأكيد أقل بكثير من المشاكل.

للأسف، بدلاً من تدريس أمثلة جيدة عن كيفية القيام بالمفردات.نحن نعلم الأمثلة السيئة، ونسمح للمبرمجين باستخدامها لفترة من الوقت، ثم نخبرهم أنها نمط تصميم سيئ.

انظر ويكيبيديا Singleton_pattern

ويعتبر أيضًا نمطًا مضادًا من قبل بعض الأشخاص، الذين يشعرون أنه يتم استخدامه بشكل مفرط، مما يؤدي إلى إدخال قيود غير ضرورية في المواقف التي لا تكون فيها نسخة وحيدة من الفصل مطلوبة فعليًا.[1] [2] [3] [4]

المراجع (المراجع ذات الصلة فقط من المقالة)

  1. ^ اليكس ميلر. الأنماط التي أكرهها رقم 1:سينجلتون, ، يوليو 2007
  2. ^ سكوت دينسمور. لماذا المفردات شريرة, ، مايو 2004
  3. ^ ستيف ييج. يعتبر المفردون أغبياء, ، سبتمبر 2004
  4. ^ ج.ب.رينسبيرجر، آي بي إم. استخدم أغانيك المنفردة بحكمة, ، يوليو 2001

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

يعد استخدام مثيل واحد لفئة ما بمثابة بناء صالح طالما قمت بتطبيق الوسائل التالية في التعليمات البرمجية:

  1. تأكد من أن الفئة التي سيتم استخدامها كوحدة فردية تنفذ واجهة.يسمح هذا بتنفيذ الدعائم أو النماذج باستخدام نفس الواجهة

  2. تأكد من أن Singleton آمن للخيط.هذا أمر معطى.

  3. يجب أن يكون المفرد بسيطًا بطبيعته وليس معقدًا للغاية.

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

  5. أثناء الاختبار ولضمان السلوك الحتمي، قم بإنشاء فئة مفردة كمثيل منفصل إما للفئة الفعلية نفسها أو كعب روتين/نموذج ينفذ سلوكها وتمريره كما هو إلى الفئة التي تتطلب ذلك.لا تستخدم عامل الفئة الذي ينشئ هذا الكائن قيد الاختبار والذي يحتاج إلى المفرد أثناء الاختبار لأنه سيجتاز المثيل العام الوحيد له، مما يتعارض مع الغرض.

لقد استخدمنا Singletons في حلولنا مع قدر كبير من النجاح الذي يمكن اختباره لضمان السلوك الحتمي في تدفقات تشغيل الاختبار المتوازية.

فينس هيوستن لديه هذه المعايير، والتي تبدو معقولة بالنسبة لي:

لا ينبغي النظر في Singleton إلا في حالة استيفاء المعايير الثلاثة التالية:

  • لا يمكن تعيين ملكية المثيل الفردي بشكل معقول
  • التهيئة البطيئة أمر مرغوب فيه
  • لم يتم توفير الوصول العالمي بخلاف ذلك

إذا لم تكن ملكية المثيل الفردي ومتى وكيف تحدث التهيئة والوصول الشامل مشكلات، فإن Singleton ليس مثيرًا للاهتمام بدرجة كافية.

أود أن أتناول النقاط الأربع في الإجابة المقبولة، وآمل أن يتمكن شخص ما من شرح سبب خطأي.

  1. لماذا يعد إخفاء التبعيات في التعليمات البرمجية الخاصة بك أمرًا سيئًا؟يوجد بالفعل العشرات من التبعيات المخفية (استدعاءات وقت تشغيل C، واستدعاءات OS API، واستدعاءات الوظائف العامة)، ومن السهل العثور على التبعيات المفردة (ابحث عن المثال ()).

    "جعل شيء عالمي لتجنب تمريره هو رائحة رمز." لماذا لا يمر شيء ما حوله لتجنب جعله رائحة رمز مفردة؟

    إذا كنت تقوم بتمرير كائن من خلال 10 وظائف في مكدس الاستدعاءات فقط لتجنب وظيفة مفردة، فهل هذا رائع جدًا؟

  2. مبدأ المسؤولية الفردية:أعتقد أن هذا غامض بعض الشيء ويعتمد على تعريفك للمسؤولية.سيكون السؤال ذو الصلة هو لماذا تتم إضافة هذا محدد "المسؤولية" تجاه مسألة طبقية؟

  3. لماذا يؤدي تمرير كائن إلى فصل دراسي إلى جعله مقترنًا بشكل أكثر إحكامًا من استخدام هذا الكائن كمفرد من داخل الفصل؟

  4. لماذا يتغير مدة استمرار الدولة؟يمكن إنشاء العناصر المنفردة أو إتلافها يدويًا، وبالتالي يظل عنصر التحكم موجودًا، ويمكنك جعل العمر الافتراضي هو نفسه عمر الكائن غير المفرد.

بخصوص اختبارات الوحدة:

  • لا تحتاج جميع الفصول إلى اختبار الوحدة
  • لا تحتاج جميع الفئات التي تحتاج إلى اختبار الوحدة إلى تغيير تنفيذ المفرد
  • اذا هم يفعل يجب اختبار الوحدة وتحتاج إلى تغيير التنفيذ ، من السهل تغيير فصل ما من استخدام Singleton إلى تمرير Singleton إليه من خلال حقن التبعية.

لن أعلق على حجة الخير/الشر، لكنني لم أستخدمها منذ ذلك الحين ربيع جاء على طول.استخدام حقن التبعية لقد أزال إلى حد كبير متطلباتي الخاصة بالمفردات ومحددات الخدمة والمصانع.أجد أن هذه بيئة أكثر إنتاجية ونظيفة، على الأقل بالنسبة لنوع العمل الذي أقوم به (تطبيقات الويب المستندة إلى Java).

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

أعتقد أن رد الفعل العنيف يرجع إلى الإفراط في استخدامه والذي بدوره يرجع إلى حقيقة أنه النمط الأسهل للفهم والتنفيذ.

العزاب سيئون من وجهة نظر نقية.

ومن وجهة نظر عملية، المفرد هو مقايضة وقت التطوير مقابل التعقيد.

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

أحيانًا ما تكون المفردات معقدة أيضًا وحدة التجارب.

Singleton هو نمط ويمكن استخدامه أو إساءة استخدامه تمامًا مثل أي أداة أخرى.

الجزء السيئ من المفردة بشكل عام هو المستخدم (أو يجب أن أقول الاستخدام غير المناسب للمفردة للأشياء التي لم يتم تصميمها للقيام بها).أكبر مرتكب الجريمة هو استخدام المفرد كمتغير عالمي مزيف.

عندما تكتب تعليمات برمجية باستخدام مفردات، على سبيل المثال، مسجل أو اتصال بقاعدة بيانات، ثم تكتشف بعد ذلك أنك بحاجة إلى أكثر من سجل واحد أو أكثر من قاعدة بيانات واحدة، فأنت في مشكلة.

تجعل المفردات من الصعب جدًا الانتقال منها إلى الأشياء العادية.

أيضًا، من السهل جدًا كتابة مفردة غير آمنة للخيط.

بدلاً من استخدام المفردات، يجب عليك تمرير كافة الكائنات المساعدة المطلوبة من وظيفة إلى أخرى.يمكن تبسيط ذلك إذا قمت بتغليفها كلها في كائن مساعد، مثل هذا:

void some_class::some_function(parameters, service_provider& srv)
{
    srv.get<error_logger>().log("Hi there!");
    this->another_function(some_other_parameters, srv);
}

مشاكل المفردات هي مسألة النطاق المتزايد وبالتالي اقتران.ليس هناك من ينكر أن هناك بعض المواقف التي تحتاج فيها إلى الوصول إلى مثيل واحد، ويمكن تحقيق ذلك بطرق أخرى.

أفضّل الآن التصميم حول ملف قلب السيطرة (IoC) وتسمح بالحاوية بالتحكم في العمر الافتراضي.يمنحك هذا فائدة الفئات التي تعتمد على المثيل لتكون غير مدركة لحقيقة وجود مثيل واحد.يمكن تغيير عمر المفردة في المستقبل.أحد الأمثلة التي واجهتها مؤخرًا كان تعديلًا سهلاً من الخيوط الفردية إلى الخيوط المتعددة.

FWIW، إذا كان PIA عندما تحاول اختبار الوحدة، فسوف ينتقل إلى PIA عندما تحاول تصحيح الأخطاء أو إصلاح الأخطاء أو تحسينها.

مقالة حديثة حول هذا الموضوع كتبها كريس ريث في الترميز بدون تعليقات.

ملحوظة:الترميز بدون تعليقات لم يعد صالحًا.ومع ذلك، تم نسخ المقالة التي تم الارتباط بها بواسطة مستخدم آخر.

http://geekswithblogs.net/AngelEyes/archive/2013/09/08/singleton-i-love-you-but-youre-bringing-me-down-re-uploaded.aspx

إليك شيء آخر عن المفردات التي لم يقلها أحد بعد.

في معظم الحالات، تكون "المفردة" بمثابة تفاصيل التنفيذ لبعض الفئات وليست سمة من سمات الواجهة الخاصة بها.قد يؤدي انقلاب حاوية التحكم إلى إخفاء هذه الخاصية عن مستخدمي الفصل؛كل ما عليك فعله هو وضع علامة على فصلك باعتباره مفردًا (مع @Singleton تعليق توضيحي في Java على سبيل المثال) وهذا كل شيء؛سوف تقوم IoCC بالباقي.لا تحتاج إلى توفير وصول عالمي إلى المثيل المفرد الخاص بك لأن الوصول تتم إدارته بالفعل بواسطة IoCC.وبالتالي لا يوجد شيء خاطئ في IoC Singletons.

من المفترض أن تقوم GoF Singletons على عكس IoC Singletons بكشف "التفرد" في الواجهة من خلال طريقة getInstance()، وبالتالي فإنها تعاني من كل ما ذكر أعلاه.

المفردات ليست سيئة.إنه أمر سيء فقط عندما تصنع شيئًا فريدًا عالميًا وليس فريدًا عالميًا.

ومع ذلك، هناك "خدمات نطاق التطبيق" (فكر في نظام المراسلة الذي يجعل المكونات تتفاعل) - يستدعي هذا فئة مفردة، فئة "MessageQueue" التي لها طريقة "SendMessage(...)".

يمكنك بعد ذلك القيام بما يلي من كل مكان:

messageQueue.Current.SendMessage(new MailArrivedMessage(...));

وبالطبع قم بما يلي:

messageQueue.Current.RegisterReceiver(this);

في الفئات التي تطبق IMessageReceiver.

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

نظرًا لأنها في الأساس متغيرات عامة موجهة للكائنات، يمكنك عادةً تصميم فئاتك بطريقة لا تحتاج إليها.

يظهر النمط عندما يصل العديد من الأشخاص (أو الفرق) إلى حلول مماثلة أو متطابقة.لا يزال الكثير من الأشخاص يستخدمون المفردات في شكلها الأصلي أو يستخدمون قوالب المصنع (مناقشة جيدة في تصميم Alexandrescu's Modern C++ Design).يعد التزامن والصعوبة في إدارة عمر الكائن من العوائق الرئيسية، مع سهولة إدارة الأول كما تقترح.

مثل جميع الخيارات، تتمتع سينجلتون بنصيبها العادل من الصعود والهبوط.أعتقد أنه يمكن استخدامها باعتدال، خاصة بالنسبة للكائنات التي تدوم طوال عمر التطبيق.ومن المفترض أن حقيقة أنها تشبه الكرات العالمية (وربما تكون كذلك) قد أثارت غضب الأصوليين.

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

في كثير من الأحيان تصبح البرامج أكثر تعقيدًا، فمن المنطقي أن يكون لديك مثيلات مستقلة متعددة للفئة "المفردة" بحالة مختلفة.يعد الالتزام بالتعليمات البرمجية للاستيلاء على المفردة أمرًا خاطئًا في مثل هذه الحالات.قد يكون استخدام Singleton.getInstance() أمرًا جيدًا للأنظمة الصغيرة البسيطة ولكنه لا يعمل/يتسع عندما يحتاج المرء إلى مثيل مختلف من نفس الفئة.

لا ينبغي اعتبار أي فئة بمثابة فئة مفردة، بل يجب أن تكون تطبيقًا لاستخدامها أو كيفية استخدامها لتكوين المعالين.بالنسبة إلى عملية سريعة وسيئة، لا يهم هذا - فقط الترميز الثابت يقول أن مسارات الملفات لا تهم ولكن بالنسبة للتطبيقات الأكبر حجمًا، يجب أخذ هذه التبعيات في الاعتبار وإدارتها بطريقة أكثر ملاءمة باستخدام DI.

المشاكل التي يسببها المفرد في الاختبار هي أحد أعراض حالة/بيئة الاستخدام الفردي المشفرة.مجموعة الاختبار والاختبارات العديدة فردية وتفصل شيئًا غير متوافق مع الترميز الفردي.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top