Чистий код
DESCRIPTION
Якісний код забезпечує швидкість розробки програмних продуктів. Олександр ПавлишакTRANSCRIPT
Стандартні питання до розробника
Що робить система, коли відбувається Х?
Як конкретно працює функціональність Y?
Звідки дістаються опції Z?
Скільки часу займе реалізувати Х?
В реальному житті
Неякісний код сповільнює розробку
Щоб дотриматись графіка, розробники швидко пишуть неякісний код
Парадокс!
Осмислені іменa
public void Copy(char[] a1, char[] a2){ for (int i = 0; i < a1.Length; i++) { a2[i] = a1[i]; }}
Осмислені іменa
public void Copy(char[] source, char[] destination){ for (int i = 0; i < source.Length; i++) { destination[i] = source[i]; }}
Ім’я відображає наміри і суть
d, tot, res, srv, c, cc, c1, c2 – BAD!
createdDate, totalCount, result, storageService, characters – GOOD!
Довге змістовне ім’я краще короткого незрозумілого
Змістовне ім’я краще чисельної константи
Константи...
if (!resource.Exists){ result = 404;}else if (resource.WasMoved){ result = 301;}else if (user == null){ result = 401;}...
Константи
...
if (!resource.Exists){ result = HttpStatusCode.NotFound;}else if (resource.WasMoved){ result = HttpStatusCode.MovedPermanently;}else if (user == null){ result = HttpStatusCode.Unauthorized;}...
Коротке ім’я – коротка видимість
int[] elements = …for (int i = 0; i < elements.Count(); i++){ elements[i] = elements[i] * 2;}
Послідовні імена
Get, Fetch, Retrieve, Obtain, Acquire
Feed feed = FeedService.ObtainSyndicationFeed();if (feed.IsValid){ Storage feedStorage = GetFileStorage(path); List<Post> posts = feed.RetrievePosts(); feedStorage.Save(posts);}
Виберіть одне і послідовно дотримуйтесь його
Надлишковість
nameString, customersArray
sName, iCount, arrCustomers
string nameString = "Kenny";string[] girlsArray = FindAllAttractiveGirls();
FileStream ouputFile = File.Open("c:\\autoexec.bat");Url urlToBashOrg = new Url("http://bash.org.ru");
Імена з предметної області
List<Person> list =
country.GetAllUnemployedPeople();if (list.Count > 1000 * 1000){ presidentFacebookPage.CurrentStatus =
"Still working...";}
Імена з предметної області
List<Person> unemployedPeople =
country.GetAllUnemployedPeople();if (unemployedPeople.Count > 1000 * 1000){ presidentFacebookPage.CurrentStatus =
"Still working...";}
Імена з предметної області
if (pageIdsByUserId.Get(user.Id) .ContainsKey(page.Id) ) {...}
Map<int, Map<int, int>> pageIdsByUserId;
if (user.HasAccessTo(page)) {...}
Типові імена
Імена зміннихcustomer, currentPosition, isCompleted, result
Імена класів – іменникиCustomer, UrlParser, Page, SortAlgorithm
Імена методів – дієсловаGetCustomers(), CreateDirectory(), OpenSocket(), Save(), Close()
Коротка функція
if (user.IsAuthenticated)
{
SendProcessingCompletedEmail();
}
else
{
RedirectUserToLoginForm(user);
}
Сприяє документуванню коду
Good codeBeautiful
Easy to read
Easy to understand
Clean
Clear
TestedCohesive
Compact
Efficient
Obvious
Organic
Рівень абстракції
public void SynchronizeNewTasks(){ string database = "DBGLOBAL"; if (user.HasAttribute("LOCAL")) { database = service.GetDatabaseBaseName() +
"_LOCAL"; } if (user.Tasks.ContainsNewTasks()) { SaveNewTasks(database, user.Tasks.GetNewTasks()); }}
Рівень абстракції
public void SynchronizeNewTasks(){ if (user.Tasks.ContainsNewTasks()) {
string database = GetDatabaseForUser( user); SaveNewTasks(database,
user.Tasks.GetNewTasks()); }}
Аргументи функцій
Без аргументів, Save() – найкращі :)
Один аргумент, Send(address) – теж нічого
Два, Copy(source, destination) – ОК
Три – вже не дуже
Більше – значно погіршують читабельність :(
Булеві аргументи
List<Task> tasks = ...;Storage storage = ...;SaveNewTasks(tasks, storage, true, false, false, true, true);
:(
Вихідні аргументи
byte[] fileContent = ...byte[] compressedContent;Compress(fileContent, out compressedContent);
Побічні ефекти
public bool IsPasswordValid(string userName, string password){ User user = Database.FindUserByName(userName); string encryptedPassword = Encryptor.Encrypt(password); bool result = user.EncryptedPassword == encryptedPassword; if (result) { Session.Initialize(); } return result;}
Exceptions замість кодів помилок
if (FindUserByName(userName, out user) == STATUS_OK)
{ if (Encrypt(password, out encryptedPassword) == STATUS_OK)
{ bool result = user.EncryptedPassword == encryptedPassword; if (result)
{ if (Session.Initialize() == STATUS_OK) return true; else LogError(...); } ...
Обробляйте помилки
...
// Catching exceptions is for communists
...Wrong!
http://stackoverflow.com
Граничні умови
Враховуйте граничні умови
Тестуйте граничні умови
public void Sort(int[] array){ ...}
Непотрібний код
Функції, які не ніде не використовуються – видаляйте їх!
Код, який ніколи не викликається – видаляйте його!
Історія – в системі контролю версій
Здоровий глузд
Код не повинен викликати здивування
Поведінка має бути очевидна
«Ви працюєте з чистим кодом, якщо кожна функція робить приблизно те, що ви очікуєте»
...
/////////////////////////////////////// this is a well commented line
...
http://stackoverflow.com
Про коментарі
Надавайте перевагу коду
Коментуйте те, що не можна виразити в коді
Проблема коментарів – супровід
Неточні коментарі – гірше відсутності коментарів
Коментарі не компенсують поганого коду
vs.
if (server.IsRedirectNeeded())…
// check if we should redirect to another URLif (server.HasResponse && server.HttpResponseCode == 301)…
vs.
bool isRedirectNeeded = server.HasResponse && server.HttpResponseCode == 301;if (isRedirectNeeded)…
Як коментувати
Коментуйте публічний API
Коментуйте внутрішній API тільки якщо він складний
Не використовуйте коментар там, де можна використати функцію або змінну
Видаляйте закоментований код
Single responsibility
Класи повинні бути компактні
Компактність визначається кількістю відповідальностей (responsibilities)
Клас повинен мати одну відповідальність (single responsibility)
Клас повинен мати одну причину для зміни
Багато компактних класів
if-statementsЧим більше if-statements, тим більше потенційних помилок
public int GetHours() { if (_numberOfManuals <= SMALL) { if (_serviceType == "writing") return 30 * _numberOfManuals; if (_serviceType == "analysis") return 10; } else if (_numberOfManuals <= MEDIUM) { if (_serviceType == "writing") return (SMALL * 30) + (20 * _numberOfManuals - SMALL);
if (_serviceType == "analysis") return 20; } else //i.e. LARGE { if (_serviceType == "writing") return (SMALL * 30) + (20 * (MEDIUM - SMALL)) + (10 * _numberOfManuals - MEDIUM); if (_serviceType == "analysis") return 30; } return 0; //Just a default fallback for this contrived example }
Робота
Робота розробника не закінчується після того, коли програма запрацювала
Після цього потрібно покращити структуру і чистоту коду
Правило
Дотримуйтесь «правила бойскаута»
кожен раз коли ви працюєте з кодом, залишайте його трохи чистішим, ніж він був до цього
Це просто
Переважно неважко покращити код
Лише трохи переіменувать, введення нових функцій, трохи реструктуризації
Це не rocket science
Навіть якщо це не просто, ...
... це все одно цікаво
Чому б цим не зайнятись?
Професіоналізм
Будьте професіоналом
Професіонали не пишуть неякісний код
Якщо професіонал написав неякісний код, він його почистить