JavaRush /وبلاگ جاوا /Random-FA /راهنمای سبک برنامه نویسی عمومی
pandaFromMinsk
مرحله
Минск

راهنمای سبک برنامه نویسی عمومی

در گروه منتشر شد
این مقاله بخشی از دوره آموزشی "جاوا پیشرفته" است. این دوره برای کمک به شما در یادگیری نحوه استفاده موثر از ویژگی های جاوا طراحی شده است. این مطالب موضوعات "پیشرفته" مانند ایجاد شی، رقابت، سریال سازی، بازتاب و غیره را پوشش می دهد. این دوره به شما یاد می دهد که چگونه به طور موثر بر تکنیک های جاوا تسلط پیدا کنید. جزئیات اینجا _
محتوا
1. مقدمه 2. دامنه متغیر 3. فیلدهای کلاس و متغیرهای محلی 4. آرگومان های روش و متغیرهای محلی 5. جعبه سازی و جعبه گشایی 6. رابط ها 7. رشته ها 8. قراردادهای نامگذاری 9. کتابخانه های استاندارد 10. تغییر ناپذیری 11. بعدی. آزمایش. .. 13. دانلود سورس کد
1. معرفی
در این قسمت از آموزش به ادامه بحث خود در مورد اصول کلی سبک برنامه نویسی خوب و طراحی واکنشگرا در جاوا می پردازیم. قبلاً برخی از این اصول را در فصل های قبلی راهنما دیده ایم، اما نکات کاربردی زیادی با هدف بهبود مهارت های یک توسعه دهنده جاوا ارائه خواهد شد.
2. دامنه متغیر
در بخش سوم («نحوه طراحی کلاس‌ها و رابط‌ها») بحث کردیم که چگونه قابلیت مشاهده و دسترسی را می‌توان برای اعضای کلاس‌ها و رابط‌ها، با توجه به محدودیت‌های دامنه اعمال کرد. با این حال، ما هنوز در مورد متغیرهای محلی که در پیاده سازی متد استفاده می شوند بحث نکرده ایم. در زبان جاوا، هر متغیر محلی، پس از اعلام، یک محدوده دارد. این متغیر از جایی که اعلام می شود تا نقطه ای که اجرای متد (یا بلوک کد) کامل می شود قابل مشاهده می شود. به طور کلی، تنها قانونی که باید از آن پیروی کرد این است که یک متغیر محلی را تا حد امکان نزدیک به مکانی که در آن استفاده خواهد شد، اعلام کنیم. اجازه دهید به یک مثال معمولی نگاه کنم: for( final Locale locale: Locale.getAvailableLocales() ) { // блок codeа } try( final InputStream in = new FileInputStream( "file.txt" ) ) { // блока codeа } در هر دو قطعه کد، دامنه متغیرها محدود به بلوک های اجرایی است که این متغیرها در آن اعلان شده اند. هنگامی که بلوک کامل می شود، دامنه به پایان می رسد و متغیر نامرئی می شود. این واضح تر به نظر می رسد، اما با انتشار جاوا 8 و معرفی لامبدا، بسیاری از اصطلاحات شناخته شده این زبان که از متغیرهای محلی استفاده می کنند در حال منسوخ شدن هستند. اجازه دهید مثالی از مثال قبلی با استفاده از lambdas به جای حلقه بزنم: Arrays.stream( Locale.getAvailableLocales() ).forEach( ( locale ) -> { // блок codeа } ); می بینید که متغیر محلی به یک آرگومان برای تابع تبدیل شده است که به نوبه خود به عنوان آرگومان به متد forEach ارسال می شود .
3. فیلدهای کلاس و متغیرهای محلی
هر متد در جاوا به یک کلاس خاص تعلق دارد (یا در مورد Java8، یک رابط که در آن متد به عنوان روش پیش فرض اعلام شده است). بین متغیرهای محلی که فیلدهای یک کلاس یا روش‌های مورد استفاده در پیاده‌سازی هستند، احتمال تضاد نام وجود دارد. کامپایلر جاوا می داند که چگونه متغیر صحیح را از بین متغیرهای موجود انتخاب کند، حتی اگر بیش از یک توسعه دهنده قصد استفاده از آن متغیر را داشته باشند. IDEهای جاوای مدرن، از طریق هشدارهای کامپایلر و برجسته کردن متغیرها، به توسعه‌دهنده اطلاع می‌دهند که چنین درگیری‌هایی در شرف وقوع هستند. اما باز هم بهتر است هنگام نوشتن کد به چنین مواردی فکر کنید. پیشنهاد می کنم به یک مثال نگاه کنید: public class LocalVariableAndClassMember { private long value; public long calculateValue( final long initial ) { long value = initial; value *= 10; value += value; return value; } } مثال بسیار آسان به نظر می رسد، اما یک تله است. متد calculeValue یک مقدار متغیر محلی را معرفی می کند و با عمل بر روی آن، فیلد کلاس را با همین نام پنهان می کند. خط 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. آرگومان های روش و متغیرهای محلی
یکی دیگر از مشکلاتی که توسعه دهندگان بی تجربه جاوا اغلب در آن قرار می گیرند، استفاده از آرگومان های متد به عنوان متغیرهای محلی است. جاوا به شما اجازه می‌دهد تا مقادیر را به آرگومان‌های غیر ثابت تخصیص دهید (البته، این تأثیری بر مقدار اصلی ندارد): 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 نام تکنیکی است که در جاوا برای تبدیل انواع ابتدایی ( int، long، double و غیره ) به نوع متناظر ( Integer، Long، Double و غیره) استفاده می شود. در قسمت 4 آموزش نحوه و زمان استفاده از Generics، زمانی که در مورد بسته بندی انواع اولیه به عنوان پارامترهای نوع ژنریک صحبت کردم، این را در عمل مشاهده کردید. اگرچه کامپایلر جاوا تمام تلاش خود را می کند تا با انجام اتوباکسینگ، چنین تبدیل هایی را پنهان کند، اما گاهی اوقات این کمتر از حد انتظار است و نتایج غیرمنتظره ای را به همراه دارد. بیایید به یک مثال نگاه کنیم: public static void calculate( final long value ) { // блок codeа } final Long value = null; calculate( value ); قطعه کد بالا به خوبی کامپایل می شود. با این حال، NullPointerException را روی خطی که بین Long و long // блок تبدیل می‌کند، پرتاب می‌کند . توصیه برای چنین موردی این است که توصیه می شود از انواع اولیه استفاده کنید (با این حال، ما قبلاً می دانیم که این همیشه امکان پذیر نیست).
6. رابط ها
در قسمت 3 آموزش، "نحوه طراحی کلاس ها و رابط ها"، ما در مورد اینترفیس ها و برنامه نویسی قرارداد بحث کردیم، با تاکید بر این که رابط ها باید در صورت امکان بر کلاس های بتن ترجیح داده شوند. هدف این بخش تشویق شما به در نظر گرفتن واسط ها با نشان دادن آن با مثال های واقعی است. رابط ها به یک پیاده سازی خاص (به جز روش های پیش فرض) متصل نیستند. آنها فقط قرارداد هستند و به عنوان مثال، آزادی و انعطاف زیادی را در نحوه اجرای قراردادها فراهم می کنند. این انعطاف‌پذیری زمانی اهمیت بیشتری پیدا می‌کند که پیاده‌سازی شامل سیستم‌ها یا خدمات خارجی باشد. بیایید به مثالی از یک رابط ساده و اجرای احتمالی آن نگاه کنیم: 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 مورد نیاز است، استفاده کرد و اسکریپت تست از وابستگی به اجزای خارجی. بسیاری از نمونه‌های عالی استفاده مؤثر از چنین رابط‌هایی در کتابخانه استاندارد جاوا گنجانده شده‌اند. مجموعه‌ها، فهرست‌ها، مجموعه‌ها - این رابط‌ها پیاده‌سازی‌های متعددی دارند که می‌توان آنها را به‌طور یکپارچه تعویض کرد و زمانی که قراردادها سود می‌برند، قابل تعویض هستند. مثلا: 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. رشته ها
رشته ها یکی از پرکاربردترین انواع در جاوا و سایر زبان های برنامه نویسی هستند. زبان جاوا بسیاری از دستکاری‌های روتین رشته‌ها را با پشتیبانی از عملیات الحاق و مقایسه مستقیماً ساده می‌کند. علاوه بر این، کتابخانه استاندارد شامل کلاس های زیادی است که عملیات رشته را کارآمد می کند. این دقیقاً همان چیزی است که ما در این بخش به آن می پردازیم. در جاوا، رشته ها اشیاء تغییرناپذیر هستند که در رمزگذاری UTF-16 نشان داده می شوند. هر بار که رشته ها را به هم متصل می کنید (یا هر عملیاتی را انجام می دهید که رشته اصلی را تغییر می دهد)، نمونه جدیدی از کلاس String ایجاد می شود . به همین دلیل، عملیات الحاق می تواند بسیار ناکارآمد شود و باعث ایجاد بسیاری از نمونه های متوسط ​​از کلاس String شود (به طور کلی ایجاد زباله). اما کتابخانه استاندارد جاوا شامل دو کلاس بسیار مفید است که هدف آنها راحت کردن دستکاری رشته است. اینها 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 ); اغلب بهترین جایگزین برای ساده سازی الحاق، استفاده از قالب بندی رشته و همچنین کتابخانه استاندارد جاوا برای کمک به ارائه یک روش کمکی 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% کند . شایان ذکر است که IDE های جاوا مدرن می توانند مشخصات قالب را از آرگومان های ارسال شده به متد String.format تجزیه کنند و در صورت شناسایی ناهماهنگی به توسعه دهندگان هشدار دهند.
8. قراردادهای نامگذاری
جاوا زبانی است که توسعه‌دهندگان را مجبور نمی‌کند به طور دقیق از هیچ گونه قرارداد نام‌گذاری پیروی کنند، اما جامعه مجموعه‌ای از قوانین ساده را ایجاد کرده است که باعث می‌شود کد جاوا هم در کتابخانه استاندارد و هم در سایر پروژه‌های جاوا یکدست به نظر برسد:
  • نام بسته ها با حروف کوچک هستند: org.junit، com.fasterxml.jackson، javax.json
  • نام کلاس ها، شمارش ها، رابط ها، حاشیه نویسی ها با حروف بزرگ نوشته می شوند: StringBuilder، Runnable، @Override
  • نام فیلدها یا روش‌ها (به استثنای نهایی استاتیک ) در علامت شتر مشخص شده است: isEmpty، format، addAll
  • فیلدهای نهایی ثابت یا نام های ثابت شمارش با حروف بزرگ و با زیرخط ("_") از هم جدا شده اند: LOG، MIN_RADIX، INSTANCE.
  • متغیرهای محلی یا آرگومان‌های متد در نماد شتر تایپ می‌شوند: str، newLength، minimumCapacity
  • نام نوع پارامتر برای ژنریک ها با یک حرف به صورت بزرگ نشان داده می شود: T، U، E
با پیروی از این قراردادهای ساده، کدی که می نویسید مختصر و غیرقابل تشخیص از کتابخانه یا چارچوب دیگری به نظر می رسد و احساس می کند که توسط همان شخص ایجاد شده است (یکی از موارد نادری که قراردادها واقعاً کار می کنند).
9. کتابخانه های استاندارد
مهم نیست که روی چه نوع پروژه جاوا کار می کنید، کتابخانه های استاندارد جاوا بهترین دوستان شما هستند. بله، سخت است مخالفت کنید که آنها لبه های خشن و تصمیمات طراحی عجیب دارند، با این حال، 99٪ مواقع کد با کیفیت بالا توسط متخصصان نوشته شده است. ارزش کاوش را دارد. هر نسخه جاوا بسیاری از ویژگی‌های جدید را به کتابخانه‌های موجود می‌آورد (با برخی مشکلات احتمالی با ویژگی‌های قدیمی)، و همچنین بسیاری از کتابخانه‌های جدید را اضافه می‌کند. جاوا 5 یک کتابخانه همزمان جدید را به عنوان بخشی از بسته java.util.concurrent آورده است . جاوا 6 پشتیبانی (کمتر شناخته شده) برای اسکریپت نویسی ( بسته javax.script ) و یک API کامپایلر جاوا (به عنوان بخشی از بسته javax.tools ) ارائه می کند. جاوا 7 پیشرفت های زیادی را در java.util.concurrent به ارمغان آورد و یک کتابخانه ورودی/خروجی جدید در بسته java.nio.file و پشتیبانی از زبان های پویا در java.lang.invoke معرفی کرد . و در نهایت، جاوا 8 تاریخ/زمان مورد انتظار را در بسته java.time اضافه کرد . جاوا به عنوان یک پلتفرم در حال تکامل است و پیشرفت آن در کنار تغییرات فوق بسیار مهم است. هر زمان که در نظر دارید یک کتابخانه یا فریم ورک شخص ثالث را در پروژه خود بگنجانید، مطمئن شوید که عملکرد مورد نیاز از قبل در کتابخانه های استاندارد جاوا موجود نیست (البته، بسیاری از پیاده سازی های تخصصی و با کارایی بالا از الگوریتم ها وجود دارند که جلوتر از آنها هستند. الگوریتم‌ها در کتابخانه‌های استاندارد، اما در بیشتر موارد واقعاً مورد نیاز نیستند).
10. تغییر ناپذیری
تغییرناپذیری در سرتاسر راهنما و در این قسمت به عنوان یادآوری باقی می ماند: لطفا آن را جدی بگیرید. اگر کلاسی که طراحی می‌کنید یا روشی که پیاده‌سازی می‌کنید می‌تواند تضمین تغییرناپذیری را ارائه دهد، می‌توان آن را در بیشتر موارد در همه جا بدون ترس از تغییر همزمان استفاده کرد. این زندگی شما را به عنوان یک توسعه دهنده (و امیدوارم زندگی اعضای تیم شما) آسان تر کند.
11. آزمایش
تمرین توسعه تست محور (TDD) در جامعه جاوا بسیار محبوب است و کیفیت کد را بالا می برد. با تمام مزایایی که TDD ارائه می کند، مایه تاسف است که می بینیم کتابخانه استاندارد جاوا امروزه شامل هیچ چارچوب تست یا ابزار پشتیبانی نمی شود. با این حال، آزمایش به بخشی ضروری از توسعه جاوا مدرن تبدیل شده است و در این بخش به چند تکنیک اساسی با استفاده از چارچوب 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" ) ); } } هر دو تست بسیار خوانا به نظر می رسند و اجرای آنها نمونه است. امروزه، متوسط ​​پروژه جاوا شامل صدها مورد آزمایشی است که به توسعه‌دهنده بازخورد سریع در طول فرآیند توسعه درباره رگرسیون‌ها یا ویژگی‌ها می‌دهد.
12. بعد
این بخش از راهنما مجموعه ای از مباحث مربوط به تمرین برنامه نویسی در جاوا و کتابچه راهنمای این زبان برنامه نویسی را تکمیل می کند. دفعه بعد به ویژگی های زبان باز می گردیم و دنیای جاوا را در مورد استثناها، انواع آنها، نحوه و زمان استفاده از آنها بررسی می کنیم.
13. کد منبع را دانلود کنید
این یک درس در مورد اصول کلی توسعه از دوره جاوا پیشرفته بود. کد منبع درس را می توانید از اینجا دانلود کنید .
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION