Upload
evgeny-kaziak
View
910
Download
3
Embed Size (px)
DESCRIPTION
Clean Code
Citation preview
Понятный код
Paul Malikov
содержание
• в чем проблема?
• названия• функции• комментарии• форматирование• классы
в чем проблема?
в чем проблема?
в чем проблема?
в чем проблема?
содержательные имена
// elapsed time in daysint d;int dd;int d2;
int elapsedTimeInDays; int daysSinceCreation;int daysSinceModification; int fileAgeInDays;
имя должно передавать намерение программиста
public List<int[]> getThem() { List<int[]> list1 = new ArrayList<int[]>(); for (int[] x : theList) if (x[0] == 4) list1.add(x); return list1;}
Неявный код
1. Что хранится в theList?2. Почему так важен нулевой элемент x[0]?3. Что означает 4?4. Как использовать возвращаемое значение?
public List<int[]> getFlaggedCells() { List<int[]> flaggedCells = new ArrayList<int[]>(); for (int[] cell : gameBoard) if (cell[STATUS_VALUE] == FLAGGED) flaggedCells.add(cell); return flaggedCells;}
public List<Cell> getFlaggedCells() { List<Cell> flaggedCells = new ArrayList<Cell>(); for (Cell cell : gameBoard) if (cell.isFlagged()) flaggedCells.add(cell); return flaggedCells;}
Явный код лучше неявного
public List<int[]> getThem() { List<int[]> list1 = new ArrayList<int[]>(); for (int[] x : theList) if (x[0] == 4) list1.add(x); return list1;}
Неявный код
1. Что хранится в theList?2. Почему так важен нулевой элемент x[0]?3. Что означает 4?4. Как использовать возвращаемое значение?
public List<int[]> getFlaggedCells() { List<int[]> flaggedCells = new ArrayList<int[]>(); for (int[] cell : gameBoard) if (cell[STATUS_VALUE] == FLAGGED) flaggedCells.add(cell); return flaggedCells;}
public List<Cell> getFlaggedCells() { List<Cell> flaggedCells = new ArrayList<Cell>(); for (Cell cell : gameBoard) if (cell.isFlagged()) flaggedCells.add(cell); return flaggedCells;}
Явный код лучше неявного
избегайте дезинформации
• избегайте названий, значение которых зависит от контекста
• hp, aix, sco - названия Unix платформ
hp - hipotenuseaix - augmentationIndexsco - spaceControlOficer
избегайте дезинформации
• аккуратно используйте специфические программерские названия
• используйте альтернативные названияaccountList - type of List<T>?
accountGroupbunchOfAccounts
accounts
избегайте дезинформации
• избегайте слабо отличающихся имен
XYZControllerForHandlingOfStringsXYZControllerForStorageOfStrings
избегайте дезинформации
• не используйте последовательную нумерацию
public static void copyChars(char a1[], char a2[]) { for (int i = 0; i < a1.length; i++) { a2[i] = a1[i]; }}
public static void copyChars( char source[], char destination[]) { for (int i = 0; i < source.length; i++) { destination[i] = source[i]; }}
избегайте дезинформации
• не используйте лишние слова (noise words)
ProductInfo - ProducttheMessage - messagemoneyAmount - money
accountData - account
используйте произносимые имена
• humans are good in words
class DtaRcrd102 { private Date genymdhms; private Date modymdhms; private final String pszqint = "102";};
class Customer { private Date generationTimestamp; private Date modificationTimestamp; private final String recordId = "102";};
используйте имена, удобные для поиска
• используйте имена переменных из одной буквы только в локальной области видимости в маленьких методах
• не используйте численные константыMAX_CLASSES_PER_STUDENT вместо 7
e - самая встречаемая буква алфавита
используйте имена, удобные для поиска
for (int j=0; j<34; j++) { s += (t[j]*4)/5;}
int realDaysPerIdealDay = 4; const int WORK_DAYS_PER_WEEK = 5; int sum = 0; for (int j=0; j < NUMBER_OF_TASKS; j++) { int realTaskDays = taskEstimate[j] * realDaysPerIdealDay; int realTaskWeeks = (realdays / WORK_DAYS_PER_WEEK); sum += realTaskWeeks;}
названия методов
• должны выражать действие
• с перегруженными конструкторами можно использовать static factory методы с названиями, описывающими агрументы
Complex fulcrumPoint = new Complex(23.0);
Complex fulcrumPoint = Complex.FromRealNumber(23.0);
размер методов
• методы должны быть маленкими
• еще меньше!• это подразумевает однострочные if, esle,
while (названия методов отлично документируют ветвление)
• и вложенные структуры не глубже второго уровня
public static String renderPageWithSetupsAndTeardowns( PageData pageData, boolean isSuite) throws Exception { boolean isTestPage = pageData.hasAttribute("Test"); if (isTestPage) { WikiPage testPage = pageData.getWikiPage(); StringBuffer newPageContent = new StringBuffer(); includeSetupPages(testPage, newPageContent, isSuite); newPageContent.append(pageData.getContent()); includeTeardownPages(testPage, newPageContent, isSuite); pageData.setContent(newPageContent.toString()); } return pageData.getHtml();}
public static String renderPageWithSetupsAndTeardowns( PageData pageData, boolean isSuite) throws Exception { if (isTestPage(pageData)) includeSetupAndTeardownPages(pageData, isSuite); return pageData.getHtml();}
правило одной операции
• функция должна выполнять только одну операцию
• она должна выполнять ее хорошо• и ничего другого она делать не должна
• один уровень абстракции на функцию
private void includeSetupAndTeardownPages() { includeSetupPages(); includePageContent(); includeTeardownPages(); updatePageContent();}private void includeSetupPages() { if (isSuite) includeSuiteSetupPage(); includeSetupPage();}private void includeSuiteSetupPage() { include(SuiteResponder.SUITE_SETUP_NAME, "-setup");}private void includeSetupPage() { include("SetUp", "-setup");}private void includePageContent() { newPageContent.append(pageData.getContent());}
аргументы функции
• zero (niladic) - наилучший вариант
• one (monadic)
• two (dyadic)
• three (triadic)
• more (polyadic) - следует избегать
аргументы функции
• с точки зрения тестирования малое количество аргументов упрощает перебор комбинаций
• использование выходных аргументов нарушает общую идею подачи исходных данных в качестве аргументов и получение результата через возвращаемое значение функции
niladic
void saveTheMankind()
типичные случаи monadic
• проверка условия, связанного с аргументом
• обработка аргумента, его преобразование и возвращение
• сообщение о событии
boolean contains(“MyValue”)
InputStream fileOpen(“MyFile”)
void passwordAttemptFailedNtimes(int attempts)
типичные случаи monadic
• старайтесь избегать выходных аргументов
• используйте возвращаемое значениеStringBuffer transform(StringBuffer in)
void transform(StringBuffer out)
monadic
Mrs marry(Miss girl)
типичные случаи diadic
• более сложны для понимания
• подходят для агрументов с естественным порядком
writeField(outputStream, name) -> outputStream.writeField(name)
Point p = new Point(x,y);
типичные случаи diadic
• если порядок не известен, можно преобразовать в monadic
compare(expected, actual) -> object.compare(expected)
diadic
compare(expected, actual)
разделение команд и запросов
• функция должна либо делать что-то, либо отвечать на какой-то вопрос, но не оба действия вместе
• метод устанавливает атрибут?• или метод проверяет значение атрибута?
public boolean set(String attribute, String value);
if (set("username", "unclebob"))...
public boolean setAndCheckIfExists(String attribute, String value);
if (attributeExists("username")) { setAttribute("username", "unclebob"); ...}
разделение команд и запросов
использование исключений
• старайтесь использовать исключения вместо кодов ошибок в качестве возвращаемого значения
if (deletePage(page) == E_OK) { if (registry.deleteReference(page.name) == E_OK) { if (configKeys.deleteKey(page.name.makeKey()) == E_OK) { logger.log("page deleted"); } else { logger.log("configKey not deleted"); } } else { logger.log("deleteReference from registry failed"); } } else { logger.log("delete failed"); return E_ERROR;}
try { deletePage(page); registry.deleteReference(page.name); configKeys.deleteKey(page.name.makeKey());} catch (Exception e) { logger.log(e.getMessage());}
комментарии
• не делают плохой код лучше• объясняйте при помощи кода
// Check to see if the employee is eligible for full benefits if ((employee.flags & HOURLY_FLAG) && (employee.age > 65)) ...
if (employee.isEligibleForFullBenefits()) ...
boolean isEligibleForFullBenefits = (employee.flags & HOURLY_FLAG) && (employee.age > 65);
if (isEligibleForFullBenefits) ...
комментарии
• закомментированный код должен удаляться
• для читателя такой код означает, что есть серьезная причина, по которой этот код не был удален
форматирование
• вертикальное форматирование и метафора газетной статьи
• первый параграф дает вам общее описание статьи
• прочитав его, вы определяете, та ли это статья, которая вам нужна
• чем дальше, тем больше дателей
• газета состоит из статей маленьких и побольше• если бы газета состояла из одной большой статьи, ее невозможно было бы читать
классы
• классы должны быть маленькие
• еще меньше!• единица измерения - количество обязанностей (responsibility count)
название класса
• должно выражать его обязанность• название, по сути, один из первых способов определить размер класса
• слова Processor, Manager, Super указывают на совмещение обязанностей
• опишите класс в 25 словах, не употребляя “если”, “и”, “или”, “но”
Single Responsibility Principle
• утверждает, что класс или модуль должен иметь одну и только одну причину для изменения
• наиболее часто игнорируется программистами, т.к. считают, что их работа закончена после того, как код начал работать
public class SuperDashboard { public Component getLastFocusedComponent(); public void setLastFocused(Component lastFocused); public int getMajorVersionNumber(); public int getMinorVersionNumber(); public int getBuildNumber()}
public class Version { public int getMajorVersionNumber(); public int getMinorVersionNumber(); public int getBuildNumber();}
связность
• классы должны иметь небольшое количество переменных экземпляра
• каждый метод класса должен оперировать с этими переменными
• чем больше переменных, с которыми оперируют методы (для каждого метода), тем выше связность
public class Stack { private int topOfStack = 0; List<Integer> elements = new LinkedList<Integer>(); public int size() { return topOfStack; } public void push(int element) { topOfStack++; elements.add(element); } public int pop() throws PoppedWhenEmpty { if (topOfStack == 0) throw new PoppedWhenEmpty(); int element = elements.get(--topOfStack); elements.remove(topOfStack); return element; }}
связность
• разбиение функций ведет к снижению связности
• если классы утрачивают связность, разбейте их!
что еще в этой книге?
• обработка ошибок• модульные тесты• системы• формирование архитектуры• многопоточность• smells and heruistics
литература
• Robert C. Martin, Clean Code. A Handbook of Agile Software Craftsmanship - ISBN-13: 978-0-13-235088-4
• Robert C. Martin, Agile Principles, Patterns, and Practices ISBN-13: 978-0135974445
• Martin Fowler, Refactoring: Improving the Design of Existing Code ISBN-13: 978-0201485677