Elegante en leesbare code schrijven

In deze tutorial geven we je negen praktische technieken voor het schrijven van elegante en leesbare code. We hebben het niet over specifieke architecturen, talen of platforms. De focus ligt op het schrijven van betere code. Laten we beginnen.

"Het meten van de programmeervoortgang door coderegels is als het meten van de vooruitgang van vliegtuigen in gewicht." - Bill Gates

Invoering

Als je een ontwikkelaar bent, zijn er waarschijnlijk tijden geweest dat je code hebt geschreven en na een paar dagen, weken of maanden keek je er nog eens naar en zei je tegen jezelf: "Wat doet dit stuk code?" Het antwoord op die vraag was misschien: "Ik weet het echt niet!" In dat geval is het enige dat u kunt doen de code van begin tot einde doorlopen, proberen te begrijpen wat u dacht toen u het schreef.

Dit gebeurt meestal als we lui zijn en we willen gewoon die nieuwe functie implementeren waar de klant om vroeg. We willen de klus gewoon klaren met zo min mogelijk moeite. En als het werkt, geven we niet om de code zelf, omdat de klant de code nooit zal zien lelijk waarheid, laat staan ​​dat je het begrijpt. Rechts? Fout. Tegenwoordig is samenwerken aan software de standaard geworden en zullen mensen de code die je schrijft zien, lezen en inspecteren. Zelfs als uw code niet door uw collega's wordt gecontroleerd, moet u er een gewoonte van maken om duidelijke en leesbare code te schrijven. Altijd.

Meestal werk je niet alleen aan een project. We zien vaak lelijke code met variabelen met namen zoals ik, een, p, pro, en RQS. En als het echt slecht wordt, is dit patroon zichtbaar in het hele project. Als dit bekend klinkt, ben ik er vrij zeker van dat je jezelf de vraag hebt gesteld: "Hoe kan deze persoon code als deze schrijven?" Dit maakt je natuurlijk des te meer dankbaar wanneer je duidelijke, leesbare en zelfs mooie code tegenkomt. Duidelijke en schone code kan binnen enkele seconden worden gelezen en kan u en uw collega's veel tijd besparen. Dat zou je motivatie moeten zijn om kwaliteitscode te schrijven.

1. Eenvoudig te begrijpen

We zijn het er allemaal over eens dat code gemakkelijk te begrijpen moet zijn. Rechts? Het eerste voorbeeld richt zich op spacing. Laten we twee voorbeelden bekijken.

 terugkeer geslacht == "1"? gewicht * (hoogte / 10): gewicht * (hoogte * 10);
 if (geslacht == "1") retourgewicht * (lengte / 10);  else return weight * (height * 10); 

Hoewel het resultaat van deze voorbeelden identiek is, zien ze er heel anders uit. Waarom zou u meer regels code gebruiken als u minder kunt schrijven? Laten we nog twee andere voorbeelden verkennen, iets wat ik wed dat u vaak ziet.

 for (Node * node = list-> head; node! = NULL; node = node-> next) print (node-> data);
 Node * node = list-> head; als (knoop == NULL) terugkomt; while (node-> next! = NULL) Afdrukken (knooppunt-> gegevens); knoop = knooppunt-> volgende;  if (node! = NULL) Afdrukken (knooppunt-> gegevens);

Nogmaals, het resultaat van deze voorbeelden is identiek. Welke is beter? En waarom? Betekenen minder regels code betere code? We zullen deze vraag later in deze tutorial opnieuw bekijken.

2. Is kleiner altijd beter?

In de informatica hoor je vaak de uitdrukking "minder is meer". Over het algemeen geldt dat hoe beter een probleem in minder coderegels kan worden opgelost. Het kost u waarschijnlijk minder tijd om een ​​klasse met 200 lijnen te begrijpen dan een klasse met 500 lijnen. Is dit echter altijd waar? Bekijk de volgende voorbeelden.

 reservering ((! room = FindRoom (room_id))) || ! Kamer-> isOccupied ());
 room = FindRoom (room_id); if (room! = NULL) reservering (! room-> isOccupied ());

Ben je het er niet mee eens dat het tweede voorbeeld gemakkelijker te lezen en te begrijpen is?? U moet in staat zijn om te optimaliseren voor leesbaarheid. Natuurlijk kunt u een paar opmerkingen aan het eerste voorbeeld toevoegen om het gemakkelijker te begrijpen te maken, maar is het niet beter om de opmerkingen weg te laten en code te schrijven die gemakkelijker te lezen en te begrijpen is?

 // Bepaal waar het monster langs de Y-as moet worden uitgezet CGSize winSize = [CCDirector sharedDirector] .winSize; int minY = monster.contentSize.width / 2; int maxY = winSize.width - monster.contentSize.width / 2; int bereik Y = maxY - minY; int actualY = (arc4random ()% bereikY) + minY;

3. Benoemen

Het kiezen van beschrijvende namen voor zaken als variabelen en functies is een belangrijk aspect van het schrijven van leesbare code. Het helpt zowel uw collega's als uzelf om de code snel te begrijpen. Een variabele een naam geven tmp vertelt je niets anders dan dat de variabele om een ​​of andere reden tijdelijk is, wat niets meer is dan een opgeleide schatting. Het geeft niet aan of de variabele een naam, een datum enz.

Een ander mooi voorbeeld is het benoemen van een methode hou op. Het is geen slechte naam per se, maar dat hangt echt af van de implementatie van de methode. Als het een gevaarlijke bewerking uitvoert die niet ongedaan kan worden gemaakt, wilt u deze misschien een andere naam geven doden of pauze als de operatie kan worden hervat. Krijg je het idee?

Als u werkt met een variabele voor het gewicht van aardappelen, waarom zou u het een naam geven? tmp? Als je dat stukje code een paar dagen later opnieuw bezoekt, weet je niet meer wat tmp is gebruikt voor.

Dat zeggen we niet tmp is een slechte naam voor een variabele, omdat er tijden zijn waarin tmp is volkomen redelijk als variabele naam. Bekijk het volgende voorbeeld waarin tmp is helemaal geen slechte keuze.

 tmp = first_potato; first_potato = second_potato; second_potato = tmp;

In het bovenstaande voorbeeld, tmp beschrijft wat het doet, het slaat tijdelijk een waarde op. Het wordt niet doorgegeven aan een functie of methode en het wordt niet opgehoogd of gewijzigd. Het heeft een goed gedefinieerde levensduur en geen ervaren ontwikkelaar zal worden weggegooid door de naam van de variabele. Soms is het echter gewoon luiheid. Bekijk het volgende voorbeeld.

 NSString * tmp = user.name; tmp + = "" + user.phone_number; tmp + = "" + user.email; ... [sjabloon setObject: tmp forKey: @ "user_info"];

Als tmp slaat de gebruikersinformatie op, waarom heet het niet gebruikers informatie? Een juiste naamgeving van variabelen, functies, methoden, klassen, enz. Is belangrijk bij het schrijven van leesbare code. Het maakt uw code niet alleen leesbaarder, het bespaart u ook tijd in de toekomst.

Objectief-C is vrij uitgebreid, maar het is heel gemakkelijk te lezen. Apple gebruikt een goed gedefinieerde naamconventie die je in de meeste programmeertalen kunt gebruiken. U kunt meer lezen over deze naamgevingsconventie in Programmeren met Objective-C.

4. Voeg betekenis aan namen toe

Zoals we in de vorige tip zagen, is het belangrijk om namen wijs te kiezen. Het is echter even belangrijk om betekenis toe te voegen aan de namen die u gebruikt voor variabelen, functies, methoden, enz. Dit helpt niet alleen om verwarring te voorkomen, het maakt de code die u schrijft gemakkelijker te begrijpen. Het kiezen van een logische naam lijkt bijna op het toevoegen van metadata aan een variabele of een methode. Kies beschrijvende namen en vermijd generieke namen. Het woord toevoegen, bijvoorbeeld, is niet altijd ideaal zoals u in het volgende voorbeeld kunt zien.

 bool addUser (Gebruiker u) ...

Het is niet duidelijk wat Voeg gebruiker toe hoort te doen. Voegt het een gebruiker toe aan een lijst met gebruikers, een database of een lijst met mensen die zijn uitgenodigd voor een feest? Vergelijk dit met registerUser of signupUser. Dit is logischer. Rechts? Bekijk de volgende lijst om een ​​beter idee te krijgen van waar we naartoe rijden.

Woord Synoniemen
do make, perform, execute, compose, add begin lanceren, maken, beginnen, openen ontploffen ontploffen, opblazen, vertrekken, barsten

5. Benoem de grootte

Veel programmeurs houden niet van lange namen, omdat ze moeilijk te onthouden en lastig te typen zijn. Natuurlijk mag een naam niet zo belachelijk lang zijn als newClassForNavigationControllerNamedFirstViewController. Deze is moeilijk te onthouden en het maakt je code gewoon lelijk en onleesbaar.

Zoals we eerder zagen, zijn de tegenovergestelde, korte namen ook niet goed. Wat is de juiste maat voor een variabele of methode naam? Hoe kies je tussen het benoemen van een variabele len, lengte, of user_name_length? Het antwoord hangt af van de context en de entiteit waaraan de naam is gekoppeld.

Lange namen zijn niet langer een probleem bij het gebruik van een moderne IDE (Integrated Development Environment). Met codevermindering kunt u typefouten vermijden en worden er ook suggesties gedaan om het herinneren van namen minder erg te maken.

U kunt korte (re) namen gebruiken als de variabele lokaal is. Bovendien wordt aangeraden kortere namen te gebruiken voor lokale variabelen om uw code leesbaar te houden. Bekijk het volgende voorbeeld.

 NSString * link = [[NSString alloc] initWithFormat: @ "http: // localhost: 8080 / WrittingGoodCode / resources / GoodCode / getGoodCode /% @", idCode]; NSURL * infoCode = [NSURL URLWithString: link];

6. Booleans benoemen

Booleans kunnen lastig zijn om te noemen, omdat ze een andere betekenis kunnen hebben, afhankelijk van de manier waarop je de naam leest of interpreteert. In het volgende codefragment, read_password kan betekenen dat het wachtwoord is gelezen door het programma, maar het kan ook betekenen dat het programma het wachtwoord moet lezen.

 BOOL readPassword = YES;

Om dit probleem te voorkomen, kunt u de bovenstaande boolean hernoemen naar didReadPassword om aan te geven dat het wachtwoord is gelezen of shouldReadPassword om aan te geven dat het programma het wachtwoord moet lezen. Dit zie je bijvoorbeeld heel vaak in Objective-C.

7. Om commentaar te geven of om niet te reageren

Opmerkingen toevoegen aan code is belangrijk, maar het is net zo belangrijk om ze spaarzaam te gebruiken. Ze moeten worden gebruikt om iemand te helpen uw code te begrijpen. Het lezen van opmerkingen kost echter ook tijd en als een opmerking niet veel waarde toevoegt, wordt die tijd verspild. Het volgende codefragment laat zien hoe niet om opmerkingen te gebruiken.

 // Dit gebeurt wanneer een geheugenwaarschuwing wordt ontvangen - (ongeldig) didReceiveMemoryWarning [super didreceive memory alert]; // Verwijder alle bronnen die opnieuw kunnen worden gemaakt.  // Dit valideert de velden - (BOOL) validateFields 

Zijn deze codefragmenten nuttig voor u? Het antwoord is waarschijnlijk "Nee" De opmerkingen in de bovenstaande voorbeelden voegen geen aanvullende informatie toe, vooral omdat de methodamen al erg beschrijvend zijn, wat gebruikelijk is in Objective-C. Voeg geen opmerkingen toe die het voor de hand liggende uitleggen. Bekijk het volgende voorbeeld. Is dit niet een veel beter gebruik van opmerkingen??

 // Bepaal de snelheid van het monster int minDuration = 2.0; int maxDuration = 8,0; int bereikDuration = maxDuration - minDuration; int actualDuration = (arc4random ()% rangeDuration) + minDuration;

Door dit soort opmerkingen is het heel gemakkelijk om snel en efficiënt door een codebasis te navigeren. Het voorkomt dat u de code moet lezen en helpt u de logica of het algoritme te begrijpen.

8. Stijl en consistentie

Elke taal of platform heeft een (of meer) stijlgids en zelfs de meeste bedrijven hebben er een. Zet je de accolades van een Objective-C methode op een aparte regel of niet?

 - (void) calculationOffset 
 - (void) calculationOffset 

Het antwoord is dat het er niet toe doet. Er is geen goed antwoord. Natuurlijk zijn er stijlgidsen die u kunt gebruiken. Het belangrijkste is dat uw code consistent is in termen van stijl. Hoewel dit misschien niet van invloed is op de kwaliteit van je code, beïnvloedt het zeker de leesbaarheid en zal het hoogstwaarschijnlijk je collega's irriteren of wie dan ook je code leest. Voor de meeste ontwikkelaars is lelijke code de slechtste code.

9. Gerichte methoden en functies

Een veelgemaakte fout bij ontwikkelaars is om zoveel mogelijk functionaliteit in functies en methoden te proppen. Dit werkt, maar het is onelegant en maakt debuggen een pijn in de nek. Je leven - en dat van je collega's - zal veel gemakkelijker worden als je grotere problemen opsplitst in kleine stukjes en die stukjes aanpakt in afzonderlijke functies of methoden. Bekijk het volgende voorbeeld waarin we een afbeelding naar schijf schrijven. Dit lijkt een triviale taak, maar er is nog veel meer als je het goed wilt doen.

 - (BOOL) saveToImage: (UIImage *) image withFileName: (NSString *) bestandsnaam BOOL result = NO; NSString * documents = nihil; NSArray * paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES); if (paths.count) documents = [paths objectAtIndex: 0]; NSString * basePath = [documents stringByAppendingPathComponent: @ "Archive"]; if (! [[NSFileManager defaultManager] fileExistsAtPath: basePath]) NSError * error = nil; [[NSFileManager defaultManager] createDirectoryAtPath: basePath withIntermediateDirectories: YES attributes: nil error: & error]; if (! error) NSString * filePath = [basePath stringByAppendingPathComponent: fileName]; result = [UIImageJPEGRepresentation (image, 8.0) writeToFile: filePath atomically: YES];  else NSLog (@ "Kan de map niet aanmaken vanwege fout% @ met gebruikersinformatie% @.", error, error.userInfo);  retourneer resultaat; 

Als een code-eenheid te veel probeert te doen, krijg je vaak diep geneste voorwaardelijke uitspraken, veel foutcontrole en te ingewikkelde voorwaardelijke uitspraken. Deze methode doet drie dingen, haalt het pad van de documentenmap van de toepassing op, haalt het pad op en maakt het voor de archievendirectory en schrijft de afbeelding naar schijf. Elke taak kan op zijn eigen manier worden geplaatst zoals hieronder wordt getoond.

 - (BOOL) saveToImage: (UIImage *) image withFileName: (NSString *) bestandsnaam NSString * archivesDirectory = [self applicationArchivesDirectory]; als (! archivesDirectory) NO retourneert; // Pad maken NSString * filePath = [archivesDirectory stringByAppendingPathComponent: fileName]; // Write Image to Disk return [UIImageJPEGRepresentation (image, 8.0) writeToFile: filePath atomically: YES]; 
 - (NSString *) applicationDocumentsDirectory NSArray * paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES); return paths.count? [paths objectAtIndex: 0]: nihil; 
 - (NSString *) applicationArchivesDirectory NSString * documentsDirectory = [self applicationDocumentsDirectory]; NSString * archivesDirectory = [documentsDirectory stringByAppendingPathComponent: @ "Archieven"]; NSFileManager * fm = [NSFileManager defaultManager]; if (! [fm fileExistsAtPath: archivesDirectory]) NSError * error = nil; [fm createDirectoryAtPath: archivesDirectory withIntermediateDirectories: YES attributes: nil error: & error]; if (error) NSLog (@ "Kan directory niet aanmaken vanwege fout% @ met gebruikersinfo% @.", error, error.userInfo); terugkeer nul;  return archivesDirectory; 

Dit is veel gemakkelijker te debuggen en te onderhouden. U kunt zelfs het applicationDocumentsDirectory methode op andere plaatsen van het project, wat een ander voordeel is van het doorbreken van grotere problemen in hanteerbare stukken. Testcode wordt ook veel eenvoudiger.

Conclusie

In dit artikel hebben we aandachtig gekeken naar het schrijven van leesbare code door verstandig namen te kiezen voor variabelen, functies en methoden, consistent te zijn bij het schrijven van code en complexe problemen te doorbreken in hanteerbare brokken. Als u vragen of feedback heeft, kunt u hieronder een reactie achterlaten.