JavaRush /Java блогу /Random-KY /Жалпы программалоо стили боюнча колдонмо
pandaFromMinsk
Деңгээл
Минск

Жалпы программалоо стили боюнча колдонмо

Группада жарыяланган
Бул макала "Advanced Java" академиялык курсунун бир бөлүгү болуп саналат. Бул курс Java функцияларын кантип натыйжалуу колдонууну үйрөнүүгө жардам берүү үчүн иштелип чыккан. Материал an objectти түзүү, атаандаштык, сериялаштыруу, рефлексия ж.б. сыяктуу "өнүккөн" темаларды камтыйт. Курс Java ыкмаларын кантип натыйжалуу өздөштүрүү керектигин үйрөтөт. Чоо-жайы бул жерде .
Мазмун
1. Киришүү 2. Өзгөрмөлөрдүн масштабы 3. Класс талаалары жана локалдык өзгөрмөлөр 4. Метод аргументтери жана жергorктүү өзгөрмөлөр 5. Бокс жана кутудан чыгаруу 6. Интерфейстер 7. Саптар 8. Ат коюу конвенциялары 9. Стандарттык китепканалар 10. Өзгөрбөстүк 11. Кийинки Сыноо. 12. .. 13. Баштапкы codeду жүктөп алыңыз
1. Киришүү
Окутуунун бул бөлүгүндө биз Java тorнде жакшы программалоо стorнин жана жооп берүүчү дизайндын жалпы принциптерин талкуулоону улантабыз. Биз бул принциптердин айрымдарын колдонмонун мурунку бөлүмдөрүндө көрдүк, бирок Java иштеп чыгуучунун көндүмдөрүн жогорулатуу максатында көптөгөн практикалык кеңештер берилет.
2. Өзгөрмө чөйрөсү
Үчүнчү бөлүктө («Класстарды жана интерфейстерди кантип долбоорлоо керек») биз масштабдагы чектөөлөрдү эске алуу менен класстардын жана интерфейстердин мүчөлөрүнө көрүнүү жана жеткorктүүлүктү кантип колдонсо болорун талкууладык. Бирок, биз методду ишке ашырууда колдонулган жергorктүү өзгөрмөлөрдү талкуулай элекпиз. Java тorнде ар бир локалдык өзгөрмө бир жолу жарыялангандан кийин масштабга ээ. Бул өзгөрмө жарыяланган жерден методдун (же code блогунун) аткарылышы аяктаганга чейин көрүнүп турат. Жалпысынан, локалдык өзгөрмө колдонула турган жерге мүмкүн болушунча жакын жарыялоо гана эрежени сактайт. Типтүү мисалды карап көрөйүн: for( final Locale locale: Locale.getAvailableLocales() ) { // блок codeа } try( final InputStream in = new FileInputStream( "file.txt" ) ) { // блока codeа } эки code фрагментинде тең өзгөрмөлөрдүн масштабы бул өзгөрмөлөр жарыяланган аткаруу блоктору менен чектелет. Блок аяктаганда, масштаб бүтөт жана өзгөрмө көрүнбөй калат. Бул айкыныраак көрүнөт, бирок Java 8дин чыгышы жана ламбдалардын ишке кириши менен жергorктүү өзгөрмөлөрдү колдонгон тилдин белгилүү идиомаларынын көбү эскирип баратат. Мурунку мисалдан циклдин ордуна ламбдаларды колдонуу менен мисал келтирейин: Arrays.stream( Locale.getAvailableLocales() ).forEach( ( locale ) -> { // блок codeа } ); Локалдык өзгөрмө функциянын аргументине айланганын, ал өз кезегинде forEach ыкмасына аргумент катары берилгенин көрүүгө болот .
3. Класс талаалары жана жергorктүү өзгөрмөлөр
Javaдагы ар бир ыкма белгилүү бир класска таандык (же Java8де, метод демейки ыкма катары жарыяланган интерфейс). Класстын талаалары же ишке ашырууда колдонулган ыкмалар болгон локалдык өзгөрмөлөрдүн ортосунда аттардын конфликтинин ушундай мүмкүнчүлүгү бар. Java компилятору бир нече иштеп чыгуучу ошол өзгөрмөнү колдонууга ниеттенгенине карабастан, колдо болгондордун ичинен туура өзгөрмөнү кантип тандоону билет. Заманбап Java IDEлери компилятордун эскертүүлөрү жана өзгөрмөлөрдү бөлүп көрсөтүү аркылуу иштеп чыгуучуга мындай чыр-чатактар ​​качан пайда болоорун айтып берүү үчүн чоң жумуш аткарат. Бирок code жазып жатканда ушундай нерселер жөнүндө ойлонгонуңуз жакшы. Мен бир мисалды карап чыгууну сунуштайм: public class LocalVariableAndClassMember { private long value; public long calculateValue( final long initial ) { long value = initial; value *= 10; value += value; return value; } } Мисал абдан оңой көрүнөт, бирок бул тузак. AccountValue ыкмасы локалдык өзгөрмө маанини киргизет жана анын үстүндө иштеп, ошол эле ат менен класс талаасын жашырат. Сап value += value; класс талаасынын жана жергorктүү өзгөрмөнүн маанисинин суммасы болушу керек, бирок анын ордуна башка нерсе жасалып жатат. Туура ишке ашыруу төмөнкүдөй көрүнөт (бул ачкыч сөздү колдонуу менен): public class LocalVariableAndClassMember { private long value; public long calculateValue( final long initial ) { long value = initial; value *= 10; value += this.value; return value; } } Бул мисал кандайдыр бир жагынан жөнөкөй болсо да, кээ бир учурларда мүчүлүштүктөрдү оңдоо жана оңдоо бир нече саатка созулушу мүмкүн экенин көрсөтүп турат.
4. Метод аргументтери жана жергorктүү өзгөрмөлөр
Тажрыйбасыз Java иштеп чыгуучулары көп кездешкен дагы бир тузак бул метод аргументтерин жергorктүү өзгөрмөлөр катары колдонуу. Java сизге баалуулуктарды туруктуу эмес аргументтерге кайра дайындоого мүмкүндүк берет (бирок бул баштапкы мааниге эч кандай таасир этпейт): public String sanitize( String str ) { if( !str.isEmpty() ) { str = str.trim(); } str = str.toLowerCase(); return str; } Жогорудагы code үзүндүсү жарашыктуу эмес, бирок ал көйгөйдү ачууда жакшы иштейт: аргумент str дайындалган. башка маани (жана негизинен жергorктүү өзгөрмө катары колдонулат) . Бардык учурларда (эч кандай тышкаркысыз) бул мисалсыз (мисалы, аргументтерди константалар катары жарыялоо менен) кыла аласыз жана аткарышыңыз керек. Мисалы: public String sanitize( final String str ) { String sanitized = str; if( !str.isEmpty() ) { sanitized = str.trim(); } sanitized = sanitized.toLowerCase(); return sanitized; } Бул жөнөкөй эрежени сактоо менен, локалдык өзгөрмөлөрдү киргизип жатканда да, берилген codeду издөө жана маселенин булагын табуу оңой болот.
5. Таңгактоо жана таңгактан чыгаруу
Boxing and unboxing – Java тorнде примитивдүү типтерди ( int, long, double , ж . Generics кантип жана качан колдонуу боюнча окуу куралынын 4-бөлүгүндө, мен примитивдик типтерди генериктердин тип параметрлери катары таңуу жөнүндө сүйлөшкөндө сиз муну иш жүзүндө көрдүңүз. Java компилятору автобоксту аткаруу менен мындай конversionларды жашырууга болгон аракетин жумшаса да, кээде бул күтүлгөндөн азыраак болуп, күтүлбөгөн натыйжаларды берет. Мисалга карап көрөлү: public static void calculate( final long value ) { // блок codeа } final Long value = null; calculate( value ); Жогорудагы code үзүндүсү жакшы компиляцияланат. Бирок, ал Long жана long ортосунда өзгөрүп жаткан сызыкта NullPointerException ыргытат . Мындай учурда, бул примитивдүү түрлөрүн колдонуу максатка ылайыктуу болуп саналат (бирок, бул дайыма эле мүмкүн эмес экенин билебиз). // блок
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(); } } Жогорудагы code үзүндүсү типтүү интерфейс үлгүсүн жана аны ишке ашырууну көрсөтөт. Бул ишке ашыруу белгилүү бир жердин убакыт алкагын алуу үчүн тышкы HTTP кызматын ( http://api.geonames.org/ ) колдонот. Бирок, анткени келишим интерфейстен көз каранды, мисалы, маалымат базасын же кадимки жалпак файлды колдонуу менен интерфейстин башка ишке ашыруусун киргизүү абдан оңой. Алар менен интерфейстер сыналуучу codeду иштеп чыгууда абдан пайдалуу. Мисалы, ар бир тестте тышкы кызматтарды чакыруу дайыма эле практикалык боло бербейт, андыктан анын ордуна альтернативалуу, эң жөнөкөй ишке ашырууну (мисалы, stub) ишке ашыруунун мааниси бар: Бул ишке ашыруу TimezoneService интерфейси талап кылынган public class TimezoneServiceTestImpl implements TimezoneService { @Override public TimeZone getTimeZone(final double lat, final double lon) throws IOException { return TimeZone.getDefault(); } } бардык жерде колдонулушу мүмкүн . скриптти тышкы компоненттерге көз карандылыктан сыноо. Мындай интерфейстерди эффективдүү колдонуунун көптөгөн сонун мисалдары 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. Саптар
Strings Java жана башка программалоо тилдеринде эң көп колдонулган типтердин бири. Java тor конкатенация жана салыштыруу операцияларын кутудан эле колдоп, көптөгөн күнүмдүк сап манипуляцияларын жөнөкөйлөтөт. Мындан тышкары, стандарттык китепкана сап операцияларын эффективдүү кылган көптөгөн класстарды камтыйт. Дал ушул нерсе биз бул бөлүмдө талкуулайбыз. Java тorнде саптар UTF-16 codeдоосунда көрсөтүлгөн өзгөрбөс an objectтер. Сиз саптарды бириктирген сайын (же баштапкы сапты өзгөрткөн кандайдыр бир операцияны аткарганда), String классынын жаңы үлгүсү түзүлөт . Ушундан улам, бириктирүү операциясы өтө натыйжасыз болуп, String классынын көптөгөн аралык инстанцияларынын түзүлүшүнө алып келиши мүмкүн (жалпысынан таштанды жаратат). Бирок 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 жардамчы ыкмасын камсыздоого жардам берет . Бул формат спецификаторлорунун бай топтомун колдойт, анын ичинде сандар, символдор, дата/убакыт ж.б. (Толук маалымат алуу үчүн маалымдама documentациясын караңыз) 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% ар кандай маалымат түрлөрүнөн саптарды түзүү үчүн таза жана жеңил ыкманы камсыз кылат. Белгилей кетсек, заманбап Java IDEлери String.format ыкмасына берилген аргументтерден формат спецификациясын талдап , кандайдыр бир дал келбестиктер аныкталса, иштеп чыгуучуларга эскерте алат.
8. Ат коюу шарты
Java - бул иштеп чыгуучуларды эч кандай ат коюу конвенциясын так аткарууга мажбурлабаган тил, бирок коомчулук Java codeун стандарттуу китепканада да, башка Java долбоорлорунда да ырааттуу кылган жөнөкөй эрежелердин топтомун иштеп чыккан:
  • пакеттердин аталыштары кичине тамгалар менен жазылган: org.junit, com.fasterxml.jackson, javax.json
  • класстардын аттары, тизмектер, интерфейстер, annotationлар баш тамга менен жазылат: StringBuilder, Runnable, @Override
  • талаалардын же методдордун аттары ( статикалык финалдан башкасы ) төө белгилеринде көрсөтүлөт: isEmpty, format, addAll
  • статикалык акыркы талаа же санап чыгуунун туруктуу аталыштары баш тамга менен, астынкы сызыктар менен бөлүнгөн ("_"): LOG, MIN_RADIX, INSTANCE.
  • жергorктүү өзгөрмөлөр же метод аргументтери төө белгиси менен терилген: str, newLength, minimumCapacity
  • генериктердин параметр түрүнүн аталыштары жогорку регистрдеги бир тамга менен көрсөтүлөт: T, U, E
Ушул жөнөкөй конвенцияларды аткаруу менен, сиз жазган code кыска жана стor боюнча башка китепканадан же алHowтардан айырмалангыс болуп көрүнөт жана аны бир адам иштеп чыккандай сезет (конвенциялар иш жүзүндө иштеген сейрек учурдун бири).
9. Стандарттык китепканалар
Кандай гана Java долбоорунун үстүндө иштеп жатпаңыз, Java стандарттык китепканалары сиздин эң жакшы досторуңуз. Ооба, алардын кээ бир орой четтери жана кызыктай дизайн чечимдери бар экенине макул болбоо кыйын, бирок 99% убакыттын ичинде бул эксперттер тарабынан жазылган жогорку сапаттагы code. Бул изилдөөгө арзырлык. Ар бир Java чыгарылышы учурдагы китепканаларга көптөгөн жаңы функцияларды алып келет (эски функциялар менен мүмкүн болгон көйгөйлөр менен), ошондой эле көптөгөн жаңы китепканаларды кошот. Java 5 java.util.concurrent пакетинин бир бөлүгү катары жаңы конкуренттик китепкананы алып келди . Java 6 скрипт ( javax.script пакети) жана Java компилятор API ( javax.tools пакетинин бир бөлүгү катары) үчүн (азыраак белгилүү) колдоо көрсөткөн . Java 7 java.util.concurrent программасына көптөгөн өркүндөтүүлөр алып келип , java.nio.file пакетинде жаңы I/O китепканасын жана java.lang.invoke динамикалык тилдерин колдоону киргизди . Акыр-аягы, Java 8 көптөн күткөн датаны/убакытты java.time пакетине кошту . Java платформа катары өнүгүп жатат жана анын жогорудагы өзгөрүүлөр менен бирге өнүгүүсү абдан маанилүү. Долбооруңузга үчүнчү тараптын китепканасын же алкагын кошууну ойлонгон сайын, талап кылынган функция стандарттуу Java китепканаларында камтылган эмес экенине ынаныңыз (албетте, алгоритмдердин көптөгөн адистештирилген жана жогорку натыйжалуу ишке ашыруулары бар. стандарттык китепканалардагы алгоритмдер, бирок көпчүлүк учурларда алар чындыгында керек эмес).
10. Өзгөрбөстүк
Колдонмонун бүткүл бөлүгүндө жана бул бөлүктө өзгөрбөстүк эскертүү катары сакталат: ага олуттуу мамиле жасаңыз. Эгерде сиз иштеп чыккан класс же сиз ишке ашырган ыкма өзгөрүлбөстүктү камсыз кыла турган болсо, аны көпчүлүк учурларда бир эле учурда өзгөртүлүп калуудан коркпостон колдонсо болот. Бул иштеп чыгуучу катары жашооңузду (жана командаңыздын мүчөлөрүнүн жашоосун) жеңилдетет.
11. Сыноо
Тестке негизделген иштеп чыгуу практикасы (TDD) Java коомчулугунда абдан популярдуу болуп, codeдун сапаты үчүн штрихти жогорулатат. TDD берген бардык артыкчылыктары менен, Java стандарттык китепканасы бүгүнкү күндө эч кандай тест алHowтарын же колдоо куралдарын камтыбаганын көрүү өкүнүчтүү. Бирок, тестирлөө заманбап Java өнүктүрүүнүн зарыл бөлүгү болуп калды жана бул бөлүмдө биз JUnit алкагын колдонуу менен бир нече негизги ыкмаларды карап чыгабыз . JUnitте, негизинен, ар бир тест an objectтин күтүлгөн абалы же жүрүм-туруму жөнүндө билдирүүлөрдүн жыйындысы болуп саналат. Улуу тесттерди жазуунун сыры - аларды жөнөкөй жана кыска кылып, бир эле учурда бир нерсени сынап көрүү. Көнүгүү катары, келгиле, 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 тorнде программалоо практикасына байланыштуу бир катар талкууларды жана бул программалоо тor үчүн колдонмолорду аяктайт. Кийинки жолу биз тилдин өзгөчөлүктөрүнө кайрылып, өзгөчө учурлар, алардын түрлөрү, аларды кантип жана качан колдонуу керектиги жөнүндө Java дүйнөсүн изилдейбиз.
13. Баштапкы codeду жүктөп алыңыз
Бул Advanced Java курсунан жалпы өнүгүү принциптери боюнча сабак болду. Сабактын баштапкы codeун бул жерден көчүрүп алса болот .
Комментарийлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION