JavaRush /مدونة جافا /Random-AR /دليل لأسلوب البرمجة العامة
pandaFromMinsk
مستوى
Минск

دليل لأسلوب البرمجة العامة

نشرت في المجموعة
هذه المقالة جزء من الدورة الأكاديمية "جافا المتقدمة." تم تصميم هذه الدورة لمساعدتك على تعلم كيفية استخدام ميزات جافا بشكل فعال. تغطي المادة موضوعات "متقدمة" مثل إنشاء الكائنات، والمنافسة، والتسلسل، والانعكاس، وما إلى ذلك. ستعلمك الدورة كيفية إتقان تقنيات Java بشكل فعال. التفاصيل هنا .
محتوى
1. مقدمة 2. النطاق المتغير 3. حقول الفئة والمتغيرات المحلية 4. وسيطات الطريقة والمتغيرات المحلية 5. الملاكمة والفتح 6. الواجهات 7. السلاسل 8. اصطلاحات التسمية 9. المكتبات القياسية 10. الثبات 11. الاختبار 12. التالي. .. 13. تنزيل الكود المصدري
1 المقدمة
في هذا الجزء من البرنامج التعليمي سنواصل مناقشتنا للمبادئ العامة لأسلوب البرمجة الجيد والتصميم سريع الاستجابة في Java. لقد رأينا بالفعل بعض هذه المبادئ في الفصول السابقة من الدليل، ولكن سيتم تقديم العديد من النصائح العملية بهدف تحسين مهارات مطور Java.
2. نطاق متغير
في الجزء الثالث ("كيفية تصميم الفئات والواجهات")، ناقشنا كيف يمكن تطبيق الرؤية وإمكانية الوصول على أعضاء الفئات والواجهات، مع مراعاة قيود النطاق. ومع ذلك، لم نناقش بعد المتغيرات المحلية المستخدمة في تطبيقات الطريقة. في لغة جافا، كل متغير محلي، بمجرد الإعلان عنه، يكون له نطاق. يصبح هذا المتغير مرئيًا من المكان الذي تم الإعلان عنه إلى النقطة التي يكتمل فيها تنفيذ الطريقة (أو كتلة التعليمات البرمجية). بشكل عام، القاعدة الوحيدة التي يجب اتباعها هي الإعلان عن متغير محلي في أقرب مكان ممكن من المكان الذي سيتم استخدامه فيه. اسمحوا لي أن ألقي نظرة على مثال نموذجي: for( final Locale locale: Locale.getAvailableLocales() ) { // блок codeа } try( final InputStream in = new FileInputStream( "file.txt" ) ) { // блока codeа } في كلا أجزاء التعليمات البرمجية، يقتصر نطاق المتغيرات على كتل التنفيذ حيث يتم الإعلان عن هذه المتغيرات. عند اكتمال الكتلة، ينتهي النطاق ويصبح المتغير غير مرئي. يبدو هذا أكثر وضوحًا، ولكن مع إصدار Java 8 وإدخال lambdas، أصبحت العديد من التعابير اللغوية المعروفة التي تستخدم المتغيرات المحلية قديمة. اسمحوا لي أن أقدم مثالاً من المثال السابق باستخدام lambdas بدلاً من الحلقة: Arrays.stream( Locale.getAvailableLocales() ).forEach( ( locale ) -> { // блок codeа } ); يمكن ملاحظة أن المتغير المحلي أصبح وسيطًا للدالة، والتي بدورها يتم تمريرها كوسيطة للأسلوب forEach .
3. حقول الفئة والمتغيرات المحلية
تنتمي كل طريقة في Java إلى فئة معينة (أو، في حالة Java8، إلى واجهة يتم فيها الإعلان عن الطريقة كطريقة افتراضية). بين المتغيرات المحلية التي هي حقول فئة أو طرق مستخدمة في التنفيذ، هناك احتمال حدوث تعارض في الأسماء. يعرف مترجم Java كيفية اختيار المتغير الصحيح من بين المتغيرات المتاحة، على الرغم من أن أكثر من مطور ينوي استخدام هذا المتغير. تقوم Java IDEs الحديثة بعمل رائع في إخبار المطور عندما تكون مثل هذه التعارضات على وشك الحدوث، من خلال تحذيرات المترجم وتمييز المتغيرات. ولكن لا يزال من الأفضل التفكير في مثل هذه الأشياء أثناء كتابة التعليمات البرمجية. أقترح النظر إلى مثال: public class LocalVariableAndClassMember { private long value; public long calculateValue( final long initial ) { long value = initial; value *= 10; value += value; return value; } } المثال يبدو سهلاً للغاية، لكنه فخ. يقدم أسلوب CalculatorValue قيمة متغيرة محلية ، ويعمل عليها، ويخفي حقل الفئة الذي يحمل نفس الاسم. يجب أن يكون السطر value += value; هو مجموع قيمة حقل الفئة والمتغير المحلي، ولكن بدلاً من ذلك، يتم القيام بشيء آخر. سيبدو التنفيذ الصحيح على النحو التالي (باستخدام الكلمة الأساسية this): public class LocalVariableAndClassMember { private long value; public long calculateValue( final long initial ) { long value = initial; value *= 10; value += this.value; return value; } } على الرغم من أن هذا المثال ساذج في بعض النواحي، إلا أنه يوضح نقطة مهمة وهي أنه في بعض الحالات قد يستغرق تصحيح الأخطاء وإصلاحها ساعات.
4. وسائط الأسلوب والمتغيرات المحلية
هناك مأزق آخر غالبًا ما يقع فيه مطورو Java عديمي الخبرة وهو استخدام وسائط الطريقة كمتغيرات محلية. تسمح لك Java بإعادة تعيين القيم للوسائط غير الثابتة (ومع ذلك، ليس لهذا أي تأثير على القيمة الأصلية): public String sanitize( String str ) { if( !str.isEmpty() ) { str = str.trim(); } str = str.toLowerCase(); return str; } مقتطف الكود أعلاه ليس أنيقًا، ولكنه يقوم بعمل جيد في الكشف عن المشكلة: تم تعيين الوسيطة str قيمة مختلفة (وتستخدم بشكل أساسي كمتغير محلي). في جميع الحالات (دون أي استثناء)، يمكنك ويجب عليك الاستغناء عن هذا المثال (على سبيل المثال، عن طريق إعلان الوسائط كثوابت). على سبيل المثال: public String sanitize( final String str ) { String sanitized = str; if( !str.isEmpty() ) { sanitized = str.trim(); } sanitized = sanitized.toLowerCase(); return sanitized; } باتباع هذه القاعدة البسيطة، يكون من الأسهل تتبع الكود المحدد والعثور على مصدر المشكلة، حتى عند إدخال المتغيرات المحلية.
5. التعبئة والتفريغ
Boxing and unboxing هو اسم تقنية مستخدمة في Java لتحويل الأنواع البدائية ( int، long، double، إلخ. ) إلى أغلفة النوع المقابلة ( Integer، Long، Double ، إلخ.). في الجزء 4 من البرنامج التعليمي كيف ومتى يتم استخدام الأدوية العامة، لقد رأيت هذا بالفعل أثناء العمل عندما تحدثت عن تغليف الأنواع البدائية كمعلمات نوع للأدوية العامة. على الرغم من أن مترجم Java يبذل قصارى جهده لإخفاء مثل هذه التحويلات عن طريق إجراء عملية التشغيل التلقائي، إلا أن هذا في بعض الأحيان يكون أقل من المتوقع ويؤدي إلى نتائج غير متوقعة. دعونا نلقي نظرة على مثال: public static void calculate( final long value ) { // блок codeа } final Long value = null; calculate( value ); يتم تجميع مقتطف الشفرة أعلاه بشكل جيد. ومع ذلك، فإنه سيتم طرح NullPointerException على السطر // блок الذي يتم فيه التحويل بين Long و long . النصيحة في مثل هذه الحالة هي أنه من المستحسن استخدام الأنواع البدائية (ومع ذلك، نحن نعلم بالفعل أن هذا ليس ممكنًا دائمًا).
6. واجهات
في الجزء الثالث من البرنامج التعليمي، "كيفية تصميم الفئات والواجهات"، ناقشنا الواجهات وبرمجة العقود، مع التركيز على ضرورة تفضيل الواجهات على الفئات الملموسة كلما أمكن ذلك. الغرض من هذا القسم هو تشجيعك على التفكير في الواجهات أولاً من خلال توضيح ذلك بأمثلة واقعية. لا ترتبط الواجهات بتنفيذ محدد (باستثناء الطرق الافتراضية). إنها مجرد عقود، وعلى سبيل المثال، فهي توفر قدرًا كبيرًا من الحرية والمرونة في طريقة تنفيذ العقود. تصبح هذه المرونة أكثر أهمية عندما يتضمن التنفيذ أنظمة أو خدمات خارجية. دعونا نلقي نظرة على مثال لواجهة بسيطة وتنفيذها المحتمل: public interface TimezoneService { TimeZone getTimeZone( final double lat, final double lon ) throws IOException; } public class TimezoneServiceImpl implements TimezoneService { @Override public TimeZone getTimeZone(final double lat, final double lon) throws IOException { final URL url = new URL( String.format( "http://api.geonames.org/timezone?lat=%.2f&lng=%.2f&username=demo", lat, lon ) ); final HttpURLConnection connection = ( HttpURLConnection )url.openConnection(); connection.setRequestMethod( "GET" ); connection.setConnectTimeout( 1000 ); connection.setReadTimeout( 1000 ); connection.connect(); int status = connection.getResponseCode(); if (status == 200) { // Do something here } return TimeZone.getDefault(); } } يُظهر مقتطف التعليمات البرمجية أعلاه نمط واجهة نموذجيًا وتنفيذه. يستخدم هذا التنفيذ خدمة HTTP خارجية ( http://api.geonames.org/ ) لاسترداد المنطقة الزمنية لموقع معين. ومع ذلك، لأن يعتمد العقد على الواجهة، فمن السهل جدًا تقديم تطبيق آخر للواجهة، باستخدام، على سبيل المثال، قاعدة بيانات أو حتى ملف ثابت عادي. معهم، تكون الواجهات مفيدة جدًا في تصميم تعليمات برمجية قابلة للاختبار. على سبيل المثال، ليس من العملي دائمًا استدعاء خدمات خارجية في كل اختبار، لذا فمن المنطقي بدلاً من ذلك تنفيذ تطبيق بديل وأبسط (مثل كعب الروتين): public class TimezoneServiceTestImpl implements TimezoneService { @Override public TimeZone getTimeZone(final double lat, final double lon) throws IOException { return TimeZone.getDefault(); } } يمكن استخدام هذا التنفيذ في أي مكان حيث تكون واجهة TimezoneService مطلوبة، وعزل اختبار البرنامج النصي من الاعتماد على المكونات الخارجية. تم تضمين العديد من الأمثلة الممتازة للاستخدام الفعال لهذه الواجهات في مكتبة Java القياسية. المجموعات والقوائم والمجموعات - تحتوي هذه الواجهات على تطبيقات متعددة يمكن تبديلها بسهولة ويمكن تبادلها عندما تستفيد العقود. على سبيل المثال: public static< T > void print( final Collection< T > collection ) { for( final T element: collection ) { System.out.println( element ); } } print( new HashSet< Object >( /* ... */ ) ); print( new ArrayList< Integer >( /* ... */ ) ); print( new TreeSet< String >( /* ... */ ) );
7. السلاسل
تعد السلاسل النصية من أكثر الأنواع استخدامًا في كل من Java ولغات البرمجة الأخرى. تعمل لغة Java على تبسيط العديد من عمليات معالجة السلاسل الروتينية من خلال دعم عمليات التسلسل والمقارنة فورًا. بالإضافة إلى ذلك، تحتوي المكتبة القياسية على العديد من الفئات التي تجعل عمليات السلسلة فعالة. وهذا هو بالضبط ما سنناقشه في هذا القسم. في Java، السلاسل هي كائنات غير قابلة للتغيير ممثلة بتشفير UTF-16. في كل مرة تقوم فيها بتسلسل السلاسل (أو تنفيذ أي عملية لتعديل السلسلة الأصلية)، يتم إنشاء مثيل جديد لفئة السلسلة . ولهذا السبب، يمكن أن تصبح عملية التسلسل غير فعالة للغاية، مما يتسبب في إنشاء العديد من المثيلات المتوسطة لفئة السلسلة (إنشاء البيانات المهملة بشكل عام). لكن مكتبة Java القياسية تحتوي على فئتين مفيدتين للغاية، والغرض منهما هو تسهيل معالجة السلسلة. هذه هي StringBuilder و StringBuffer (الفرق الوحيد بينهما هو أن StringBuffer آمن لمؤشر الترابط بينما StringBuilder هو العكس). دعونا نلقي نظرة على مثالين لأحد هذه الفئات المستخدمة: final StringBuilder sb = new StringBuilder(); for( int i = 1; i <= 10; ++i ) { sb.append( " " ); sb.append( i ); } sb.deleteCharAt( 0 ); sb.insert( 0, "[" ); sb.replace( sb.length() - 3, sb.length(), "]" ); على الرغم من أن استخدام StringBuilder/StringBuffer هو الطريقة الموصى بها للتعامل مع السلاسل، فقد يبدو الأمر مبالغًا فيه في أبسط سيناريو لتسلسل سلسلتين أو ثلاث سلاسل، بحيث يكون عامل الإضافة العادي ( ("+")، على سبيل المثال: String userId = "user:" + new Random().nextInt( 100 ); غالبًا ما يكون أفضل بديل لتبسيط التسلسل هو استخدام تنسيق السلسلة بالإضافة إلى مكتبة Java القياسية للمساعدة في توفير أسلوب مساعد String.format ثابت . يدعم هذا مجموعة غنية من محددات التنسيق، بما في ذلك الأرقام والرموز والتاريخ/الوقت وما إلى ذلك. (راجع الوثائق المرجعية للحصول على التفاصيل الكاملة) يوفر String.format( "%04d", 1 ); -> 0001 String.format( "%.2f", 12.324234d ); -> 12.32 String.format( "%tR", new Date() ); -> 21:11 String.format( "%tF", new Date() ); -> 2014-11-11 String.format( "%d%%", 12 ); -> 12% أسلوب String.format أسلوبًا نظيفًا وخفيف الوزن لإنشاء سلاسل من أنواع بيانات مختلفة. تجدر الإشارة إلى أن Java IDEs الحديثة يمكنها تحليل مواصفات التنسيق من الوسائط التي تم تمريرها إلى طريقة String.format وتحذير المطورين في حالة اكتشاف أي حالات عدم تطابق.
8. اصطلاحات التسمية
Java هي لغة لا تجبر المطورين على اتباع أي اصطلاح تسمية بشكل صارم، لكن المجتمع طور مجموعة من القواعد البسيطة التي تجعل كود Java يبدو متسقًا في كل من المكتبة القياسية وفي أي مشاريع Java أخرى:
  • أسماء الحزم مكتوبة بأحرف صغيرة: org.junit، com.fasterxml.jackson، javax.json
  • تتم كتابة أسماء الفئات والتعدادات والواجهات والشروح بحرف كبير: StringBuilder، Runnable،Override
  • يتم تحديد أسماء الحقول أو الأساليب (باستثناء النهائي الثابت ) بترميز الجمل: isEmpty، format، addAll
  • تكون أسماء الحقول النهائية الثابتة أو أسماء ثوابت التعداد مكتوبة بأحرف كبيرة، مفصولة بشرطات سفلية ("_"): LOG، MIN_RADIX، INSTANCE.
  • تتم كتابة المتغيرات المحلية أو وسيطات الطريقة بترميز الجمل: str، newLength، minorCapacity
  • يتم تمثيل أسماء أنواع المعلمات للأدوية العامة بحرف واحد بأحرف كبيرة: T، U، E
من خلال اتباع هذه الأعراف البسيطة، سيبدو الكود الذي تكتبه موجزًا ​​ولا يمكن تمييزه من حيث الأسلوب عن مكتبة أو إطار عمل آخر، وستشعر وكأنه تم تطويره بواسطة نفس الشخص (واحدة من تلك الأوقات النادرة التي تعمل فيها الأعراف بالفعل).
9. المكتبات القياسية
بغض النظر عن نوع مشروع Java الذي تعمل عليه، فإن مكتبات Java القياسية هي أفضل أصدقائك. نعم، من الصعب أن نختلف في أن لديهم بعض الحواف الخشنة وقرارات التصميم الغريبة، ومع ذلك، في 99٪ من الحالات تكون تعليمات برمجية عالية الجودة كتبها خبراء. إنه يستحق الاستكشاف. يجلب كل إصدار من إصدارات Java العديد من الميزات الجديدة إلى المكتبات الموجودة (مع بعض المشكلات المحتملة في الميزات القديمة)، ويضيف أيضًا العديد من المكتبات الجديدة. جلبت Java 5 مكتبة متزامنة جديدة كجزء من الحزمة java.util.concurrent . قدمت Java 6 دعمًا للبرمجة النصية (أقل شهرة) ( حزمة javax.script ) وواجهة برمجة تطبيقات مترجم Java (كجزء من حزمة javax.tools ). جلب Java 7 العديد من التحسينات إلى java.util.concurrent ، حيث قدم مكتبة إدخال/إخراج جديدة في حزمة java.nio.file ودعم اللغات الديناميكية في java.lang.invoc . وأخيرًا، أضاف Java 8 التاريخ/الوقت الذي طال انتظاره في حزمة java.time . تتطور Java كمنصة ومن المهم جدًا أن تتقدم جنبًا إلى جنب مع التغييرات المذكورة أعلاه. عندما تفكر في تضمين مكتبة أو إطار عمل تابع لجهة خارجية في مشروعك، تأكد من أن الوظيفة المطلوبة ليست موجودة بالفعل في مكتبات Java القياسية (بالطبع، هناك العديد من التطبيقات المتخصصة وعالية الأداء للخوارزميات التي تسبق الخوارزميات الموجودة في المكتبات القياسية، ولكن في معظم الحالات لا تكون هناك حاجة إليها حقًا).
10. الثبات
يبقى الثبات في جميع أنحاء الدليل وفي هذا الجزء بمثابة تذكير: يرجى أخذ الأمر على محمل الجد. إذا كانت الفئة التي تصممها أو الطريقة التي تنفذها يمكن أن توفر ضمانًا بعدم قابلية التغيير، فيمكن استخدامها في معظم الحالات في كل مكان دون خوف من التعديل في نفس الوقت. وهذا سيجعل حياتك كمطور (ونأمل أن حياة أعضاء فريقك) أسهل.
11. الاختبار
تحظى ممارسة التطوير المبني على الاختبار (TDD) بشعبية كبيرة في مجتمع Java، مما يرفع مستوى جودة التعليمات البرمجية. مع كل المزايا التي يوفرها TDD، من المحزن أن نرى أن مكتبة Java القياسية اليوم لا تتضمن أي إطار اختبار أو أدوات دعم. ومع ذلك، أصبح الاختبار جزءًا ضروريًا من تطوير Java الحديث وفي هذا القسم سنلقي نظرة على بعض التقنيات الأساسية باستخدام إطار عمل JUnit . في JUnit، كل اختبار عبارة عن مجموعة من البيانات حول الحالة أو السلوك المتوقع للكائن. إن سر كتابة الاختبارات الرائعة هو إبقائها بسيطة وقصيرة، واختبار شيء واحد في كل مرة. كتمرين، دعنا نكتب مجموعة من الاختبارات للتحقق من أن String.format هي دالة من قسم السلسلة التي تُرجع النتيجة المطلوبة. package com.javacodegeeks.advanced.generic; import static org.junit.Assert.assertThat; import static org.hamcrest.CoreMatchers.equalTo; import org.junit.Test; public class StringFormatTestCase { @Test public void testNumberFormattingWithLeadingZeros() { final String formatted = String.format( "%04d", 1 ); assertThat( formatted, equalTo( "0001" ) ); } @Test public void testDoubleFormattingWithTwoDecimalPoints() { final String formatted = String.format( "%.2f", 12.324234d ); assertThat( formatted, equalTo( "12.32" ) ); } } يبدو كلا الاختبارين قابلين للقراءة للغاية ويتم تنفيذهما في حالات. اليوم، يحتوي مشروع Java العادي على مئات من حالات الاختبار، مما يمنح المطور ملاحظات سريعة أثناء عملية التطوير بشأن الانحدارات أو الميزات.
12. التالي
يكمل هذا الجزء من الدليل سلسلة من المناقشات المتعلقة بممارسة البرمجة بلغة Java والأدلة الخاصة بلغة البرمجة هذه. في المرة القادمة سنعود إلى ميزات اللغة، ونستكشف عالم Java فيما يتعلق بالاستثناءات وأنواعها وكيف ومتى يتم استخدامها.
13. قم بتنزيل كود المصدر
كان هذا درسًا حول مبادئ التطوير العامة من دورة Java المتقدمة. يمكن تنزيل الكود المصدري للدرس هنا .
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION