Я начинаю каждый день с чашки кофе.

Процесс тот же — я выбираю кружку, наполняю машину Keurig водой, ставлю внутрь k-cup и нажимаю кнопку запуска. Я нажимаю кнопку «Пуск», будучи полностью уверенным, что через несколько минут у меня будет дымящаяся чашка кофе, потому что это именно то, что делает машина Keurig.

Дело в том, что я понятия не имею, как моя машина Keurig делает мне чашку кофе. Конечно, у меня есть предположения, связанные с работой в кофейне с более крупными пивоварнями. Я могу предположить, что где-то в процессе машина Keurig нагревает воду, которую я ей подаю, пропуская эту воду через k-cup и сливая ее в мою кружку. Однако, когда я нажимаю эту кнопку запуска, я все еще не совсем понимаю, как работает моя машина Keurig. я не знаю его реализации; Я знаю что он делает, а не как он это делает. Другими словами, машина Keurig определила интерфейс, который позволяет мне получить чашку кофе, не зная, как это делается, удовлетворяя мою зависимость от кофеина.

От модулей к системам и интерфейсам

Прежде чем идти дальше, давайте определим несколько терминов:

Сама система слов достаточно расплывчата и может указывать на множество элементов, различающихся как по существу, так и по сложности. Система может относиться к чему-то столь же сложному, как нервная система, действующая как сеть нейронов и клеток, которые передают сообщения в мозг и из него, а также к чему-то столь же простому, как многоразовая бутылка для воды, позволяющая пользователям легко потреблять жидкости. Объединение этих системных объектов состоит в том, что они оба состоят из единиц, работающих вместе для достижения одной цели. В нервной системе единственной целью является передача сигналов к различным частям тела и от них. От центральной нервной системы до височной доли каждая единица имеет отдельную, определенную ответственность и работает вместе с другими единицами для достижения ключевой цели нервной системы, в то время как черепные и спинномозговые нервы служат интерфейсом для центральной нервной системы. нервной системы от периферической нервной системы. Точно так же закручивающаяся крышка и рифленый край многоразовой бутылки для воды выступают в качестве единиц, образующих единое целое и служащих системой, ключевой задачей которой является предоставление человеку возможности пить из нее.

В программном обеспечении та же самая идея. Приложения, независимо от исходной платформы, строятся на нескольких модулях, функционирующих как единая система. Сами эти единицы состоят из внутренних единиц, о которых внешний мир не знает, вложенных до самых нижних уровней. Модули также делают предположения о других модулях, использующих связующий объект между ними, а именно интерфейс. Когда я говорю интерфейс, я имею в виду не просто ключевое слово interface, которое так часто используется в таких языках, как TypeScript или Java. Нет, я имею в виду нечто более общее — я имею в виду точку, в которой системам (возможно, несвязанным) предоставляется возможность взаимодействовать друг с другом, часто обозначаемая как публичный или внешний интерфейс. Этот интерфейс, или контракт, полностью определяет взаимодействие между единицами, составляющими систему. Эти интерфейсы должны ограничивать как можно больше частных знаний о реализации, которые модуль поддерживает независимо, чтобы предоставить самый простой и понятный уровень функциональности потребителю его ресурса. Потребляющая единица не должна знать, как что-то делается, но она должна знать, что ей нужно достичь и куда нужно идти. По словам Санди Мец, это смещает вопрос с «Я знаю, что мне нужно, и я знаю, как вы это делаете» на «Я знаю, что мне нужно, и я верю, что вы сделаете свою часть». Создавая четко определенные общедоступные интерфейсы между модулями, код становится легче читать и анализировать. Рассмотрим следующие примеры систем и их соответствующих модулей и интерфейсов:

Кофе для кода

Наличие хорошо определенных интерфейсов имеет первостепенное значение, будь то аппаратное или программное обеспечение. Значение остается постоянным. С точки зрения аппаратного обеспечения было бы не очень удобно, если бы для каждого действия, направленного на достижение главной цели — приготовления чашки кофе, были бы отдельные кнопки. Что делать, если пользователь забыл нажать одну из кнопок? Что делать, если кнопка была нажата в неправильном порядке? Почему нагрузка на пользователя должна быть такой большой, если механические обязанности должны быть делегированы самой машине Keurig? Гораздо логичнее иметь один понятный интерфейс, доступный для пользователя, при этом выполняя основную задачу системы. Скотт Мейерс пришел к выводу, что наиболее важным общим принципом проектирования интерфейса является следующее:

«Сделайте интерфейсы простыми для правильного использования и трудными для неправильного использования», — Скотт Мейерс.

Наличие четко определенных интерфейсов решает эту проблему напрямую и предполагает наличие абстракции как сложности, так и функциональности. Возможность абстрагироваться от деталей того, как что-то работает, значительно упрощает использование имеющейся системы, делая работу пользователя намного проще и прямолинейнее. Это то, что объединяет все высококачественные интерфейсы и должно быть принято, чтобы считаться таковым. Наличие четко определенных интерфейсов и модулей, содержащих четкие и четкие обязанности, упрощает код, чтобы быть правильным с точки зрения как системных целей, так и оставаться на максимально возможном уровне в отношении возможности изменять или добавлять код в систему в будущем. . Это создает гибкие и эффективные взаимодействия между модулями, что приводит к созданию надежных и высокофункциональных систем.

Давайте посмотрим, как система, состоящая из блоков с разными обязанностями, приводит к более четкому интерфейсу между блоками и приводит к тому, что интерфейс системы становится более простым в использовании для пользователя.

Рассмотрим приведенный ниже код (конечно, написанный с помощью псевдокода).

Здесь у нас есть две системы: BadKeurig и GoodKeurig.

BadKeurig, как следует из названия, представляет собой плохо спроектированный класс (или систему), который предоставляет общедоступный интерфейс, который заставляет потребителя класса знать и понимать реализацию того, как работает машина Keurig. Это требует более глубокого знания порядка, в котором должны вызываться функции (или модули), и приводит к большому и сложному общедоступному интерфейсу. Чтобы получить чашку Джо, пользователь должен вызвать функцию lockLid(), а затем последовательно выполнить функциюheatWater(), и этот список можно продолжить. Хотя у этих модулей могут быть разные обязанности, интерфейс, предоставляемый пользователю, сложен и может быть значительно улучшен.

Однако надежда остается. В GoodKeurig публичный интерфейс разработан четко и значительно упрощает процесс для потребителя класса. Чтобы приготовить чашку кофе, пользователю достаточно знать, что в классе GoodKerig существует функция start(). Единицы или функции системы GoodKeurig имеют четко определенные обязанности, каждая из которых существует независимо. Пользователю не нужно знать, как работает машина Keurig. Это связано с тем, что система GoodKeurig ориентирована на предоставление четко определенного общедоступного интерфейса, в результате чего код легче читать, анализировать и изменять в будущем.

Последние мысли

Если мы стремимся писать высококачественный код, мы должны понимать, что каждая система, независимо от того, состоит она из кода или нет, состоит из отдельных отдельных блоков с соответствующими обязанностями. Мы должны проектировать программные системы таким образом, чтобы они отражали ключевую цель единого целого подразделений. Поскольку контракты и интерфейсы служат списком требований, которым должна соответствовать реализация, результирующий интерфейс должен отражать намерение предоставить пользователю возможность четко знать, что делает система. Система должна выполнять это на тщательно разработанном более высоком уровне, а не случайно обременять пользователя, требуя знания вопросов, которые, кажется, лучше делегировать ответственности потребляемого модуля. Эта абстракция сложности приводит к более ясным, более сложным программным системам, которые легко адаптируются к изменениям в будущем — изменениям, которые неизбежно произойдут.

В следующий раз, когда вы будете использовать что-то столь же простое, как кофеварка Keurig, обратите внимание на сложные детали, которые включает в себя каждый блок системы, и на простой интерфейс, который был предоставлен вам, наслаждаясь возможностями, возможными для предоставления пользователям тех же механизмов в дизайне. ваших программных систем (или многоразовых бутылок с водой, конечно). Возможно, вы только начинаете осознавать и ценить абстрактную сложность всего, что вас окружает — блоков, интерфейсов и систем во всем.

Источники

(По полезным советам Энтони Фельдхейка и Захарии Швермана)

https://www.merriam-webster.com/dictionary/system

https://www.merriam-webster.com/dictionary/unit

https://www.merriam-webster.com/dictionary/interface

https://cseweb.ucsd.edu/~wgg/CSE218/Parnas-IFIP71-information-distribution.PDF

https://www.researchgate.net/publication/221553406_A_procedure_for_designing_abstract_interfaces_for_device_interface_modules

https://en.wikipedia.org/wiki/Предварительное условие

https://en.wikipedia.org/wiki/Постусловие

https://en.wikipedia.org/wiki/Design_by_contract

https://web.archive.org/web/20100911095014/http://blogs.msdn.com/b/kcwalina/archive/2004/10/24/246947.aspx

https://www.tutorialspoint.com/typescript/typescript_interfaces.htm

https://www.poodr.com/

https://www.aristeia.com/Papers/IEEE_Software_JulAug_2004_revised.htm