В стиле функционального программирования (FP) мы часто разрабатываем наши функции, чтобы нам не нужно было синхронизировать эти ссылки.
Рассмотрим этот сценарий: вы создаете стек s
, вставляете его в объект Client
, затем перемещаете элемент в s
и получаете новый стек s2
:
s = new Stack()
client = new Client(s)
s2 = s.push(...)
Поскольку s
и s2
не синхронизированы (т. Е. Они представляют собой разные стеки), внутри объекта client
он по-прежнему видит старую версию стека (s
), которую вы не используете. не хочу Вот код Client
:
class Client {
private Stack stack;
// other properties
public Client(Stack stack) { this.stack = stack; }
public SomeType foo(/*some parameters*/) {
// access this.stack
}
}
Чтобы решить эту проблему, функциональный подход не использует такую неявную ссылку, а вместо этого передает ссылку в функцию в качестве явного параметра:
class Client {
// some properties
public SomeType foo(Stack stack, /*some parameters*/) {
// access stack
}
}
Конечно, иногда это было бы больно, поскольку функция теперь имеет один дополнительный параметр. Каждый вызывающий Client
должен поддерживать стек для вызова функции foo
. Вот почему в FP вы склонны видеть функции с большим количеством параметров, чем в ООП.
Но у FP есть концепция, которая может смягчить эту боль: так называемое частичное применение . Если у вас уже есть стек s
, вы можете написать client.foo(s)
, чтобы получить «обновленную» версию foo
, для которой не требуется стек, а требуется только другой some parameters
. Затем вы можете передать эту обновленную функцию foo
получателю, который не поддерживает ни одного стека.
Тем не менее, стоит упомянуть, что есть люди, которые уверяют, что эта боль может быть действительно полезной. Например, Скотт Влашин в своей статье Функциональные подходы к внедрению зависимости :
Недостатком, конечно, является то, что теперь есть пять дополнительных параметров для функции, что выглядит болезненно. (Конечно, эквивалентный метод в версии OO также имел эти пять зависимостей, но они были неявными).
На мой взгляд, эта боль действительно полезна! С интерфейсами в стиле OO у них есть естественная тенденция со временем расти. Но с такими явными параметрами есть естественное препятствие иметь слишком много зависимостей! Потребность в руководящих принципах, таких как принцип разделения интерфейсов, значительно уменьшилась.
Также у Марка Симанна - автора книги Внедрение зависимостей - была интересная серия по Отклонение зависимостей .
Если вы не можете испытать эту боль, просто сломайте CQS и вернитесь к традиционной реализации стека. В конце концов, если функция (например, pop
/ dequeue
) хорошо известна и знает, что она одновременно что-то возвращает и изменяет свои внутренние данные, нарушение CQS не так уж и плохо.
Даже в этом случае некоторые языки FP предлагают механизм передачи сообщений, позволяющий реализовать изменяемый стек таким образом, чтобы вы не писали данные, изменяющие код (например, код, использующий символ назначения). MailboxProcessor в F # - такой механизм.
Надеюсь, это поможет:)