47

WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

  • Upload
    webcamp

  • View
    117

  • Download
    3

Embed Size (px)

Citation preview

Page 1: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь
Page 2: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

РАЗРАБОТКАнаукаремесло

Page 3: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

REEF KNOT

Page 4: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

MONOLOG // Create the logger$logger = new Logger('my_logger');

// Now add some handlers$handler = new StreamHandler('my_app.log', Logger::DEBUG);$logger­>pushHandler($handler);

$logger­>pushProcessor(function ($record) $record['extra']['dummy'] = 'Hello world!';

return $record;);

// You can now use your logger$logger­>addInfo('My logger is now ready');

Page 5: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

ОБЪЕКТНАЯГИМНАСТИКАангл. Object Calisthenics.Calisthenics — зарядка,

гимнастика.

читаемостьтестируемостьпонятностьподдерживаемость

Page 6: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

ОБЪЕКТНАЯГИМНАСТИКА

1. Только один уровень отступа в методе2. Не используйте else3. Оберните все примитивные типы4. Коллекции первого класса5. Одна точка на строку6. Не используйте сокращения7. Сохраняйте сущности короткими8. Никаких классов с более чем 2 атрибутами9. Никаких геттеров, сеттеров и свойств

Page 7: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

9. Никаких геттеров, сеттеров и свойств

ПРЕЗЕНТАЦИЯbit.ly/webcamp16

КОДbit.ly/webcamp16code

Page 8: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

#4КОЛЛЕКЦИИ ПЕРВОГО

КЛАССАинкапсуляция поведений, относящихся кколлекции:поискфильтрация

Page 9: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

LOGGER/** * The handler stack * * @var HandlerInterface[] */ protected $handlers; public function isHandling(int $level): bool $record = array( 'level' => $level, );

foreach ($this­>handlers as $handler) if ($handler­>isHandling($record)) return true;

return false;

Page 10: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

HANDLERSTACK/** * The handler stack * * @var HandlerStack */ protected $handlers; public function isHandling(int $level): bool return $this­>handlers­>isHandling($level);

Page 11: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

GROUPHANDLER/** * The handler stack * * @var HandlerInterface[] */ protected $handlers; public function isHandling($record): bool foreach ($this­>handlers as $handler) if ($handler­>isHandling($record)) return true;

return false;

Page 12: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

HANDLERSTACK/** * The handler stack * * @var HandlerStack */ protected $handlers; public function isHandling(int $level): bool return $this­>handlers­>isHandling($level);

Page 13: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

LOGGER/** * Processors that will process all log records * * To process records of a single handler instead, add the processor on that specific handler * @var callable[] */ protected $processors;

public function pushProcessor(callable $callback): self array_unshift($this­>processors, $callback);

return $this; ...

foreach ($this­>processors as $processor) $record = call_user_func($processor, $record);

Page 14: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

PROCESSORSTACK/** * The procesors stack stack * * To process records of a single handler instead, add the processor on that specific handler * @var ProcessorStack */ protected $processors;

public function pushProcessor(callable $callback): self $this­>processors­>push($callback);

return $this;

...

$record = $this­>processors­>process($record);

Page 15: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

PROCESSABLEHANDLERTRAIT/** * Processors that will process all log records * * To process records of a single handler instead, add the processor on that specific handler * @var callable[] */ protected $processors;

public function pushProcessor(callable $callback): self array_unshift($this­>processors, $callback);

return $this;

protected function processRecord(array $record) foreach ($this­>processors as $processor) $record = $processor($record); return $record;

Page 16: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

DRY-30 LOC

-4 теста

Page 17: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

FIGURE 8 KNOT

Page 18: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

#3ОБЕРНИТЕ ВСЕ

ПРИМИТИВНЫЕ ТИПЫпредсказуемостьинкапсуляция операцийесли у объекта есть поведениеадаптировано для PHP

Page 19: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

LOG LEVELpublic function addRecord(int $level, string $message, array $context = array()): bool

public function isHandling(array $record): bool return $record['level'] >= $this­>level;

Page 20: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

LOG LEVELpublic static function toMonologLevel($level): int if (is_string($level)) if (defined(__CLASS__.'::'.strtoupper($level))) return constant(__CLASS__.'::'.strtoupper($level)); throw new InvalidArgumentException('Level "'.$level.'" is not defined, use one of: '.implode( return $level;

/** * This is a static variable and not a constant to serve as an extension point for custom levels * * @var string[] $levels Logging levels with the levels as key */ protected static $levels = [ self::DEBUG => 'DEBUG', self::INFO => 'INFO',.....

Page 21: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

LOG LEVELclass LogLevel implements LogLevelInterface .... public function __toString() $name = array_search($this­>level, self::$levels); if ($name !== false)

return strtoupper($name);

return 'undefined'; .... /** * @inheritdocs */ public function includes($level): bool if ($level instanceof LogLevel) return $level­>getLevel() >= $this­>level;

return $level >= $this­>level;

Page 22: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

BOWLINE

Page 23: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

#2НЕ ИСПОЛЬЗУЙТЕ ELSE

читаемостьпредсказуемостьearly returnзащитное программирование

Page 24: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

WEBPROCESSOR public function __construct($serverData = null) if (null === $serverData) $this­>serverData =& $_SERVER; elseif (is_array($serverData)) $this­>serverData = $serverData; else throw new \UnexpectedValueException('$serverData ....');

Page 25: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

WEBPROCESSOR public function __construct($serverData = null) if (null === $serverData) $serverData =& $_SERVER; if (!is_array($serverData)) throw new \UnexpectedValueException('$serverData ....'); $this­>serverData =& $serverData;

Page 26: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

CLOVE HITCH

Page 27: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

#1ТОЛЬКО ОДИН УРОВЕНЬОТСТУПА В МЕТОДЕ

без примера

цикломатическая сложностьодин уровень абстракции

Page 28: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

SQUARE LASHING

Page 29: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

#8НИКАКИХ КЛАССОВ С

БОЛЕЕ ЧЕМ 2АТРИБУТАМИэто не шутка, а упражнениевысокая связностьинкапсуляция

Page 30: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь
Page 31: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

ПОЧЕМУ ДВА?обслуживают состояние одного атрибутакоординируют две отдельные переменные

Page 32: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

LOGGER /** * @var bool */ protected $microsecondTimestamps = false;

/** * @var DateTimeZone */ protected $timezone;

if ($this­>microsecondTimestamps) $ts = \DateTime::createFromFormat('U.u', sprintf('%.6F', microtime(true)), $this­>timezone); else $ts = new \DateTime('', $this­>timezone);

$ts­>setTimezone($this­>timezone);

Page 33: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

DATETIMEPROCESSOR class DateTimeProcessor /** * @var bool */ protected $microsecondTimestamps = false;

/** * @var DateTimeZone */ protected $timezone;

public function __construct(DateTimeZone $timezone = null) ...

Page 34: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

LOGGER protected $dateTimeProcessor;

public function __construct(string $name, array $handlers = array(), array $processors = parent::__construct($name, $handlers, $processors);

// BC compatible date time $this­>dateTimeProcessor = new DateTimeProcessor($timezone); $this­>pushProcessor($this­>dateTimeProcessor);

Page 35: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

FILTERHANDLER­class FilterHandler extends Handler implements ProcessableHandlerInterface

use ProcessableHandlerTrait;

/** * Whether the messages that are handled can bubble up the stack or not * * @var Boolean */ protected $bubble;

public function handle(array $record): bool ...

if ($this­>processors) $record = $this­>processRecord($record);

...

return false === $this­>bubble;

Page 36: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

GENERALHANDLERabstract class GeneralHandler extends Handler /* * * @var ProcessorStack */ protected $processors;

protected $bubble = true;

public function handle(array $record): bool if (!$this­>isHandling($record)) return false; $record = $this­>processors­>process($record); $this­>postProcess($record); return false === $this­>bubble;

Page 37: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

FILTERHANDLER class FilterHandler extends GeneralHandler

class GroupHandler extends GeneralHandler

class AbstractProcessingHandler extends GeneralHandler

class SamplingHandler extends GeneralHandler

Page 38: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

ХОРОШЕЕ ПРАВИЛОи результаты интересные

bubble - один раз-20 LOC-ProcessableHandlerTrait

Page 39: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

TRIPOD LASHING

Page 40: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

#7СОХРАНЯЙТЕ СУЩНОСТИ

КОРОТКИМИ200 сток в файле (включая docblock)10 методов в классе15 классов в пространстве имен

Page 41: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

LOGGER И NEWLOGGERДо Logger - 442 строк кодаПосле Logger - 275 строк кодаПосле NewLogger - 106 строк кода

Page 42: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

TRUCKERS HITCH

Page 43: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

#9ГЕТТЕРЫ, СЕТТЕРЫ И

СВОЙСТАуказывай, а не спрашивайпринцип открытости/закрытости

Page 44: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

ROPE SWING

Page 45: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

#5ОДНА ТОЧКА НА

СТРОКУ* *** ДВА -> НА СТРОКУ В PHP

** КРОМЕ ТЕКУЧИХ ИНТЕРФЕЙСОВ

тестируемость (mock-объекты)закон Деметры

Page 46: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

#6НЕ ИСПОЛЬЗУЙТЕСОКРАЩЕНИЯ

слишком длинное название метода?слишком часто приходиться вызыватьметод?читаемость

Page 47: WebCamp 2016: PHP.Денис Потапов.Рефакторим код не задумываясь

ВОПРОСЫ?bit.ly/webcamp16

bit.ly/webcamp16code

denyspotapov.com