Каковы за и против закрытий против классов, и наоборот?
Править: Как пользователь Faisal выразился, и закрытия и классы могут использоваться для "описания объекта, который поддерживает и управляет состоянием", таким образом, закрытия позволяют программировать в объектно-ориентированном способе использовать функциональные языки. Как большинство программистов, я более знаком с классами.
Намерение этого вопроса не состоит в том, чтобы открыть другую войну пламени, о которой парадигма программирования лучше, или если закрытия и классы являются полностью эквивалентным, или бедным человеком друг друга.
То, что я хотел бы знать, - то, если кто-либо нашел сценарий, в котором подход действительно бьет другой, и почему.
Функционально замыкания и объекты эквивалентны. Замыкание может имитировать объект и наоборот. Итак, какой из них вы используете, зависит от синтаксического удобства, или какой из них лучше всего подходит для вашего языка программирования.
В C ++ замыкания синтаксически недоступны, поэтому вы вынуждены использовать «функторы», которые представляют собой объекты, которые переопределяют operator ()
и могут быть вызваны способом, похожим на вызов функции.
В Java у вас даже нет функторов, поэтому вы получаете такие вещи, как шаблон Visitor, который был бы просто функцией более высокого порядка на языке, поддерживающем замыкания.
В стандартной схеме у вас нет объектов, поэтому иногда вы завершаете их реализацию, записывая замыкание с функцией отправки, выполняя различные подзакрытия в зависимости от входящих параметров.
В таком языке, как Python, синтаксис которого имеет как функторы, так и замыкания, это, в основном, дело вкуса, и вы считаете, что это лучший способ выразить то, что вы делаете.
Лично я бы сказал, что на любом языке, имеющем синтаксис для обоих, замыкания - это гораздо более ясный и чистый способ выразить объекты с помощью одного метода. И наоборот, если ваше закрытие начинает обрабатывать отправку частичным замыканиям на основе входящих параметров, вам, вероятно, следует вместо этого использовать объект.
Закрытия очень слабо связаны с классами. Классы позволяют определять поля и методы, а замыкания хранят информацию о локальных переменных, полученных при вызове функции. Невозможно сравнить эти два понятия в языковом отношении: они не служат одной и той же цели. Кроме того, замыкания гораздо больше связаны с функциональным программированием, чем с объектно-ориентированным.
Например, посмотрите на следующий код C#:
static void Main(String[] args)
{
int i = 4;
var myDelegate = delegate()
{
i = 5;
}
Console.WriteLine(i);
myDelegate();
Console.WriteLine(i);
}
Это дает "4", затем "5". myDelegate
, будучи делегатом, является закрытием и знает обо всех переменных, используемых в данный момент функцией. Поэтому, когда я вызываю его, ему разрешается изменить значение i
inside "родительской" функции. Для обычной функции это было бы недопустимо.
Классы, если вы знаете, что это такое, - это совершенно другое.
Возможная причина вашей путаницы в том, что когда в языке нет поддержки замыканий, их можно имитировать с помощью классов, которые будут хранить все переменные, которые нам нужны. Например, мы можем переписать приведенный выше код следующим образом:
class MainClosure()
{
public int i;
void Apply()
{
i = 5;
}
}
static void Main(String[] args)
{
MainClosure closure;
closure.i = 4;
Console.WriteLine(closure.i);
closure.Apply();
Console.WriteLine(closure.i);
}
Мы преобразовали делегат
в класс, который мы назвали MainClosure
. Вместо того чтобы создавать переменную i
внутри функции Main
, мы создали объект MainClosure
, который имеет поле i
. Именно его мы и будем использовать. Кроме того, мы встроили код, выполняемый функцией, внутрь метода экземпляра, а не внутрь метода.
Как видите, несмотря на то, что это был простой пример (только одна переменная), работы в нем значительно больше. В контексте, где вам нужны закрытия, использование объектов - плохое решение. Однако классы полезны не только для создания замыканий, и их обычное назначение обычно совсем другое.
Лично я думаю, что это вопрос использования правильного инструмента для работы... а точнее, правильной передачи ваших намерений.
Если вы хотите явно показать, что все ваши объекты имеют общее определение, и хотите обеспечить строгую проверку типов, вам, вероятно, следует использовать класс. Недостаток невозможности изменить структуру класса во время выполнения на самом деле является преимуществом в этом случае, поскольку вы точно знаете, с чем имеете дело.
Если вместо этого вы хотите создать разнородную коллекцию "объектов" (т.е. состояние, представленное в виде переменных, закрытых под некоторой функцией/внутренними функциями для манипулирования этими данными), вам лучше создать закрытие. В этом случае нет никаких реальных гарантий относительно структуры объекта, который вы получите в итоге, но вы получаете всю гибкость определения его именно так, как вам нужно во время выполнения.
Спасибо за вопрос, на самом деле; сначала я ответил что-то вроде "классы и закрытия - это совершенно разные вещи!", но после некоторых исследований я понял, что проблема не так однозначна, как я думал.