JavaRush /Java Blog /Random-TL /Gabay sa Pangkalahatang Estilo ng Programming
pandaFromMinsk
Antas
Минск

Gabay sa Pangkalahatang Estilo ng Programming

Nai-publish sa grupo
Ang artikulong ito ay bahagi ng kursong pang-akademiko na "Advanced Java." Ang kursong ito ay idinisenyo upang tulungan kang matutunan kung paano epektibong gumamit ng mga feature ng Java. Ang materyal ay sumasaklaw sa "advanced" na mga paksa tulad ng paggawa ng bagay, kumpetisyon, serialization, reflection, atbp. Ang kurso ay magtuturo sa iyo kung paano epektibong makabisado ang mga diskarte sa Java. Mga detalye dito .
Nilalaman
1. Panimula 2. Saklaw ng Variable 3. Mga Field ng Klase at Lokal na Variable 4. Mga Argumento ng Pamamaraan at Lokal na Variable 5. Boxing at Unboxing 6. Mga Interface 7. Mga String 8. Mga Kumbensyon sa Pangalan 9. Mga Standard na Aklatan 10. Immutability 11. Pagsubok 12. Susunod. .. 13. I-download ang source code
1. Panimula
Sa bahaging ito ng tutorial, ipagpapatuloy namin ang aming pagtalakay sa mga pangkalahatang prinsipyo ng magandang istilo ng programming at tumutugon na disenyo sa Java. Nakita na namin ang ilan sa mga prinsipyong ito sa mga nakaraang kabanata ng gabay, ngunit maraming praktikal na tip ang ibibigay na may layuning pahusayin ang mga kasanayan ng isang developer ng Java.
2. Variable na saklaw
Sa Ikatlong Bahagi ("Paano Magdisenyo ng Mga Klase at Interface"), tinalakay namin kung paano mailalapat ang visibility at accessibility sa mga miyembro ng mga klase at interface, dahil sa mga paghihigpit sa saklaw. Gayunpaman, hindi pa namin napag-uusapan ang mga lokal na variable na ginagamit sa mga pagpapatupad ng pamamaraan. Sa wikang Java, ang bawat lokal na variable, kapag ipinahayag, ay may saklaw. Ang variable na ito ay makikita mula sa lugar kung saan ito idineklara hanggang sa punto kung saan nakumpleto ang pagpapatupad ng pamamaraan (o block ng code). Sa pangkalahatan, ang tanging tuntunin na dapat sundin ay ang magdeklara ng isang lokal na variable na mas malapit hangga't maaari sa lugar kung saan ito gagamitin. Hayaan akong tumingin sa isang tipikal na halimbawa: for( final Locale locale: Locale.getAvailableLocales() ) { // блок codeа } try( final InputStream in = new FileInputStream( "file.txt" ) ) { // блока codeа } Sa parehong mga fragment ng code, ang saklaw ng mga variable ay limitado sa mga bloke ng pagpapatupad kung saan idineklara ang mga variable na ito. Kapag nakumpleto na ang block, matatapos ang saklaw at magiging invisible ang variable. Ito ay tila mas malinaw, ngunit sa paglabas ng Java 8 at ang pagpapakilala ng mga lambdas, marami sa mga kilalang idyoma ng wika na gumagamit ng mga lokal na variable ay nagiging hindi na ginagamit. Hayaan akong magbigay ng isang halimbawa mula sa nakaraang halimbawa gamit ang lambdas sa halip na isang loop: Arrays.stream( Locale.getAvailableLocales() ).forEach( ( locale ) -> { // блок codeа } ); Makikita na ang lokal na variable ay naging argumento sa function, na siya namang ipinasa bilang argumento sa forEach method .
3. Mga field ng klase at mga lokal na variable
Ang bawat pamamaraan sa Java ay kabilang sa isang partikular na klase (o, sa kaso ng Java8, isang interface kung saan ang pamamaraan ay idineklara bilang default na paraan). Sa pagitan ng mga lokal na variable na mga patlang ng isang klase o mga pamamaraan na ginamit sa pagpapatupad, mayroong, dahil dito, isang posibilidad ng pagkakasalungatan ng pangalan. Alam ng Java compiler kung paano pumili ng tamang variable mula sa mga available, kahit na higit sa isang developer ang nagnanais na gamitin ang variable na iyon. Ang mga modernong Java IDE ay gumagawa ng isang mahusay na trabaho ng pagsasabi sa developer kung kailan magaganap ang mga naturang salungatan, sa pamamagitan ng mga babala ng compiler at variable na pag-highlight. Ngunit mas mabuti pa ring isipin ang mga ganitong bagay habang nagsusulat ng code. Iminumungkahi kong tumingin sa isang halimbawa: public class LocalVariableAndClassMember { private long value; public long calculateValue( final long initial ) { long value = initial; value *= 10; value += value; return value; } } Ang halimbawa ay mukhang medyo madali, ngunit ito ay isang bitag. Ang pamamaraan ng kalkulasyon ay nagpapakilala ng isang lokal na halaga ng variable at, na tumatakbo dito, itinatago ang field ng klase na may parehong pangalan. Ang linya value += value; ay dapat ang kabuuan ng halaga ng field ng klase at ang lokal na variable, ngunit sa halip, may iba pang ginagawa. Magiging ganito ang isang maayos na pagpapatupad (gamit ang keyword na ito): public class LocalVariableAndClassMember { private long value; public long calculateValue( final long initial ) { long value = initial; value *= 10; value += this.value; return value; } } Bagama't walang muwang ang halimbawang ito sa ilang paraan, nagpapakita ito ng mahalagang punto na sa ilang pagkakataon ay maaaring tumagal ng ilang oras upang i-debug at ayusin.
4. Mga argumento ng pamamaraan at mga lokal na variable
Ang isa pang pitfall na kadalasang nahuhulog sa mga walang karanasan na mga developer ng Java ay ang paggamit ng mga argumento ng pamamaraan bilang mga lokal na variable. Binibigyang-daan ka ng Java na muling italaga ang mga halaga sa mga hindi pare-parehong argumento (gayunpaman, ito ay walang epekto sa orihinal na halaga): public String sanitize( String str ) { if( !str.isEmpty() ) { str = str.trim(); } str = str.toLowerCase(); return str; } Ang code snippet sa itaas ay hindi elegante, ngunit ito ay isang mahusay na trabaho ng pagtuklas ng problema: ang argument str ay itinalaga ibang halaga (at karaniwang ginagamit bilang lokal na variable) . Sa lahat ng kaso (nang walang anumang pagbubukod), magagawa mo at dapat mong gawin nang wala ang halimbawang ito (halimbawa, sa pamamagitan ng pagdedeklara ng mga argumento bilang mga constant). Halimbawa: public String sanitize( final String str ) { String sanitized = str; if( !str.isEmpty() ) { sanitized = str.trim(); } sanitized = sanitized.toLowerCase(); return sanitized; } Sa pamamagitan ng pagsunod sa simpleng panuntunang ito, mas madaling masubaybayan ang ibinigay na code at hanapin ang pinagmulan ng problema, kahit na nagpapakilala ng mga lokal na variable.
5. Pag-iimpake at pag-unpack
Ang boxing at unboxing ay ang pangalan ng isang pamamaraan na ginagamit sa Java upang i-convert ang mga primitive na uri ( int, long, double, atbp. ) sa mga katumbas na uri ng wrapper ( Integer, Long, Double , atbp.). Sa Bahagi 4 ng tutorial na Paano at Kailan Gumamit ng Generics, nakita mo na ito sa pagkilos noong pinag-usapan ko ang tungkol sa pagbabalot ng mga primitive na uri bilang mga parameter ng uri ng generics. Bagama't sinusubukan ng Java compiler ang lahat ng makakaya upang itago ang mga naturang conversion sa pamamagitan ng pagsasagawa ng autoboxing, minsan ito ay mas mababa kaysa sa inaasahan at nagdudulot ng mga hindi inaasahang resulta. Tingnan natin ang isang halimbawa: public static void calculate( final long value ) { // блок codeа } final Long value = null; calculate( value ); Ang snippet ng code sa itaas ay naipon nang maayos. Gayunpaman, maglalagay ito ng NullPointerException sa linya // блок kung saan nagko-convert ito sa pagitan ng Long at long . Ang payo para sa naturang kaso ay ipinapayong gumamit ng mga primitive na uri (gayunpaman, alam na natin na hindi ito laging posible).
6. Mga Interface
Sa Part 3 ng tutorial, "How to Design Classes and Interfaces," tinalakay namin ang mga interface at contract programming, na binibigyang-diin na ang mga interface ay dapat na mas gusto kaysa sa mga kongkretong klase hangga't maaari. Ang layunin ng seksyong ito ay hikayatin kang isaalang-alang muna ang mga interface sa pamamagitan ng pagpapakita nito sa mga totoong buhay na halimbawa. Ang mga interface ay hindi nakatali sa isang partikular na pagpapatupad (maliban sa mga default na pamamaraan). Ang mga ito ay mga kontrata lamang at, bilang isang halimbawa, nagbibigay sila ng maraming kalayaan at kakayahang umangkop sa paraan ng pagpapatupad ng mga kontrata. Ang kakayahang umangkop na ito ay nagiging mas mahalaga kapag ang pagpapatupad ay nagsasangkot ng mga panlabas na sistema o serbisyo. Tingnan natin ang isang halimbawa ng isang simpleng interface at ang posibleng pagpapatupad nito: 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(); } } Ang snippet ng code sa itaas ay nagpapakita ng karaniwang pattern ng interface at ang pagpapatupad nito. Gumagamit ang pagpapatupad na ito ng panlabas na serbisyo ng HTTP ( http://api.geonames.org/ ) upang makuha ang time zone ng isang partikular na lokasyon. Gayunpaman, dahil ang kontrata ay nakasalalay sa interface, napakadaling ipakilala ang isa pang pagpapatupad ng interface, gamit, halimbawa, isang database o kahit isang regular na flat file. Sa kanila, ang mga interface ay lubhang nakakatulong sa pagdidisenyo ng masusubok na code. Halimbawa, hindi palaging praktikal na tumawag sa mga panlabas na serbisyo sa bawat pagsubok, kaya makatuwiran na sa halip ay magpatupad ng alternatibo, pinakasimpleng pagpapatupad (tulad ng stub): public class TimezoneServiceTestImpl implements TimezoneService { @Override public TimeZone getTimeZone(final double lat, final double lon) throws IOException { return TimeZone.getDefault(); } } Magagamit ang pagpapatupad na ito kahit saan kung saan kinakailangan ang interface ng TimezoneService , na ihiwalay ang test script mula sa dependency sa mga panlabas na bahagi . Maraming mahuhusay na halimbawa ng epektibong paggamit ng naturang mga interface ay naka-encapsulated sa loob ng Java standard library. Mga koleksyon, listahan, set - ang mga interface na ito ay may maraming pagpapatupad na maaaring maayos na ipagpalit at maaaring palitan kapag sinamantala ng mga kontrata. Halimbawa: 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. Mga string
Ang mga string ay isa sa mga pinaka ginagamit na uri sa parehong Java at iba pang mga programming language. Pinapasimple ng wikang Java ang maraming nakagawiang manipulasyon ng string sa pamamagitan ng pagsuporta sa mga operasyon ng pagsasama-sama at paghahambing mula mismo sa kahon. Bilang karagdagan, ang karaniwang library ay naglalaman ng maraming klase na ginagawang mahusay ang mga operasyon ng string. Ito mismo ang tatalakayin natin sa seksyong ito. Sa Java, ang mga string ay mga hindi nababagong bagay na kinakatawan sa UTF-16 encoding. Sa tuwing magsasama-sama ka ng mga string (o magsagawa ng anumang operasyon na nagbabago sa orihinal na string), isang bagong instance ng String class ang nalilikha . Dahil dito, ang pagpapatakbo ng concatenation ay maaaring maging lubhang hindi epektibo, na nagiging sanhi ng maraming intermediate na pagkakataon ng String class na malikha (paglikha ng basura, sa pangkalahatan). Ngunit ang Java standard library ay naglalaman ng dalawang napaka-kapaki-pakinabang na klase na ang layunin ay gawing maginhawa ang pagmamanipula ng string. Ang mga ito ay StringBuilder at StringBuffer (ang tanging pagkakaiba sa pagitan nila ay ang StringBuffer ay ligtas sa thread habang ang StringBuilder ay ang kabaligtaran). Tingnan natin ang ilang halimbawa ng isa sa mga klase na ito na ginagamit: 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(), "]" ); Habang ginagamit ang StringBuilder/StringBuffer ay ang inirerekomendang paraan upang manipulahin ang mga string, maaaring magmukhang overkill ito sa pinakasimpleng senaryo ng pagsasama-sama ng dalawa o tatlong string, upang ang normal na operator ng karagdagan ( ( "+"), halimbawa: String userId = "user:" + new Random().nextInt( 100 ); Kadalasan ang pinakamahusay na alternatibo upang pasimplehin ang concatenation ay ang paggamit ng string formatting gayundin ang Java Standard Library upang makatulong na magbigay ng static na String.format helper method . Sinusuportahan nito ang isang rich set ng format specifiers, kabilang ang mga numero, simbolo, petsa/oras, atbp. (Sumangguni sa reference na dokumentasyon para sa kumpletong mga detalye) Ang 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% method ay nagbibigay ng malinis at magaan na diskarte sa pagbuo ng mga string mula sa iba't ibang uri ng data. Kapansin-pansin na maaaring i-parse ng mga modernong Java IDE ang detalye ng format mula sa mga argumentong ipinasa sa paraan ng String.format at bigyan ng babala ang mga developer kung may matukoy na hindi pagkakatugma.
8. Pagpapangalan sa mga kumbensyon
Ang Java ay isang wika na hindi pumipilit sa mga developer na mahigpit na sundin ang anumang kombensiyon ng pagbibigay ng pangalan, ngunit ang komunidad ay nakabuo ng isang hanay ng mga simpleng panuntunan na ginagawang pare-pareho ang Java code sa karaniwang library at sa anumang iba pang proyekto ng Java:
  • nasa lowercase ang mga pangalan ng package: org.junit, com.fasterxml.jackson, javax.json
  • ang mga pangalan ng mga klase, enumerasyon, interface, annotation ay nakasulat gamit ang malaking titik: StringBuilder, Runnable, @Override
  • ang mga pangalan ng mga field o pamamaraan (maliban sa static na final ) ay tinukoy sa camel notation: isEmpty, format, addAll
  • static na final field o enumeration constant na mga pangalan ay nasa uppercase, na pinaghihiwalay ng mga underscore ("_"): LOG, MIN_RADIX, INSTANCE.
  • Ang mga lokal na variable o argumento ng pamamaraan ay nai-type sa notasyon ng kamelyo: str, newLength, minimumCapacity
  • Ang mga pangalan ng uri ng parameter para sa mga generic ay kinakatawan ng isang titik sa uppercase: T, U, E
Sa pamamagitan ng pagsunod sa mga simpleng convention na ito, ang code na isusulat mo ay magmumukhang maigsi at hindi makikilala sa istilo mula sa isa pang library o framework, at mararamdaman na isinulat ito ng parehong tao (isa sa mga bihirang pagkakataon na talagang gumagana ang mga convention ).
9. Mga karaniwang aklatan
Anuman ang uri ng proyekto ng Java na iyong ginagawa, ang mga karaniwang aklatan ng Java ay iyong matalik na kaibigan. Oo, mahirap hindi sumang-ayon na mayroon silang ilang mga magaspang na gilid at kakaibang mga desisyon sa disenyo, gayunpaman, 99% ng oras na ito ay may mataas na kalidad na code na isinulat ng mga eksperto. Ito ay nagkakahalaga ng paggalugad. Ang bawat release ng Java ay nagdadala ng maraming bagong feature sa mga kasalukuyang library (na may ilang posibleng isyu sa mga lumang feature), at nagdaragdag din ng maraming bagong library. Nagdala ang Java 5 ng bagong concurrency library bilang bahagi ng java.util.concurrent package . Ang Java 6 ay nagbigay (hindi gaanong kilala) ng suporta para sa scripting ( ang javax.script package) at isang Java compiler API (bilang bahagi ng javax.tools package ). Ang Java 7 ay nagdala ng maraming pagpapahusay sa java.util.concurrent , na nagpapakilala ng bagong I/O library sa java.nio.file package at suporta para sa mga dynamic na wika sa java.lang.invoke . At sa wakas, idinagdag ng Java 8 ang pinakahihintay na petsa/oras sa java.time package . Ang Java bilang isang platform ay umuunlad at napakahalaga para dito na umunlad kasama ng mga pagbabago sa itaas. Sa tuwing isasaalang-alang mo ang pagsasama ng isang third-party na library o framework sa iyong proyekto, siguraduhin na ang kinakailangang functionality ay hindi pa nakapaloob sa mga karaniwang Java library (siyempre, mayroong maraming dalubhasa at mataas na pagganap na pagpapatupad ng mga algorithm na nauuna sa mga algorithm sa karaniwang mga aklatan, ngunit sa karamihan ng mga kaso sila ay talagang Hindi kailangan).
10. Kawalang pagbabago
Ang kawalan ng pagbabago sa buong gabay at sa bahaging ito ay nananatiling paalala: mangyaring seryosohin ito. Kung ang isang klase na iyong idinisenyo o isang paraan na iyong ipinapatupad ay maaaring magbigay ng garantiyang hindi nababago, maaari itong magamit sa karamihan ng mga kaso kahit saan nang walang takot na mabago nang sabay. Gagawin nitong mas madali ang iyong buhay bilang isang developer (at sana ang buhay ng mga miyembro ng iyong koponan).
11. Pagsubok
Ang pagsasagawa ng test-driven development (TDD) ay napakapopular sa komunidad ng Java, na nagpapataas ng bar para sa kalidad ng code. Sa lahat ng mga benepisyong ibinibigay ng TDD, nakakalungkot na makita na ang Java standard library ngayon ay walang kasamang anumang test framework o support tools. Gayunpaman, ang pagsubok ay naging isang kinakailangang bahagi ng modernong pag-unlad ng Java at sa seksyong ito ay titingnan natin ang ilang mga pangunahing pamamaraan gamit ang JUnit framework . Sa JUnit, mahalagang, ang bawat pagsubok ay isang hanay ng mga pahayag tungkol sa inaasahang estado o pag-uugali ng isang bagay. Ang sikreto sa pagsusulat ng magagandang pagsubok ay panatilihing simple at maikli ang mga ito, na sumusubok sa isang bagay sa isang pagkakataon. Bilang isang ehersisyo, magsulat tayo ng isang hanay ng mga pagsubok upang i-verify na ang String.format ay isang function mula sa seksyon ng string na nagbabalik ng nais na resulta. 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" ) ); } } Ang parehong mga pagsubok ay mukhang nababasa at ang kanilang pagpapatupad ay mga pagkakataon. Ngayon, ang karaniwang proyekto ng Java ay naglalaman ng daan-daang kaso ng pagsubok, na nagbibigay ng mabilis na feedback sa developer sa panahon ng proseso ng pagbuo sa mga regression o feature.
12. Susunod
Kinukumpleto ng bahaging ito ng gabay ang isang serye ng mga talakayan na may kaugnayan sa pagsasagawa ng programming sa Java at mga manual para sa programming language na ito. Sa susunod ay babalik tayo sa mga tampok ng wika, tuklasin ang mundo ng Java tungkol sa mga pagbubukod, kanilang mga uri, kung paano at kailan gagamitin ang mga ito.
13. I-download ang source code
Ito ay isang aralin sa pangkalahatang mga prinsipyo ng pagpapaunlad mula sa kursong Advanced na Java. Ang source code para sa aralin ay maaaring i-download dito .
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION