clean code (ua)
TRANSCRIPT
Чому «якісний код»?
• Maintainability
• >50%
– Зміни
– Багфікси
– Підтримка
• Ітеративні методи розробки
Стандартні питання до розробника
• Що робить система, коли відбувається Х?
• Як конкретно працює функціональність Y?
• Звідки дістаються опції Z?
• Скільки часу займе реалізувати Х?
Де відповіді?
• В специфікації
• Якщо у вас вона є ;)
• В коді
• Код є завжди
• Only the Code Tells the Truth
В реальному житті
• Неякісний код сповільнює розробку
• Щоб дотриматись графіка, розробники швидко пишуть неякісний код
Парадокс!
Осмислені імен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, AcquireFeed 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);}
Сприяє документуванню коду
Рівень абстракції
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
• Навіть якщо це не просто, ...
• ... це все одно цікаво
• Чому б цим не зайнятись?
Професіоналізм
• Будьте професіоналом
• Професіонали не пишуть неякісний код
• Якщо професіонал написав неякісний код, він його почистить