أداء استخدام الأساليب الثابتة مقابل إنشاء مثيل للفئة التي تحتوي على الأساليب

StackOverflow https://stackoverflow.com/questions/202631

سؤال

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

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

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

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

المحلول

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

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

نصائح أخرى

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

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

وحدة تحكم المستخدم، وحدة التحكم الجغرافي، وحدة تحكم التسوق...

يوجد داخل كل فئة وحدة تحكم أساليب ثابتة تقوم بإجراء مكالمات للتخزين المؤقت أو DAL باستخدام النمط المفرد.

لقد أعطانا هذا ميزتين رئيسيتين.إنه أسرع قليلاً (حوالي 2-3 مرات أسرع ولكننا نتحدث عن النانو ثانية هنا ؛ P).والآخر هو أن الكود أكثر نظافة

أي

ShoppingController.ListPaymentMethods()

بدلاً من

new ShoppingController().ListPaymentMethods()

أعتقد أنه من المنطقي استخدام الأساليب أو الفئات الثابتة إذا لم يحتفظ الفصل بأي حالة.

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

ومع ذلك، لا يبدو أن الأداء هو مشكلتك الأكبر الآن.

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

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

أيضًا، إذا كنت تريد اتباع مبادئ OO الجيدة (راجع SOLID في http://en.wikipedia.org/wiki/SOLID_%28object-Oriented_design%29 ) و/أو استخدام أنماط التصميم، فمن المؤكد أنك ستنفذ الكثير من التطوير القائم على المثيلات والواجهة، ولن تستخدم العديد من الأساليب الثابتة.

أما بالنسبة لهذا الاقتراح:

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

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

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

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

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

ShapeUtils.DrawCircle(stroke, pen, origin, radius);

ShapeUtils.DrawSquare(stroke, pen, x, y, width, length);

ضد

ShapeUtils utils = new ShapeUtils(stroke,pen);

util.DrawCircle(origin,radius);

util.DrawSquare(x,y,width,length);

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

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

الأساليب الثابتة قابلة للاختبار.في الواقع، حتى أكثر قابلية للاختبار من المثال مرة واحدة.ستكون طريقة GetSum(x,y) قابلة للاختبار للغاية ليس فقط لاختبار الوحدة ولكن أيضًا لاختبار التحميل والاختبار المتكامل واختبار الاستخدام.تعتبر أساليب المثيل جيدة لاختبارات الوحدات ولكنها فظيعة لكل الاختبارات الأخرى (والتي تهم أكثر من اختبارات الوحدات راجع للشغل) ولهذا السبب نواجه الكثير من الأخطاء هذه الأيام.الشيء الذي يجعل جميع الأساليب غير قابلة للاختبار هي المعلمات التي لا معنى لها مثل (Sender s، EventArgs e) أو الحالة العامة مثل DateTime.Now.في الواقع، تعد الأساليب الثابتة جيدة جدًا في قابلية الاختبار لدرجة أنك ترى أخطاء أقل في كود C لتوزيع Linux الجديد مقارنة بمبرمج OO العادي (وهو مليء بالأشياء التي أعرفها).

أعتقد أنك أجبت جزئيًا على هذا السؤال بالطريقة التي طرحتها بها:هل يوجد أي ملحوظة عقوبات الأداء في التعليمات البرمجية التي لديك؟

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

أعتقد أن ما أقوله هو أن مشكلة الأداء هي مشكلة فقط عندما تلاحظ أنها مشكلة.

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

ومن هناك يمكنك تحريك نطاق هذه الكائنات ببطء ليصبح أصغر فأصغر حتى تحصل على تصميم OOP لائق.

ثم مرة أخرى، النهج الذي أنا من المحتمل أن يستخدم بشكل مختلف ;).

شخصيًا، من المحتمل أن أركز على الهياكل والوظائف التي تعمل عليها وأحاول تحويلها إلى فئات ذات أعضاء شيئًا فشيئًا.

أما بالنسبة لجانب الأداء في السؤال، فيجب أن تكون الأساليب الثابتة أسرع قليلاً (ولكن ليس كثيرًا) لأنها لا تتضمن إنشاء كائن وتمريره وتفكيكه.

إنه غير صالح في PHP،
طريقة الكائن أسرع:
http://www.vanylla.it/tests/static-method-vs-object.php

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