mirror of
https://github.com/twirl/The-API-Book.git
synced 2025-01-23 17:53:04 +02:00
Декомпозиция интерфейсов
This commit is contained in:
parent
645b3f77f9
commit
dceb85577e
37
API.ru.html
37
API.ru.html
@ -307,6 +307,43 @@
|
||||
|
||||
<p>Ничего удивительного в этом, конечно же, нет — поскольку api в целом связывает разные контексты, в нём всегда будут объекты, объединяющие термины разных предметных областей. Наша задача - декомпозировать объекты так, чтобы, с одной стороны, разработчикам было удобно и понятно пользоваться нашей иерархией абстракций, а нам, с другой стороны, было удобно такую архитектуру поддерживать.</p>
|
||||
|
||||
<h3>Декомпозиция интерфейсов</h3>
|
||||
|
||||
<p>Если каждый объект представляет собой объединение разнородной ответственности в терминах разных предметных областей, каким образом мы можем добиться эффективного уменьшения связанности объектов между собой? Давайте подумаем, где мы можем "сэкономить" на связях.</p>
|
||||
|
||||
<p>Возьмём, например, объект Map. Во взаимодействии с объектом source он выступает как чистый источник географических координат, вся прочая ответственность объекта карты source не касается.</p>
|
||||
|
||||
<p>Напротив, оверлею географические координаты ни к чему: он существует в некотором графическом контексте, где оперируют пикселями. </p>
|
||||
|
||||
<p>Раз смежным объектам знание о полной функциональности карты не нужно, именно здесь мы и можем убрать лишние связи: потребуем, чтобы карта при взаимодействии с источником данных выступала только как картографический контекст, а при взаимодействии с оверлеем - как чисто графический контекст. Для организации такой абстракции используются интерфейсы.</p>
|
||||
|
||||
<p>Определим два интерфейса:</p>
|
||||
|
||||
<ul>
|
||||
<li>IGeoContext - предоставляет методы работы с областью картографирования;</li>
|
||||
<li>IGraphicalContext - предоставляет контекст рендеринга.</li>
|
||||
</ul>
|
||||
|
||||
<p>Объект Map в этом случае реализует оба интерфейса, однако связанные сущности работают уже не с конкретным объектом Map, а с некоторой реализацией абстрактного интерфейса, ничего не зная о прочих свойствах этого объекта.</p>
|
||||
|
||||
<p>NB. Во многих языках программирования нет поддержки интерфейсов, абстрактных классов и/или множественного наследования. Однако выделять интерфейсы нам это не мешает, поскольку мы всегда можем "договориться", что объект source имеет право пользоваться только вот этим набором свойств и методов. Конечно, контролировать соблюдение этой договоренности в достаточно развесистом api довольно сложно, но, поверьте автору, вполне возможно, тем более, что тестами и/или статическим анализом кода соблюдение договоренностей об интерфейсах можно проверить почти всегда.</p>
|
||||
|
||||
<p>Разделение контекстов - не единственная причина, по которой выделение интерфейсов критически важно при проектировании api. Предъявление к входящим параметрам требования только удовлетворять интерфейсу существенно упрощает создание альтернативных реализаций ваших объектов, в том числе в целях тестирования. Теперь чтобы протестировать объект source достаточно написать mock на IGeoContext, а не весь класс map целиком. Аналогично, если мы захотим использовать наши оверлеи для показа их, скажем, в качестве какой-то инфографики или на абстрактном плане местности, нам не придётся переделывать для этого класс Map - достаточно будет альтернативной реализации IGraphicalContext.</p>
|
||||
|
||||
<p>При выделении интерфейсов важно также понимать, что интерфейс, в отличие от его реализации, должен быть минимально достаточным и не должен включать в себя вспомогательные методы. Например, если класс map имеет как метод для получения всей области картографирования в виде четырехугольника getBBox, так и методы получения каждого из углов по отдельности - getLeftBottom, getRightTop, например, — то интерфейс IGeoContext должен содержать что-то одно. Нет никакого смысла загромождать интерфейс альтернативными реализациями одной и той же функциональности — это затрудняет чтение и усложняет написание собственных реализаций. Если только нет каких-то показаний с точки зрения производительности, следует отдать предпочтение максимально общему методу - в нашем случае getBBox.</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user