Jedním ze základních pravidel psaní přehledného kódu
je nepsat zbytečný kód. Pojďme si ukázat pár příkladů
zbytečného kódu, kterému je zahodno se raději vyhnout.
Komentáře
Programátoři jsou často velice kreativní lidé, ale svou kreativitu si
vybíjí nesprávným způsobem. Tak se může stát, že během své kariéry
narazíte na skvosty typu:
- zakomentované kusy kódu
- označení dočasného kódu v kometáři
- vylévání srdíčka nad vlastní blbostí
K bodu prvnímu. Když něco takového potkáte, rovnou to smažte,
nesnažte se nad tím přemýšlet. Jestli někdy ten kód budete potřebovat,
najdete ho ve vašem VCS. Pokud se přistihnete, že máte označený blok kódu
a chystáte se zmáčknout magickou kombinaci kláves Ctrl+K, C,
ušetřete si práci a rovnou zmáčkněte Delete. ;)
K bodu druhému. Nejprve zjistěte, kdo to napsal a dejtemu rovnou pohlavek
nebo seberte prémie. Nejlépe oboje, u někoho lépe fungují podmíněné
reflexy spojené s fyzickou bolestí, někdo si vzpomene, že mu nezbyly
peníze na tu skvělou chlastačku a už to nebude chtít opakovat. Zjišťovat,
co na koho platí je moc drahé. ;)
Pro představu, našli jsme takovýto kód:
if (Request.Cookies.AllKeys.Contains("ticket")) {
InvitedUser iuser = _invitedUsersRepository.FindByHash(Request.Cookies["ticket"].Value);
if (iuser != null) {
iuser.Valid = false;
_invitedUsersRepository.Save(iuser);
}
}
;
Autora jsme ztrestali. První krok máme za sebou, co dál? Tohle asi hned
smazat nepůjde. Prvním možným krokem, je nadefinovat build symbol a tyto
bloky obalit preprocesorovou podmínkou:
#if TEST_VERSION
if (Request.Cookies.AllKeys.Contains("ticket")) {
InvitedUser iuser = _invitedUsersRepository.FindByHash(Request.Cookies["ticket"].Value);
if (iuser != null) {
iuser.Valid = false;
_invitedUsersRepository.Save(iuser);
}
}
#endif
;
Sice jsme se nezbavili špatného kódu, ale aspoň pujde dočasná featura
vypnout z jednoho místa a kód hovoří trochu jasněji.
Pokud napíšete blok kódu a pocítíte potřebu uvodit ho komentářem,
nebo takových bloků máte zasebou víc, je čas refaktorovat! Místo
komentářů vytvořte metody s popisnými názvy (ekvivalent komentáře).
Komentáře mají tu vlastnost, že rychle ztrácejí význam a zůstávají
v místech, kde už není co komentovat.
Pravidlo: Když ucítíte potřebu napsat v kódu komentář, zamyslete
se, zda nenajdete lepší způsob jak tuto myšlenku vyjádřit.
Poznejte svůj jazyk
Za programátora se považuje každý, kdo se v nějakém jazyce naučí
pár příkazů. Někdo si vystačí s málem a pak mu nezbývá než prasit.
Základem je poznat jazyk, ve kterém se chystáme sdělovat své myšlenkové
pochody. Zdrojový kód se mnohem častěji čte než píše, proto by měl
obsahovat co nejméně zbytečností, které odvádějí pozornost.
Začněme třeba u JavaScriptu. Často se setkávám s kódem, který
napovídá, že autor má hrubé znalosti C a jemu podobných jazyků, ale
o JavaScriptu moc potuchy nemá.
Podmíněné přiřazení
Jednou z typických ukázek může být validace vstupních parametrů a
nastavení výchozí hodnoty, pokud přišlo null. Nejhorší
případ:
function example(options) {
if (options == null) {
this.options = { color: 'red' };
}
else {
this.options = options;
}
}
O něco chytřejší programátor použije ternární podmíněný
operátor:
function example(options) {
this.options = (options != null) ? options : { color: 'red' };
}
Vývojář znalý JavaScriptu však odbourá zbytečné ruchy a vznikne
logický zápis:
function example(options) {
this.options = options || { color: 'red' };
}
Pojďme teď dál k mému oblíbenému C#. Předchozí příklad tam nejde
řešit operátorem || protože C# je silně typový a null
!= false. Ovšem C# 2.0 přinesl s Nullable<T>
hodnotovými typy i operátor ??. Ten slouží jako zkrácená
varianta ternárního podmíněného operátoru. Díky němu můžeme
následující kód:
public void Example(Options options) {
_options = (options != null) ? options : new Options { Color = Color.Red };
}
Pěkně odšumět:
public void Example(Options options) {
_options = options ?? new Options { Color = Color.Red };
}
Pokud pracujete s Nullable hodnotovými typy, zkraťte
i následující zápis:
public void Example(int? pageIndex) {
int page = 1 + (pageIndex.HasValue ? pageIndex.Value : 0);
}
Na:
public void Example(int? pageIndex) {
int page = 1 + (pageIndex ?? 0);
}
Generické parametry
Dalším, velice častým šumem, je explicitní uvádění generických
parametrů, tam kde to není potřeba. Za příklad si vezměme třeba
generickou metodu Assert.Equal<T>(T expected, T actual)
z frameworku xUnit.net. Můžeme ji
volat takto:
Assert.Equal<string>("test", null);
Jenže kompilátor není žádný hlupák a dokáže správně dosadit typ
generického parametru podle typu předávaného parametru. Proto je možné
napsat, mnohem přehlednější zápis:
Assert.Equal("test", null);
FxCop vám přímo radí: Pište generické metody tak, aby se typ
generického parametru dal implicitně odvodit.
Zbytečné volání ToString
Často se také můžete setkat s voláním metody ToString
tam, kde to není pořeba. Příklad:
double rating = 3.8;
string output = "Your rating is: " + rating.ToString();
Tohle lze napsat bez šumu:
double rating = 3.8;
string output = "Your rating is: " + rating;
Teď si jistě, říkáte, že jsem asi blbec, jak můžu po silně typovém
C# chtít, aby sečetl string a double. Nuže můžu.
Krom toho, že je C# silně typový, podporuje i přetěžování operátorů!
A designéři třídy String přidali přetížení operátoru
+ na kterýkoliv Object, na kterém zavolají metodu
ToString za vás. :)
Zbytečné explicitní
přetypování
Občas se bohužel můžeme v kódu potkat s věcmi, nad kterými
zůstává rozum stát. Někdo například už pochopil dědičnost. Pochopil,
že třída List<T> implementuje rozhraní
IList<T> a to dědí z ICollection<T>,
které je potomkem obecného rozhranní IEnumerable<T>.
Jenže pak přijde na praktické použití a je zle:
public class UserDetailViewData {
public IEnumerable<User> Friends { get; set; }
}
public class UsersController : Controller {
public ActionResult Detail(userName) {
IList<User> friends = GetFriendsOfUser(userName);
return View(new UserDetailViewData {
Friends = (IEnumerable<User>)friends
});
}
}
Takhle vytržené z kontextu to nemusí vypadat až tak zle, ale originál
mě stál málem ukroucení hlavy.
Roztahaná inicializace
Novinkou C# 3.0 jsou inicializátory. Zvažte následující kód:
var user = new User();
user.Name = "Josef Novák";
user.Gender = Gender.Male;
Přepsat na:
var user = new User {
Name = "Josef Novák",
Gender = Gender.Male,
};
Vyhnete se tím, zbytečnému opakování user..
Závorky navíc
Častý šumem v kódy bývají také závorky, které nemají žádný
význam, zejména u inicilizátorů s bezparametrickým konstrukotrem
new User() { Name = "Josef Novák" } a u anonymních
bezparametrických delegátů delegate() { return true; }. Tyto
závorky klidně smažte a u delegátů zvažte kompresi na lambda výraz:
()=> true.
Na druhou stranu existuje spousta případů, kdy vhodně vložené závorky
zvyšují čitelnost.
Závěr
Když říkám, že je lepší psát némě kódu, rozhodně tím nemám na
mysli kriptické zápisy alá regexy. To, co ušetříte na zbytečném kódu,
klidně investujte do popisných názvů proměnných a metod. Důležitá je
čitelnost. Se spoustou výše zmiňovaných úprav vám mohou
pomoci nástroje, např. CodeRush
Express, který je zcela zdarma.
Rozhodně jsem nepokryl všechny možnosti, kde psát zbytečný kód navíc.
Jistě taky některé znáte, podělte se o ně v komentářích. :)