Факт поддержки языками ОО надежного и удобного механизма полиморфизма означает, что
Теперь вернемся к дереву вызовов, изображенному на рис. 5.1, и к множеству зависимостей в его исходном коде. Любую из зависимостей в этом исходном коде можно обратить, добавив интерфейс.
При таком подходе архитекторы, работающие в системах, которые написаны на объектно-ориентированных языках, получают
Какая возможность! И эту возможность открывает ОО. Собственно, это все, что дает ОО, – по крайней мере с точки зрения архитектора.
Что можно сделать, обладая этой возможностью? Можно, например, переупорядочить зависимости в исходном коде так, что база данных и пользовательский интерфейс (ПИ) в вашей системе будут зависеть от бизнес-правил (рис. 5.3), а не наоборот.
Рис. 5.3. База данных и пользовательский интерфейс зависят от бизнес-правил
Это означает, что ПИ и база данных могут быть плагинами к бизнес-правилам. То есть в исходном коде с реализацией бизнес-правил могут отсутствовать любые ссылки на ПИ или базу данных.
Как следствие, бизнес-правила, ПИ и базу данных можно скомпилировать в три разных компонента или единицы развертывания (например, jar-файлы, библиотеки DLL или файлы Gem), имеющих те же зависимости, как в исходном коде. Компонент с бизнес-правилами не будет зависеть от компонентов, реализующих ПИ и базу данных.
Как результат, появляется возможность
Проще говоря, когда реализация компонента изменится, достаточно повторно развернуть только этот компонент. Это
Если система состоит из модулей, которые можно развертывать независимо, их можно разрабатывать независимо, разными командами. Это
Заключение
Что такое ОО? Существует много взглядов и ответов на этот вопрос. Однако для программного архитектора ответ очевиден: ОО дает, посредством поддержки полиморфизма, абсолютный контроль над всеми зависимостями в исходном коде. Это позволяет архитектору создать архитектуру со сменными модулями (плагинами), в которой модули верхнего уровня не зависят от модулей нижнего уровня. Низкоуровневые детали не выходят за рамки модулей плагинов, которые можно развертывать и разрабатывать независимо от модулей верхнего уровня.
6. Функциональное программирование
Многие идеи функционального программирования появились задолго до появления самого программирования. Эта парадигма в значительной мере основана на -исчислении, изобретенном Алонзо Чёрчем в 1930-х годах.
Квадраты целых чисел
Суть функционального программирования проще объяснить на примерах. С этой целью исследуем решение простой задачи: вывод квадратов первых 25 целых чисел (то есть от 0 до 24).
В языках, подобных Java, эту задачу можно решить так:
public class Squint {
public static void main(String args[]) {
for (int i=0; i<25; i++)
System.out.println(i*i);
}
}
В Clojure, функциональном языке и производном от языка Lisp, аналогичную программу можно записать так:
(println (take 25 (map (fn [x] (* x x)) (range))))
Этот код может показаться немного странным, если вы не знакомы с Lisp. Поэтому я, с вашего позволения, немного переоформлю его и добавлю несколько комментариев.
(println;___________________ Вывести
(take 25;_________________ первые 25
(map (fn [x] (* x x));__ квадратов
(range))));___________ целых чисел
Совершенно понятно, что println
, take
, map
и range
– это функции. В языке Lisp вызов функции производится помещением ее имени в круглые скобки. Например, (range)
– это вызов функции range.
Выражение (fn [x] (* x x))
– это анонимная функция, которая, в свою очередь, вызывает функцию умножения и передает ей две копии входного аргумента. Иными словами, она вычисляет квадрат заданного числа.
Взглянем на эту программу еще раз, начав с самого внутреннего вызова функции: