portaldacalheta.pt
  • Põhiline
  • Tooteinimesed Ja Meeskonnad
  • Ux Disain
  • Protsess Ja Tööriistad
  • Andmeteadus Ja Andmebaasid
Tagumine Ots

iOS-i tsentraliseeritud ja sidumata võrgundus: AF-võrguõpetus Singletoni klassiga



Mis puutub iOS-i arhitektuurimustritesse, siis Model-View-Controlleri (MVC) kujundusmuster sobib suurepäraselt rakenduse koodibaasi pikaealisuse ja hooldatavuse tagamiseks. See võimaldab klasside hõlpsat taaskasutamist või asendamist, et toetada erinevaid nõudeid, lahutades need üksteisest. See aitab maksimeerida objektile orienteeritud programmeerimise (OOP) eeliseid.

Kuigi see iOS-i rakenduse arhitektuur töötab hästi mikrotasandil (rakenduse üksikud ekraanid / jaotised), võite rakenduse kasvades leida mitmele mudelile sarnaseid funktsioone. Sellistel juhtudel nagu võrguühendus, võib parem lähenemine olla tavalise loogika teisaldamine oma mudeliklassidest üksikutesse abiklassidesse. Selles AFNetworking iOS-i õpetuses õpetan teile, kuidas seadistada tsentraliseeritud üksikvõrguobjekt, mida saab mikrotasandi MVC-komponentidest lahti ühendades kogu oma lahti ühendatud arhitektuurirakenduses uuesti kasutada.



AF-võrguõpetus: tsentraliseeritud ja sidumatud võrgud Singletoniga



millised on CFO ülesanded?

IOS-i võrguühenduse probleem

Apple on teinud suurepärast tööd, hõlbustades hõlpsasti kasutatavates iOS-i SDK-des mobiilse riistvara haldamise paljusid keerukusi, kuid mõnel juhul, näiteks võrguühendus, Bluetooth, OpenGL ja multimeediumitöötlus, võivad klassid olla tülikad nende eesmärgi tõttu hoida SDK-d paindlikud. Õnneks rikkad iOS-i arendaja kogukond on loonud kõrgetasemelised raamistikud, et lihtsustada enamlevinud kasutusjuhtumeid, püüdes lihtsustada rakenduse kujundust ja struktuuri. Hea programmeerija, kes kasutab ios-i rakenduse arhitektuuri parimaid tavasid, teab, milliseid tööriistu kasutada, miks neid kasutada ja millal on parem kirjutada oma tööriistad ja klassid nullist.



AFNetworking on suurepärane võrgustike näide ja üks kõige sagedamini kasutatavaid avatud lähtekoodiga raamistikke, mis lihtsustab arendaja igapäevaseid ülesandeid. See lihtsustab RESTful API-võrgu loomist ja loob moodulid päringute / vastuste mustrid koos edukuse, edenemise ja tõrke lõpetamise plokkidega. See välistab vajaduse arendaja poolt rakendatud delegeerimismeetodite ja kohandatud päringu / ühenduse seadete järele ning neid saab väga kiiresti lisada mis tahes klassi.

AF-võrgu probleem

AFNetworking on suurepärane, kuid selle modulaarsus võib viia ka selle killustatud kasutamiseni. Levinud ebaefektiivsed rakendused võivad hõlmata järgmist:



  • Mitu võrgutaotlust ühes vaate kontrolleris, kasutades sarnaseid meetodeid ja atribuute

  • Peaaegu identsed taotlused mitmes vaatekontrolleris, mis toovad kaasa hajutatud ühised muutujad, mis võivad sünkroonist väljuda



  • Klasside võrgutaotlused selle klassiga mitteseotud andmete kohta

Rakenduste puhul, millel on piiratud arv vaatamisi, rakendamiseks on vähe API-kõnesid ja tõenäoliselt ei muutu sageli, ei pruugi see suurt muret valmistada. Kuid suurema tõenäosusega mõtlete suurelt ja teil on kavas mitu aastat värskendusi. Kui teie juhtum on viimane, peate tõenäoliselt tegelema järgmisega:



  • API versioonimine rakenduse mitme põlvkonna toetamiseks

  • Uute parameetrite lisamine või olemasolevate parameetrite muutmine aja jooksul, et võimekust laiendada



  • Täiesti uute API-de juurutamine

Kui teie võrgukood on hajutatud kogu koodibaasi, on see nüüd potentsiaalne õudusunenägu. Loodetavasti on teil vähemalt osa parameetritest ühises päises staatiliselt määratletud, kuid isegi siis võite kõige väiksemate muudatuste saamiseks puudutada tosinat klassi.



Kuidas lahendada AF-võrgu piiranguid?

Taotluste, vastuste ja nende parameetrite töötlemise tsentraliseerimiseks looge võrguühendus.

Üksikobjekt pakub globaalse juurdepääsu oma klassi ressurssidele. Üksikkomponente kasutatakse olukordades, kus see üks juhtimispunkt on soovitav, näiteks klassides, kus pakutakse mõnda üldist teenust või ressurssi. Globaalse eksemplari saate üksikklassist tehase meetodil. - Apple

Niisiis, üksikisik on klass, mille rakenduses on teil kogu rakenduse vältel ainult üks eksemplar. Lisaks, kuna me teame, et eksemplare on ainult üks, on see hõlpsasti kättesaadav kõigile teistele klassidele, kes peavad oma meetoditele või omadustele juurde pääsema.

Sellepärast peaksime võrgu loomiseks kasutama üksikkomponenti:

  • See on staatiliselt initsialiseeritud, nii et kui see on loodud, on sellel samad meetodid ja omadused, mis on kättesaadavad kõigile klassidele, kes sellele juurde pääsevad. Pole võimalust juhuslike sünkroonimisprobleemide tekkeks või andmete valelt klassi eksemplarilt andmete küsimiseks.

  • Saate piirata oma API-kõnesid piirmäärade alla jäämiseks (nt kui peate oma API-taotlusi hoidma alla viie sekundis).

  • Staatilisi atribuute, nagu hostinimi, pordinumbrid, lõpp-punktid, API versioon, seadme tüüp, püsivad ID-d, ekraani suurus jne, saab paigutada nii, et üks muudatus mõjutab kõiki võrgutaotlusi.

  • Ühiseid omadusi saab paljude võrgutaotluste vahel uuesti kasutada.

  • Üksikobjekt võtab mälu alles pärast selle instantsimist. See võib olla kasulik üksikjuhtumite jaoks, millel on väga spetsiifilised kasutamisjuhud, mida mõned kasutajad ei pruugi kunagi vajada, näiteks kui teil pole seadet Chromecastile video ülekandmist.

  • Võrgutaotlusi saab vaadetest ja kontrolleritest täielikult lahti ühendada, et need saaksid jätkuda ka pärast vaadete ja kontrollerite hävitamist.

  • Võrgu logimist saab tsentraliseerida ja lihtsustada.

  • Tavapäraseid tõrkeid, näiteks hoiatusi, saab kõigi taotluste puhul uuesti kasutada.

  • Sellise üksikisiku põhistruktuuri saab taaskasutada mitmes projektis, millel on lihtsad tipptasemel staatiliste omaduste muutused.

Mõned põhjused, miks üksikisikuid mitte kasutada:

  • Neid võib ühe klassi mitme ülesande täitmiseks üle kasutada. Näiteks võiks videotöötlusmeetodeid segada võrgumeetodite või kasutaja olekumeetoditega. See oleks tõenäoliselt kehv disainipraktika ja viiks raskesti mõistetava koodini. Selle asemel tuleks luua mitu konkreetsete kohustustega üksikut üksikut.

  • Üksikuid ei saa alamklassidesse liigitada.

  • Üksikud suudavad varjata sõltuvusi ja muutuda vähem modulaarseks. Näiteks kui singlett eemaldatakse ja klassil puudus import, mille singlett importis, võib see põhjustada tulevasi probleeme (eriti kui on sõltumatuid raamatukogusõltuvusi).

  • Klass saab pikkade operatsioonide käigus muuta üksikisikute ühiseid omadusi, mis on teises klassis ootamatud. Ilma seda korralikult läbi mõeldes võivad tulemused olla erinevad.

  • Mälulekked singlites võivad muutuda oluliseks probleemiks, kuna singletti ennast kunagi eraldatakse.

    nõudluse hinnaelastsuse näited reaalses elus

Kuid iOS-i rakenduse arhitektuuri parimate tavade abil saab neid negatiivseid külgi leevendada. Mõned parimad tavad on järgmised:

  • Iga üksikisik peaks kandma ühte vastutust.

  • Kui vajate suurt täpsust, ärge kasutage üksikisikuid andmete salvestamiseks, mida mitu klassi või lõime kiiresti muudavad.

  • Looge üksikud funktsioonid funktsioonide lubamiseks / keelamiseks olemasolevate sõltuvuste põhjal.

  • Ärge salvestage suurt hulka andmeid üksikomadustes, kuna need püsivad kogu teie rakenduse vältel (kui käsitsi ei hallata).

Lihtne Singletoni näide AFNetworkinguga

Kõigepealt lisage eeltingimusena oma projekti AFNetworking. Lihtsaim lähenemisviis on Cocoapods ja juhised leiate sellest GitHubi leht .

Sel ajal soovitan lisada UIAlertController+Blocks ja MBProgressHUD (jälle hõlpsalt CocoaPodsiga lisatud). Need on selgelt valikulised, kuid see lihtsustab oluliselt edusamme ja hoiatusi, kui soovite neid rakendada AppDelegate'i akna üksikus.

Kord AFNetworking on lisatud, alustage uue kakaopuude klassi loomisega NetworkManager NSObject alamklassina. Haldurile juurdepääsu saamiseks lisage klassi meetod. Teie NetworkManager.h fail peaks välja nägema nagu allolev kood:

#import #import “AFNetworking.h” @interface NetworkManager : NSObject + (id)sharedManager; @end

Järgmisena rakendage üksikisiku põhilised lähtestamismeetodid ja importige päis AFNetworking. Teie klassi juurutamine peaks välja nägema järgmine (MÄRKUS. See eeldab, et kasutate automaatset võrdlusloendust):

#import 'NetworkManager.h' @interface NetworkManager() @end @implementation NetworkManager #pragma mark - #pragma mark Constructors static NetworkManager *sharedManager = nil; + (NetworkManager*)sharedManager { static dispatch_once_t once; dispatch_once(&once, ^ { sharedManager = [[NetworkManager alloc] init]; }); return sharedManager; } - (id)init { if ((self = [super init])) { } return self; } @end

Suurepärane! Nüüd valmistame süüa ja oleme valmis lisama omadusi ja meetodeid. Kiireks testiks, kuidas mõista, kuidas pääseda singletti, lisame NetworkManager.h -i järgmised andmed:

@property NSString *appID; - (void)test;

Ja järgmine NetworkManager.m

#define HOST @”http://www.apitesting.dev/” static const in port = 80; … @implementation NetworkManager … //Set an initial property to init: - (id)init { if ((self = [super init])) { self.appID = @”1”; } return self; } - (void)test { NSLog(@”Testing out the networking singleton for appID: %@, HOST: %@, and PORT: %d”, self.appID, HOST, port); }

Siis meie põhiosas ViewController.m fail (või mis iganes teil on), importige NetworkManager.h ja seejärel viewDidLoad lisama:

[[NetworkManager sharedManager] test];

Käivitage rakendus ja väljundis peaksite nägema järgmist:

Testing our the networking singleton for appID: 1, HOST: http://www.apitesting.dev/, and PORT: 80

Okei, nii et te tõenäoliselt ei sega #define, staatiline konst ja @property kõik korraga niimoodi, kuid lihtsalt selguse huvides oma valikud. “Staatiline konst” on parem tüübikinnituse deklaratsioon, kuid #define võib stringide loomisel kasulik olla, kuna see võimaldab makrosid kasutada. Selle jaoks, mida see väärt on, kasutan #define selle stsenaariumi lühiduse huvides. Kui te ei kasuta näpunäiteid, pole praktikas nende deklaratsioonimeetodite vahel palju erinevusi.

Nüüd, kui saate aru #defines -st, konstantidest, omadustest ja meetoditest, saame need eemaldada ja asuda asjakohasemate näidete juurde.

Võrgustike näide

Kujutage ette rakendust, kuhu kasutaja peab olema sisse logitud, et kõigele juurde pääseda. Rakenduse käivitamisel kontrollime, kas oleme salvestanud autentimismärgi, ja kui jah, täidame GET-i taotluse meie API-le, et näha, kas luba on aegunud või mitte.

Registreerige jaotises AppDelegate.m oma loa vaikeväärtus:

+ (void)initialize { NSDictionary *defaults = [NSDictionary dictionaryWithObjectsAndKeys:@'', @'token', nil]; [[NSUserDefaults standardUserDefaults] registerDefaults:defaults]; }

Lisame võrguhaldurile sümboolse tšeki ja saame kontrolli kohta tagasisidet lõpetamise plokkide kaudu. Võite need valmimisplokid kujundada nii, nagu soovite. Selles näites kasutan edukust vastuse objekti andmetega ning ebaõnnestumist tõrke reageerimise stringi ja olekukoodiga. Märkus. Ebaõnnestumise võib valikuliselt välja jätta, kui see pole vastuvõtval poolel oluline, näiteks analüüsi väärtuse suurendamine.

NetworkManager.h

Üle @interface:

typedef void (^NetworkManagerSuccess)(id responseObject); typedef void (^NetworkManagerFailure)(NSString *failureReason, NSInteger statusCode);

@ Liideses:

@omand (mitteatoomiline, tugev) AFHTTPSessionManager * networkingManager;

- (void)tokenCheckWithSuccess:(NetworkManagerSuccess)success failure:(NetworkManagerFailure)failure;

NetworkManager.m:

Määratlege meie BASE_URL:

#define ENABLE_SSL 1 #define HOST @'http://www.apitesting.dev/' #define PROTOCOL (ENABLE_SSL ? @'https://' : @'http://') #define PORT @'80' #define BASE_URL [NSString stringWithFormat:@'%@%@:%@', PROTOCOL, HOST, PORT]

Lisame mõned abimeetodid, et lihtsustada autentitud taotlusi ja sõelumisvigu (selles näites kasutatakse JSON-i veebimärki):

- (AFHTTPSessionManager*)getNetworkingManagerWithToken:(NSString*)token { if (self.networkingManager == nil) { self.networkingManager = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:BASE_URL]]; if (token != nil && [token length] > 0) { NSString *headerToken = [NSString stringWithFormat:@'%@ %@', @'JWT', token]; [self.networkingManager.requestSerializer setValue:headerToken forHTTPHeaderField:@'Authorization']; // Example - [networkingManager.requestSerializer setValue:@'application/json' forHTTPHeaderField:@'Content-Type']; } self.networkingManager.requestSerializer = [AFJSONRequestSerializer serializer]; self.networkingManager.responseSerializer.acceptableContentTypes = [self.networkingManager.responseSerializer.acceptableContentTypes setByAddingObjectsFromArray:@[@'text/html', @'application/json', @'text/json']]; self.networkingManager.securityPolicy = [self getSecurityPolicy]; } return self.networkingManager; } - (id)getSecurityPolicy { return [AFSecurityPolicy defaultPolicy]; /* Example - AFSecurityPolicy *policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone]; [policy setAllowInvalidCertificates:YES]; [policy setValidatesDomainName:NO]; return policy; */ } - (NSString*)getError:(NSError*)error { if (error != nil) { NSData *errorData = error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey]; NSDictionary *responseObject = [NSJSONSerialization JSONObjectWithData: errorData options:kNilOptions error:nil]; if (responseObject != nil && [responseObject isKindOfClass:[NSDictionary class]] && [responseObject objectForKey:@'message'] != nil && [[responseObject objectForKey:@'message'] length] > 0) { return [responseObject objectForKey:@'message']; } } return @'Server Error. Please try again later'; }

Kui lisasite MBProgressHUD, saab seda kasutada siin:

#import 'MBProgressHUD.h' @interface NetworkManager() @property (nonatomic, strong) MBProgressHUD *progressHUD; @end … - (void)showProgressHUD { [self hideProgressHUD]; self.progressHUD = [MBProgressHUD showHUDAddedTo:[[UIApplication sharedApplication] delegate].window animated:YES]; [self.progressHUD removeFromSuperViewOnHide]; self.progressHUD.bezelView.color = [UIColor colorWithWhite:0.0 alpha:1.0]; self.progressHUD.contentColor = [UIColor whiteColor]; } - (void)hideProgressHUD { if (self.progressHUD != nil) { [self.progressHUD hideAnimated:YES]; [self.progressHUD removeFromSuperview]; self.progressHUD = nil; } }

Ja meie sümboolse kontrolli taotlus:

- (void)tokenCheckWithSuccess:(NetworkManagerSuccess)success failure:(NetworkManagerFailure)failure { NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSString *token = [defaults objectForKey:@'token']; if (token == nil || [token length] == 0) { if (failure != nil) { failure(@'Invalid Token', -1); } return; } [self showProgressHUD]; NSMutableDictionary *params = [NSMutableDictionary dictionary]; [[self getNetworkingManagerWithToken:token] GET:@'/checktoken' parameters:params progress:nil success:^(NSURLSessionTask *task, id responseObject) { [self hideProgressHUD]; if (success != nil) { success(responseObject); } } failure:^(NSURLSessionTask *operation, NSError *error) { [self hideProgressHUD]; NSString *errorMessage = [self getError:error]; if (failure != nil) { failure(errorMessage, ((NSHTTPURLResponse*)operation.response).statusCode); } }]; }

Nüüd nimetame ViewController.m viewWillAppear meetodis seda üksikmeetodit. Pange tähele päringu lihtsust ja väikest rakendust vaate Kontroller küljel.

[[NetworkManager sharedManager] tokenCheckWithSuccess:^(id responseObject) { // Allow User Access and load content //[self loadContent]; } failure:^(NSString *failureReason, NSInteger statusCode) { // Logout user if logged in and deny access and show login view //[self showLoginView]; }];

See on kõik! Pange tähele, kuidas seda juppi saaks praktiliselt kasutada igas rakenduses, mis peab käivitamisel autentimist kontrollima.

Samamoodi saame hakkama sisselogimise POST-taotlusega: NetworkManager.h:

- (void)authenticateWithEmail:(NSString*)email password:(NSString*)password success:(NetworkManagerSuccess)success failure:(NetworkManagerFailure)failure;

NetworkManager.m:

- (void)authenticateWithEmail:(NSString*)email password:(NSString*)password success:(NetworkManagerSuccess)success failure:(NetworkManagerFailure)failure { if (email != nil && [email length] > 0 && password != nil && [password length] > 0) { [self showProgressHUD]; NSMutableDictionary *params = [NSMutableDictionary dictionary]; [params setObject:email forKey:@'email']; [params setObject:password forKey:@'password']; [[self getNetworkingManagerWithToken:nil] POST:@'/authenticate' parameters:params progress:nil success:^(NSURLSessionTask *task, id responseObject) { [self hideProgressHUD]; if (success != nil) { success(responseObject); } } failure:^(NSURLSessionTask *operation, NSError *error) { [self hideProgressHUD]; NSString *errorMessage = [self getError:error]; if (failure != nil) { failure(errorMessage, ((NSHTTPURLResponse*)operation.response).statusCode); } }]; } else { if (failure != nil) { failure(@'Email and Password Required', -1); } } }

Me võime siin välja mõelda ja lisada häireobjektid rakendusega AlertController + Blocks AppDelegate aknasse või lihtsalt saata tõrkeobjektid vaate kontrollerile tagasi. Lisaks võiksime siia salvestada kasutajaandmed või lasta vaate kontrolleril sellega hakkama saada. Tavaliselt rakendan eraldi UserManageri singletti, mis haldab mandaate ja õigusi, mis saavad otse NetworkManageriga suhelda (isiklikud eelistused).

Taas on vaate kontrolleri pool ülilihtne:

- (void)loginUser { NSString *email = @' [email protected] '; NSString *password = @'SomeSillyEasyPassword555'; [[NetworkManager sharedManager] authenticateWithEmail:email password:password success:^(id responseObject) { // Save User Credentials and show content } failure:^(NSString *failureReason, NSInteger statusCode) { // Explain to user why authentication failed }]; }

Vabandust! Unustasime API versiooni ja saadeti seadme tüüp. Lisaks oleme värskendanud lõpp-punkti väärtusest '/ checktoken' väärtuseks '/ token'. Kuna me koondasime oma võrguühenduse, on seda ülilihtne värskendada. Me ei pea oma koodi läbi uurima. Kuna kasutame neid parameetreid kõigi taotluste korral, loome abistaja.

#define API_VERSION @'1.0' #define DEVICE_TYPE UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ? @'tablet' : @'phone' - (NSMutableDictionary*)getBaseParams { NSMutableDictionary *baseParams = [NSMutableDictionary dictionary]; [baseParams setObject:@'version' forKey:API_VERSION]; [baseParams setObject:@'device_type' forKey:DEVICE_TYPE]; return baseParams; }

Sellele saab tulevikus hõlpsasti lisada suvalise arvu ühiseid parameetreid. Seejärel saame värskendada oma märgikontrolli ja autentimismeetodeid nii:

… NSMutableDictionary *params = [self getBaseParams]; [[self getNetworkingManagerWithToken:token] GET:@'/checktoken' parameters:params progress:nil success:^(NSURLSessionTask *task, id responseObject) { … … NSMutableDictionary *params = [self getBaseParams]; [params setObject:email forKey:@'email']; [params setObject:password forKey:@'password']; [[self getNetworkingManagerWithToken:nil] POST:@'/authenticate' parameters:params progress:nil success:^(NSURLSessionTask *task, id responseObject) {

Meie AFNetworkingu õpetuse pakkimine

Peatume siin, kuid nagu näete, oleme ühtsed võrguparameetrid ja -meetodid koondanud ühtsesse haldurisse, mis on meie vaate kontrolleri juurutamist oluliselt lihtsustanud. Tulevased värskendused on lihtsad ja kiired ning mis kõige tähtsam - see lahutab meie võrguühenduse kasutajakogemusest. Järgmine kord, kui disainimeeskond palub UI / UX-i kapitaalremonti, teame, et meie töö on juba võrgupoolel tehtud!

kas javascriptis on klasse

Selles artiklis keskendusime võrguühenduse üksikule, kuid neid samu põhimõtteid võiks rakendada paljude teiste tsentraliseeritud funktsioonide jaoks, näiteks:

  • Kasutaja oleku ja lubade käitlemine
  • Puutetoimingute suunamine rakenduse navigeerimisele
  • Video- ja helihaldus
  • Analytics
  • Teatised
  • Välisseadmed
  • ja palju muud ...

Keskendusime ka iOS-i rakenduse arhitektuurile, kuid seda saab sama hõlpsalt laiendada Androidile ja isegi JavaScripti. Boonusena muudab see täpselt määratletud ja funktsioonidele orienteeritud koodi loomisega rakenduste teisaldamise uutele platvormidele palju kiiremaks ülesandeks.

Kokkuvõtteks võib öelda, et kulutades varases projekti kavandamises veidi lisaaega, et luua peamised üksikud meetodid, nagu ülaltoodud võrguühenduse näide, võib teie tulevane kood olla puhtam, lihtsam ja hooldatavam.

Põhitõdede mõistmine

Mis on AFNetworking?

AFNetworking on iOS-i ja macOS-i jaoks avatud lähtekoodiga võrguraamatukogu, mis lihtsustab arendaja ülesandeid RESTful networking API-ga ja loob modulaarsed päringute / vastuste mustrid koos edukuse, edenemise ja ebaõnnestumise lõpetamise plokkidega. Sellel on väga aktiivne arendajate kogukond ja seda kasutatakse mõnes parimas rakenduses.

Mis on üksikobjekt?

Üksikobjekt on klass, mille rakenduses on teil kogu rakenduse vältel ainult üks eksemplar. Lisaks, kuna me teame, et eksemplare on ainult üks, on see hõlpsasti kättesaadav kõigile teistele klassidele, kes peavad oma meetoditele või omadustele juurde pääsema.

Tähed joondatud: IMDb reitingusüsteemi täiustamine

Tehnoloogia

Tähed joondatud: IMDb reitingusüsteemi täiustamine
Finantsmodelleerimise täiustatud head tavad: häkid intelligentseks ja vigadeta modelleerimiseks

Finantsmodelleerimise täiustatud head tavad: häkid intelligentseks ja vigadeta modelleerimiseks

Finantsprotsessid

Lemmik Postitused
Äriplaani anatoomia
Äriplaani anatoomia
Ladina-Ameerika ühinemiste ja ühinemiste parimad tavad
Ladina-Ameerika ühinemiste ja ühinemiste parimad tavad
Tarkvara kulude hindamine agiilses projektijuhtimises
Tarkvara kulude hindamine agiilses projektijuhtimises
Andekus pole kaup
Andekus pole kaup
Veebi juurdepääsetavus: miks W3C standardeid sageli eiratakse
Veebi juurdepääsetavus: miks W3C standardeid sageli eiratakse
 
Bränding on surnud, CX Design on kuningas
Bränding on surnud, CX Design on kuningas
Chatbot UX - disaininõuanded ja kaalutlused
Chatbot UX - disaininõuanded ja kaalutlused
Uus ettevõtluslaine
Uus ettevõtluslaine
Optimeeritud järjestikune keskmine kvantimise teisendus
Optimeeritud järjestikune keskmine kvantimise teisendus
Kasutajauuringute väärtus
Kasutajauuringute väärtus
Lemmik Postitused
  • mis on käsurea tööriistad
  • aws sertifitseeritud lahendused arhitekti eksami plaan
  • mis on juhtkonna väljaostmine
  • mis keeles on Microsoft Windows kirjutatud
  • ehitada ja programmeerida robotit
Kategooriad
  • Tooteinimesed Ja Meeskonnad
  • Ux Disain
  • Protsess Ja Tööriistad
  • Andmeteadus Ja Andmebaasid
  • © 2022 | Kõik Õigused Kaitstud

    portaldacalheta.pt