apeescape2.com
  • Põhiline
  • Brändikujundus
  • Töö Tulevik
  • Insenerihaldus
  • Mobiilne
Tagumine Ots

10 kõige tavalisemat kevadraamistiku viga

Kevad on kahtlemata üks populaarsemaid Java-raamistikke ja ka raskesti taltsutav metsaline. Kuigi selle põhitõdesid on väga lihtne mõista, võtab tugev Kevadine arendaja saamine aega ja vaeva.

Selles artiklis käsitleme kevadel kõige levinumaid vigu, mis on spetsiaalselt suunatud veebirakendustele ja Spring Bootile. Nagu me seda näeme Spring Boot veebisaidil , see võtab a vaatenurgast selle kohta, kuidas valmis tootmisrakendusi tuleks üles ehitada, nii et selles artiklis püütakse seda visiooni jäljendada ja antakse kokkuvõte mõnedest näpunäidetest, mida võiks lisada standardse Sping Boot veebirakenduse väljatöötamisse.



Juhul, kui te ei tea Spring Bootist, kuid soovite siiski mõnda mainitud asja proovida, lõin ma selle artikliga kaasas olev GitHubi hoidla . Kui tunnete end selle artikli ajal mingil hetkel kaotsi läinud, soovitaksin kloonida hoidla ja segada oma kohaliku masina koodi.



Üldine viga nr 1: liiga madalaks läheb

Alustasime selle levinud veaga, sest sündroom pole siin leiutatud ”See on tarkvaraarenduse maailmas väga levinud. Sümptomiteks on tavaliselt kasutatava koodi juppide korrapärane ümberkirjutamine ja paljud arendajad näivad seda kannatavat.

Ehkki raamatukogu sisemise töö ja selle rakendamise mõistmine on suures osas hea ja vajalik (ja see võib olla ka suurepärane õppeprotsess), on teie kui tarkvarainseneri arengule kahjulik pidevalt maadelda madalate andmebaaside samade üksikasjadega. taseme rakendamine. Seal on põhjus, miks on olemas sellised abstraktsioonid ja raamistikud nagu Spring, mis on just selleks, et eralduda korduvatest töövihikutest ja võimaldada teil keskenduda kõrgema taseme üksikasjadele - oma domeeni objektidele ja äriloogikale.



Nii et aktsepteerige abstraktsioone - järgmine kord, kui olete mõne konkreetse probleemiga silmitsi, tehke kõigepealt kiire otsing ja tehke kindlaks, kas mõni selle probleemi lahendav kogu on juba Springisse sisse ehitatud; Nendel aegadel leiate tõenäoliselt sobiva lahenduse juba olemas. Kasuliku raamatukogu näitena kasutan märkmeid aadressilt Lomboki projekt selle artikli ülejäänud osas. Lombokit kasutatakse mudelkoodigeneraatorina ja seega ei tohiks teie laiskal arendajal olla probleeme raamatukoguga tutvumisel. Näiteks vaadake, mida Java Bean standard ”Lombokiga:

@Getter @Setter @NoArgsConstructor public class Bean implements Serializable { int firstBeanProperty; String secondBeanProperty; }

Nagu võite ette kujutada, koosneb ülaltoodud kood:

public class Bean implements Serializable { private int firstBeanProperty; private String secondBeanProperty; public int getFirstBeanProperty() { return this.firstBeanProperty; } public String getSecondBeanProperty() { return this.secondBeanProperty; } public void setFirstBeanProperty(int firstBeanProperty) { this.firstBeanProperty = firstBeanProperty; } public void setSecondBeanProperty(String secondBeanProperty) { this.secondBeanProperty = secondBeanProperty; } public Bean() { } }

Pidage siiski meeles, et peate tõenäoliselt installima pistikprogrammi juhuks, kui soovite Lombokit kasutada oma IDE-ga. Pistikprogrammi IntelliJ IDEA versiooni leiate siin .



Levinud viga nr 2: sisetöö filtreerimine

Oma sisemise struktuuri paljastamine pole kunagi hea mõte, kuna see loob teenuse kujunduses paindumatuse ja soodustab seetõttu halbu koode. Sisemine töö filtreerimine ilmneb andmebaasi struktuuri juurdepääsetavaks muutmisest teatud API väljundpunktidest. Oletame näiteks, et järgmine POJO ('Plain Old Java Object') tähistab teie andmebaasi tabelit:

@Entity @NoArgsConstructor @Getter public class TopTalentEntity { @Id @GeneratedValue private Integer id; @Column private String name; public TopTalentEntity(String name) { this.name = name; } }

Oletame, et on olemas väljumispunkt, mis peab andmetele juurde pääsema TopTalentEntity. Kuigi TopTalentEntity eksemplaride tagastamine on väga ahvatlev, võib paindlikum lahendus olla uue klassi loomine andmete esitamiseks TopTalentEntity API väljumispunktis:

@AllArgsConstructor @NoArgsConstructor @Getter public class TopTalentData { private String name; }

Seega ei nõua andmebaasi tagakülje muudatuste tegemine teenuse kihis täiendavaid muudatusi. Mõelge, mis juhtuks, kui TopTalentEntity -le lisatakse väli 'parool' funktsiooni salvestamiseks räsi kasutajate parooli andmebaasis - ilma mõne pistikuta, näiteks TopTalentData, paluks kasutajaliidese muutmise unustamine kogemata ebasoovitavat salajast teavet!

Üldine viga nr 3: murede lahususe puudumine

Kui teie rakendus kasvab, muutub koodi korraldamine kiiresti palju olulisemaks. Irooniline, et suur osa hea tarkvaratehnika põhimõtetest hakkab mastaabis lagunema - eriti juhtudel, kui rakenduse arhitektuuri kujundus pole hästi läbi mõeldud. Üks levinumaid vigu, millele arendajad kipuvad alla andma, on koodiga seotud probleemide maksimeerimine ja seda on äärmiselt lihtne teha!

müügi prognoosimine on näide ülesandest (n) _____.

Mis tavaliselt lõhub murede lahusus see on lihtsalt uue funktsionaalsuse viskamine olemasolevatele klassidele. See on suurepärane lühiajaline lahendus (alustamiseks on vaja vähem sisestada), kuid paratamatult muutub see probleemiks hiljem, kas testimise, hoolduse või millalgi vahepeal. Mõelge järgmisele kontrollerile, mis tagastab TopTalentData oma hoidlast:

@RestController public class TopTalentController { private final TopTalentRepository topTalentRepository; @RequestMapping('/toptal/get') public List getTopTalent() { return topTalentRepository.findAll() .stream() .map(this::entityToData) .collect(Collectors.toList()); } private TopTalentData entityToData(TopTalentEntity topTalentEntity) { return new TopTalentData(topTalentEntity.getName()); } }

Esmapilgul võib tunduda, et sellel koodil pole midagi eriti viga; esitab loendi TopTalentData mis eraldatakse TopTalentEntity eksemplaridest. Lähemalt vaadates näeme siiski, et TopTalentController; see on peamiselt taotluste kaardistamine konkreetsesse väljumispunkti, andmete hankimine hoidlast ja üksuste teisendamine TopTalentRepository teises vormingus. Puhtam lahendus oleks jagada need mured oma klassidesse. See võib välja näha umbes selline:

@RestController @RequestMapping('/toptal') @AllArgsConstructor public class TopTalentController { private final TopTalentService topTalentService; @RequestMapping('/get') public List getTopTalent() { return topTalentService.getTopTalent(); } } @AllArgsConstructor @Service public class TopTalentService { private final TopTalentRepository topTalentRepository; private final TopTalentEntityConverter topTalentEntityConverter; public List getTopTalent() { return topTalentRepository.findAll() .stream() .map(topTalentEntityConverter::toResponse) .collect(Collectors.toList()); } } @Component public class TopTalentEntityConverter { public TopTalentData toResponse(TopTalentEntity topTalentEntity) { return new TopTalentData(topTalentEntity.getName()); } }

Selle hierarhia täiendav eelis on see, et see võimaldab meil lihtsalt klassi nime kontrollides kindlaks teha, kus funktsionaalsus asub. Samuti saame prooviperioodi jooksul vajaduse korral mõne klassi hõlpsasti välja vahetada.

Üldine viga nr 4: halb ja ebajärjekindel veakäsitlus

Järjepidevuse küsimus pole tingimata Springile (või Java-le) omane, kuid see on siiski oluline aspekt, mida kevadiste projektide kallal töötamisel arvestada. Kuigi kodeerimisstiili üle võib vaielda (ja see on tavaliselt meeskonnas või kogu ettevõttes kokkuleppe küsimus), võib ühise standardi omamine olla tohutu tööviljakus. See on väga tõsi, eriti mitme isikliku meeskonnaga; järjepidevus võimaldab tagasilükkamist ilma kulutada palju ressursse lohutades või pakkudes pikki selgitusi erinevate klasside vastutuse kohta.

Mõelgem kevadisele projektile koos erinevate konfiguratsioonifailide, teenuste ja kontrolleritega. Nende nimetamisel semantiliselt järjepidev olemine loob hõlpsasti uuritava struktuuri, kus iga arendaja, olgu see nii uus kui tahes, saab koodis ringi liikuda; lisada konfiguratsiooniklassidele konfiguratsiooniliited, teie teenustele teenuse järelliited ja kontrollerite järelliited.

Järjepidevuse küsimusega on tihedalt seotud serveripoolne tõrkeotsing, mis väärib erilist rõhutamist. Kui olete kunagi pidanud käsitsema halvasti kirjutatud API erandivastusi, siis ilmselt teate, miks - erandite nõuetekohane sõelumine võib olla tülikas ja veelgi tülikam on teha kindlaks, miks need erandid algselt tekkisid.

API arendajana peaksite ideaalis katma kõik väljumispunktid, millega kasutajad silmitsi seisavad, ja tõlkima need levinud veavormingusse. Tavaliselt tähendab see üldise veakoodi ja kirjelduse olemasolu ettekäändelahenduse asemel, näiteks a) teate '500 sisemise serveri viga' tagastamist või b) kasutajale lahenduse otsimist (mida tuleks palavalt vältida, kuna see paljastab) teie sisemine töö, peale selle, et seda on kliendil raske käsitseda).

Tavalise vastuse vormingu tõrke näide võib olla:

@Value public class ErrorResponse { private Integer errorCode; private String errorMessage; }

Midagi sarnast leidub tavaliselt kõige populaarsemates API-des ja see kipub töötama väga hästi, kuna seda saab hõlpsalt ja süstemaatiliselt dokumenteerida. Selle vormingu erandeid saab tõlkida, lisades märkuse @ExceptionHandler meetodile (märkuse näide on tavalises veas nr 6).

Üldine viga nr 5: valel viisil tegelemine mitmelõimega

Sõltumata sellest, kas see on lauaarvutites või veebirakendustes, võib kevadel mitmikeermelist käsitlemist olla keeruline või mitte. Paralleelsete programmide hukkamiste põhjustatud probleemid on stressirohked, raskesti mõistetavad ja neid on äärmiselt raske siluda - tegelikult, arvestades probleemi olemust, peate tõenäoliselt siluriga jätkama, kui mõistate, et tegemist on paralleelse täitmisprobleemiga ja kontrollige oma koodi 'käsitsi', kuni leiate juurvea põhjuse. Kahjuks puudub selliste probleemide lahendamiseks regulaarne lahendus; Sõltuvalt konkreetsest juhtumist peate olukorda uurima ja seejärel ründama probleemi nurga alt, mis teile kõige paremini meeldib.

Muidugi tahaksite ideaalselt vältida mitmikeermelisi vigu. Jällegi ei eksisteeri standardset lähenemist, kuid siin on mõned praktilised kaalutlused mitmikeermeliste vigade silumiseks ja ennetamiseks:

Vältige ülemaailmset riiki

Kõigepealt pidage alati meeles 'globaalse riigi' probleemi. Kui loote mitmikeermelist rakendust, tuleks absoluutselt kõike muudetavat jälgida väga hoolikalt ja võimaluse korral täielikult eemaldada. Kui on mõni põhjus, miks globaalne muutuja peaks muutma jääma, kasutage sünkroniseerimine ja jälgige oma rakenduse toimivust, et kinnitada, et see ei kuku uute ooteperioodide tõttu kokku.

Vältige muutlikkust

See pärineb funktsionaalne programmeerimine ja kohandatud OOP-ga öeldakse, et mutatsioonide ja oleku muutumise klassi tuleks vältida. Lühidalt tähendab see, et jätkate kinnitusmeetodite kasutamist ja privaatsete lõppväljade olemasolu kõigis oma mudeliklassides. Ainus kord, kui selle väärtusi saab muteerida, on ehituse ajal. Nii saate tagada, et ei tekiks vaidlusprobleeme ja et sellele juurdepääsuvad objekti atribuudid annaksid alati õiged väärtused.

Registreerige olulised andmed

Hinnake, kus teie rakendus võib probleeme tekitada, ja registreerige kõik olulised andmed ennetavalt. Vea ilmnemisel olete tänulik, et teil on teavet, mis ütleb, millised taotlused on saadud, ja saate paremini aru, miks teie rakendus ebaõnnestus. Jällegi on vaja märkida, et register tutvustab täiendava faili sisend / väljundit ja seetõttu ei tohiks seda kuritarvitada, kuna see võib teie rakenduse toimimist tõsiselt mõjutada.

Taaskasutage olemasolevaid rakendusi

Kui peate looma oma lõimed (nt erinevatele teenustele asünkroonsete päringute tegemiseks), kasutage oma lahenduste loomise asemel juba olemasolevaid turvalisi rakendusi. See tähendab enamasti, et peaksite kasutama ExecutorServices Y CompletableFutures Java 8 oma puhta ja funktsionaalse stiiliga lõime loomiseks. Kevad võimaldab klassi kaudu ka asünkroonseid taotlusi töödelda Edasilükatud tulemus .

Üldine viga nr 6: märkustel põhinevate valideerimiste mittekasutamine

Kujutame ette, et meie ülaltoodud TopTalenti teenus nõuab uue tipptalendi lisamiseks väljumispunkti. Ütleme nii, et mingil väga mõjuval põhjusel peab iga uus nimi olema täpselt 10 tähemärki pikk. Kehtiv viis selleks võib olla:

@RequestMapping('/put') public void addTopTalent(@RequestBody TopTalentData topTalentData) { boolean nameNonExistentOrHasInvalidLength = Optional.ofNullable(topTalentData) .map(TopTalentData::getName) .map(name -> name.length() == 10) .orElse(true); if (nameNonExistentOrInvalidLength) { // throw some exception } topTalentService.addTopTalent(topTalentData); }

Kuid ülaltoodu (välja arvatud halvasti ehitatud) ei ole tegelikult 'puhas' lahendus. Otsime rohkem kui ühte tüüpi kehtivust (peamiselt see, et TopTalentData pole null, Y see TopTalentData.name pole null, Y see TopTalentData.name on 10 tähemärki), samuti erandi tegemine, kui andmed on valed.

Seda saab teha palju puhtamalt, kasutades Talveunerežiimi valideerija kevadega. Esiteks peame uuesti arvutama addTopTalent valideerimise toetamiseks:

@RequestMapping('/put') public void addTopTalent(@Valid @NotNull @RequestBody TopTalentData topTalentData) { topTalentService.addTopTalent(topTalentData); } @ExceptionHandler @ResponseStatus(HttpStatus.BAD_REQUEST) public ErrorResponse handleInvalidTopTalentDataException(MethodArgumentNotValidException methodArgumentNotValidException) { // handle validation exception }

Lisaks peame märkima, millist omadust soovime klassis TopTalentData kinnitada:

public class TopTalentData { @Length(min = 10, max = 10) @NotNull private String name; }

Nüüd võtab Spring päringu kinni ja kinnitab selle enne meetodi kasutamist - täiendavat käsitsi testimist pole vaja.

Teine viis, kuidas oleksime sama efekti saavutanud, on oma märkuste loomine. Ehkki tavaliselt kasutate kohandatud märkusi ainult siis, kui teie vajadused ületavad Hibernate sisseehitatud piiratud komplekt , selle näite puhul eeldame, et @ Pikkust pole olemas. Teeksite valideerija, mis kontrollib märgistringi pikkust, luues kaks täiendavat klassi, ühe valideerimise ja teise märkimise omaduste jaoks:

@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented @Constraint(validatedBy = { MyAnnotationValidator.class }) public @interface MyAnnotation { String message() default 'String length does not match expected'; Class[] groups() default {}; Class[] payload() default {}; int value(); } @Component public class MyAnnotationValidator implements ConstraintValidator { private int expectedLength; @Override public void initialize(MyAnnotation myAnnotation) { this.expectedLength = myAnnotation.value(); } @Override public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) }

Pange tähele, et sellistel juhtudel nõuavad probleemide lahutamise parimad tavad kinnisvara kehtivaks märkimist, kui see on nullmeetodis (s == null | isValid), ja seejärel kasutage märkust @NotNull kui see on kinnistu lisanõue:

public class TopTalentData { @MyAnnotation(value = 10) @NotNull private String name; }

Tavaline viga nr 7: (endiselt) Base-XML-i konfiguratsiooni kasutamine

Kui Spring'i eelmistes versioonides oli vaja XML-i, siis praegu saab suurema osa seadistustest teha ainult Java-koodi / märkuste kaudu; XML-konfiguratsioonid on vaid täiendav ja mittevajalik standardne tekstikood.

Selles artiklis (nagu ka kaaslases GitHubi hoidlas) kasutatakse kevade seadistamiseks märkusi ja ta teab, millised neist oad see peaks üle kanduma, kuna juurpakett on märgitud liitmärkega @SpringBootApplication järgmiselt:

@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }

Liitmärkus (selle kohta saate lisateavet Kevadine dokumentatsioon ) annab Springile lihtsalt vihje, et pakendite eemaldamiseks tuleks neid skannida oad . Meie konkreetsel juhul tähendab see, et üleandmiseks kasutatakse ülaltoodud pakendi all (co.kukurin) järgmist:

  • @Component (TopTalentConverter, MyAnnotationValidator)
  • @RestController (TopTalentController)
  • @Repository (TopTalentRepository)
  • @Service (TopTalentService) klassid

Kui meil oleks annotatsiooniklassid @Configuration täiendavaid kontrolliks Java baasi konfiguratsioon.

Levinud viga nr 8: profiilide unustamine

Serverite arendamisel on levinud probleem eristada erinevat tüüpi konfiguratsioone, tavaliselt teie arendus- ja tootmiskonfiguratsioone. Selle asemel, et mitu korda konfigureerimiskirju käsitsi alistada, kui lähete rakenduse testimisest selle juurutamiseni, oleks kasulikum viis kasutada profiile.

Vaatleme juhtumit, kus kasutate kohalikuks arenduseks mälu sisseehitatud andmebaasi koos tootmisel MySQL-i andmebaasiga. See tähendaks sisuliselt seda, et kasutaksite igale URL-ile juurdepääsemiseks erinevat URL-i ja (loodetavasti) erinevaid volitusi. Vaatame, kuidas seda saab teha kahes erinevas konfiguratsioonifailis:

application.yaml fail

# set default profile to 'dev' spring.profiles.active: dev # production database details spring.datasource.url: 'jdbc:mysql://localhost:3306/toptal' spring.datasource.username: root spring.datasource.password:

application-dev.yaml fail

spring.datasource.url: 'jdbc:h2:mem:' spring.datasource.platform: h2

Eeldatakse, et te ei soovi koodi ülevaatamise ajal kogemata oma tootmise andmebaasis toimingut sooritada, seega on mõistlik seadistada arendamisel vaikeprofiil. Serveris saate konfiguratsiooniprofiili käsitsi alistada, andes parameetri -Dspring.profiles.active=prod aastal JVM. Teise võimalusena saate oma OS-i keskkonnamuutujale määrata soovitud vaikeprofiili.

Üldine viga nr 9: sõltumatuse süstimist ei saa vastu võtta

Sõltuvussüstimise korralik kasutamine Springiga võimaldab teil kõik oma objektid koos teisaldada, skannides kõik soovitud konfiguratsiooniklassid; see osutub kasulikuks suhete eraldamisel ja muudab testimise ka palju lihtsamaks. Selliste klasside tihedalt sobitamise asemel:

public class TopTalentController { private final TopTalentService topTalentService; public TopTalentController() { this.topTalentService = new TopTalentService(); } }

Lubame Springil meie jaoks kirjutada:

public class TopTalentController { private final TopTalentService topTalentService; public TopTalentController(TopTalentService topTalentService) { this.topTalentService = topTalentService; } }

Misko Hevery de Google'i jutt selgitab sõltuvuse süstimise 'miks' põhjalikult, nii et vaatame, kuidas seda praktikas kasutatakse. Murede lahutamise jaotises (Üldine viga nr 3) lõime teenindusklassi ja kontrolleri. Oletame, et tahame kontrollerit testida eeldusel, et TopTalentService käitub korralikult. Saame praeguse teenuse juurutamise asemele mannekeeni lisada, pakkudes eraldi konfiguratsiooniklassi:

@Configuration public class SampleUnitTestConfig { @Bean public TopTalentService topTalentService() { TopTalentService topTalentService = Mockito.mock(TopTalentService.class); Mockito.when(topTalentService.getTopTalent()).thenReturn( Stream.of('Mary', 'Joel').map(TopTalentData::new).collect(Collectors.toList())); return topTalentService; } }

Seejärel saame mannekeeni sisse süstida, käskides Springil kasutada SampleUnitTestConfig kui teie konfiguratsioonipakkuja:

@ContextConfiguration(classes = { SampleUnitTestConfig.class })

See võimaldab meil kasutada konteksti seadistust uba valmistatud tellimuse järgi katseseadmesse.

javascripti tõrge pole funktsioon

Levinud viga nr 10: testimise puudumine või sobimatu testimine

Kuigi üksuste testimise idee on olnud meil juba pikka aega, näivad paljud arendajad seda 'unustavat' (eriti kui see pole midagi, mida nad vajab ) või lisavad selle lihtsalt ebaoluliseks. See on ilmselgelt asi, mida me ei taha juhtuda, kuna testid peaksid mitte ainult kontrollima teie koodi õigsust, vaid olema ka dokumendid selle kohta, kuidas rakendus peaks erinevates olukordades käituma.

Veebiteenuste testimisel teete harva puhtalt 'puhta' üksuse testimist, kuna HTTP-side nõuab tavaliselt DispatcherServlet kevade kohta ja vaadake, mis juhtub, kui HttpServletRequest on tegelikult saadud (muutes selle tõendiks) integratsioon , tegeleda valideerimise, väljaandmise väljaandmisega jne). Puhkus tagab , Java DSL REST-teenuste hõlpsaks testimiseks, parem kui MockMVC, on tõestanud, et see võib anda väga elegantse lahenduse. Mõelge järgmisele sõltuvuse süstimisega koodile:

@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { Application.class, SampleUnitTestConfig.class }) public class RestAssuredTestDemonstration { @Autowired private TopTalentController topTalentController; @Test public void shouldGetMaryAndJoel() throws Exception { // given MockMvcRequestSpecification givenRestAssuredSpecification = RestAssuredMockMvc.given() .standaloneSetup(topTalentController); // when MockMvcResponse response = givenRestAssuredSpecification.when().get('/toptal/get'); // then response.then().statusCode(200); response.then().body('name', hasItems('Mary', 'Joel')); } }

SampleUnitTestConfig läbib TopTalentService vale rakenduse a TopTalentController samal ajal kui kõik teised klassid teisaldatakse standardkonfiguratsiooni abil, mille järeldavad rakendusklassi paketti manustatud skannimispaketid. RestAssuredMockMvc kasutatakse lihtsalt kerge keskkonna loomiseks ja saadab päringu GET väljumiskohta /toptal/get.

Hakka kevadmeistriks

Kevad on väga võimas raamistik, mille käivitamine on lihtne, kuid täieliku meisterlikkuse saavutamiseks on vaja teatavat pühendumist ja aega. Kui võtate aega raamistikuga tutvumiseks, parandab see teie tootlikkust pikas perspektiivis ja aitab teil lõpuks puhtam kood kirjutada ja paremaks arendajaks saada.

Kui otsite sellel teemal rohkem materjali, Kevad tegevuses on hea raamat, mis käsitleb paljusid kevade põhiteemasid.

Puhta koodi tagamine: pilk Pythonile, parameetriline

Tehnoloogia

Puhta koodi tagamine: pilk Pythonile, parameetriline
Arendaja juhend avatud lähtekoodiga litsentside kohta

Arendaja juhend avatud lähtekoodiga litsentside kohta

Tehnoloogia

Lemmik Postitused
Rubiini algoritmi dokumentatsioon koos AsciiDoci ja Knitriga
Rubiini algoritmi dokumentatsioon koos AsciiDoci ja Knitriga
Ärge laske end lollitada: arvutage töötajate ja konsultantide tegelikud kulud
Ärge laske end lollitada: arvutage töötajate ja konsultantide tegelikud kulud
Töötajate säilitamise strateegiad 101: töökohaõpe
Töötajate säilitamise strateegiad 101: töökohaõpe
AngularJSi õpetus: kohandatud direktiivide demüstifitseerimine
AngularJSi õpetus: kohandatud direktiivide demüstifitseerimine
Vilgas riistvara koos sisseehitatud tarkvaraarendusega
Vilgas riistvara koos sisseehitatud tarkvaraarendusega
 
Masinõppe manused: keerukate andmete lihtsustamine
Masinõppe manused: keerukate andmete lihtsustamine
Forexi algoritmiline kauplemine: praktiline lugu inseneridele
Forexi algoritmiline kauplemine: praktiline lugu inseneridele
Twitteri andmekaevandus: Pythoni abil suurandmete analüüsi juhend
Twitteri andmekaevandus: Pythoni abil suurandmete analüüsi juhend
Tehisintellekti investeeringute põhialus: põhitööde rajamine (I osa)
Tehisintellekti investeeringute põhialus: põhitööde rajamine (I osa)
ApeeScape'i nimekiri parimatest tasuta programmeerimisraamatutest
ApeeScape'i nimekiri parimatest tasuta programmeerimisraamatutest
Lemmik Postitused
  • kasuta vaarikas pi veebiserverina
  • nurgeline 4 vorm esitage näide
  • google play teenuste asukoha api
  • seleeni veebidraiveri lehe objekti mudel
  • väljendada parimate tavade veakäsitlust
  • tasuta töötavad krediitkaardinumbrid 2017
Kategooriad
Innovatsioon Tööriistad Ja Õpetused Tulud Ja Kasv Kujundusprotsess Andmeteadus Ja Andmebaasid Insenerihaldus Inimesed Ja Meeskonnad Ux Disain Investorid Ja Rahastamine Veebi Kasutajaliides

© 2021 | Kõik Õigused Kaitstud

apeescape2.com