Недавно на Хабре появилась любопытная заметка Как два программиста хлеб пекли . В ней автор противопоставляет подход «сделай все с кучей классов и паттернами проектирования» подходу «сделай как можно проще (KISS)». А вот интересно, что будет, если попытаться решить описанную в статье задачу с помощью функционального подхода?

Итак, сидит себе такой программист на Haskell . Тут к нему подходит менеджер и заявляет, дескать, нам нужно научиться печь хлеб. В общем, ничего толком не ясно, но раз надо, так надо:

data Bread = Bread

createBread = Bread

Позже становится известно, что хлеб, оказывается, должен печься в печке:

data Oven = Oven
data Bread = Bread

createBread :: Oven -> Bread
createBread _ = Bread

Как видите, пока это мало на что влияет. Как и то, что печи бывают разных видов:

data Oven = ElectricOven | GasOven | MicrowaveOven
data Bread = Bread

createBread :: Oven -> Bread
createBread _ = Bread

Первое действительно более-менее интересное условие состоит в том, что газовая печь не может печь без газа:

data GasStatus = GasAvailable | GasUnavailable
data Oven = ElectricOven | GasOven | MicrowaveOven
data Bread = Bread

breadCouldBeCreated GasOven GasUnavailable = False
breadCouldBeCreated _ _ = True

createBread oven gas
| breadCouldBeCreated oven gas = Just Bread
| otherwise = Nothing

Здесь вводится тип «состояние газа». Газ — он либо есть, либо его нет. Если мы используем газовые баллоны, можно хранить количество доступного газа в литрах, суть от этого не меняется. Функция breadCouldBeCreated проверяет, можем ли мы что-нибудь приготовить при текущих обстоятельствах (наличие газа и тип печи).

Позже становится известно, что помимо хлеба в печи также можно готовить торты и пирожки с различной начинкой:

data Stuffing = Meat | Cabbage
data Food = Cake | Bread | Pasty Stuffing
data GasStatus = GasAvailable | GasUnavailable
data Oven = ElectricOven | GasOven | MicrowaveOven

ovenCouldBeUsed GasOven GasUnavailable = False
ovenCouldBeUsed _ _ = True

create food oven gas
| ovenCouldBeUsed oven gas = Just food
| otherwise = Nothing

Вводим типы «еда» и «начинка». Функцию breadCouldBeCreated переименовываем в ovenCouldBeUsed.

Теперь менеджер хочет, чтобы торты, пирожки и хлеб пеклись не просто так, а по разным рецептам. Сказано — сделано:

data Stuffing = Meat | Cabbage
data Food = Cake | Bread | Pasty Stuffing
data GasStatus = GasAvailable | GasUnavailable
data Oven = ElectricOven | GasOven | MicrowaveOven

ovenCouldBeUsed GasOven GasUnavailable = False
ovenCouldBeUsed _ _ = True

create food oven gas
| ovenCouldBeUsed oven gas = Just food
| otherwise = Nothing

breadRecipe = create Bread
cakeRecipe = create Cake
pastyRecipe stuffing = create $ Pasty stuffing

Рецепт обычно предписывает выполнение каких-то действий с ингредиентами, печью и так далее. Очевидно, что рецепт представляет собой функцию высшего порядка.

Наконец, в печах требуется обжигать кирпичи. Судя по формулировке, обжигать кирпичи следует в тех же самых печах, включая микроволновую (хотя в статье на Хабре вводится отдельный класс Furnace):

data Stuffing = Meat | Cabbage
data Food = Cake | Bread | Pasty Stuffing
data GasStatus = GasAvailable | GasUnavailable
data Oven = ElectricOven | GasOven | MicrowaveOven
data Brick = Brick

ovenCouldBeUsed GasOven GasUnavailable = False
ovenCouldBeUsed _ _ = True

create food oven gas
| ovenCouldBeUsed oven gas = Just food
| otherwise = Nothing

breadRecipe = create Bread
cakeRecipe = create Cake
pastyRecipe stuffing = create $ Pasty stuffing

makeBrick oven gas
| ovenCouldBeUsed oven gas = Just Brick
| otherwise = Nothing

Здесь мы просто добавили тип «кирпич» и функцию «сделать кирпич».

Выводы напрашиваются сами собой. Мы получаем простой для понимания и легкий в сопровождении код. В процессе его написания мы легко и непринужденно строим модель предметной области, фактически просто делая перевод с русского на Haskell. Безо всяких там наследований, рефакторингов и UML.

Возможно, в действительности имелось в виду, что каждая печь производит хлеба и торты несколько иначе, и нам понадобится ввести дополнительный класс типов (или, если хотите, «интерфейс») с соответствующими экземплярами классов. Заодно нам не придется вручную кодировать вызов ovenCouldBeUsed всюду, где используется печь. Но не похоже, что решение существенно усложнится от всего этого.

А как вы печете хлеб?

admin

Share
Published by
admin

Recent Posts

Что такое Zulip

Zulip — программное обеспечение для реализации корпоративного чата. Разработан в 2012 году, в 2014 был…

4 дня ago

Что такое Zookeeper

Zookeeper — cервис-координатор, который позволяет обеспечить контроль синхронизации данных. Разработан на Java компанией Apache Software…

4 дня ago

Что такое Zimbra

Zimbra — программное обеспечение для реализации почтового сервиса или, если сказать точнее, автоматизации совместной деятельности…

4 дня ago

Что такое Zabbix

Zabbix — бесплатная система мониторинга. Позволяет отслеживать состояние сетевых узлов, компьютеров и серверов. Возможности: Поддержка…

4 дня ago

Что такое YouTube

YouTube — компания-владелец одноименного портала для просмотра и хранения видео. Чтобы пользоваться данным порталом достаточно…

4 дня ago

Что такое yota

Yota — провайдер, предоставляющий доступ к сети Интернет по беспроводной связи. Впервые, сервис начал работать…

4 дня ago