Вообще-то я программирую в 1С, на языке, похожем на Visual Basic. Но мне кажется, что затронутый в этой теме вопрос касается всех языков программирования, т.к. затрагивает проблему, с которой рано или поздно сталкивается программист при работе со сложным кодом.
Суть ситуации заключается в том, что есть некая процедура, которая делает расчет или выполняет какие-то действия. Возникшие изменения требуют, чтобы в некоторых случаях этот расчет или действия отличались в зависимости от того, из какого контекста вызывается данная процедура.
Приведу пример, где после изменений требуется, чтобы выводилась не сумма y и z, а эта сумма, умноженная на некоторый коэффициент c, который вводится на верхнем уровне, в процедуре Top.
Исходный код:
sub Top()
Middle
end sub
sub Middle()
y = get1
z = get2
Down y, z
end sub
sub Down(y, z)
echo y+z
end sub
Проблему можно решить передачей дополнительного параметра c, но если цепочка процедур длинная, это вызовет слишком обширные изменения во многих промежуточных процедурах:
sub Top()
Input c
Middle c
end sub
sub Middle(c)
y = get1
z = get2
Down y, z, c
end sub
sub Down(y, z, c)
echo (y+z)*c
end sub
Неплохо, если автор процедуры сделал переменную-структуру k для хранения контекста вызова, куда можно добавлять произвольные поля, тогда задача решается просто:
sub Top()
Input c
k = strukt
k.add "c", c
Middle k
end sub
sub Middle(k)
y = get1
z = get2
Down k, y, z
end sub
sub Down(k, y, z)
echo (y+z)*c
end sub
Но, практика показывает, что такая забота и предусмотрительность встречаются не так часто.
Поэтому на практике я применяю другой подход:
sub Top()
Input c
global.add "c", c
Middle
end sub
sub Middle()
y = get1
z = get2
Down y, z
end sub
sub Down(y, z)
echo (y+z)*global.c
end sub
Здесь global - это некоторая глобальная структура, которая хранит глобальные данные текущего сеанса пользователя, у каждого пользователя своя. Конкретно в 1С - это параметры сеанса.
Такой подход требует внимательной проверки на реентерабельность, но в целом, довольно часто успешно решает проблему.
Непосредственно методику использовал в двух случаях:
В процедуре рассчитывалась зарплата исходя из учтенного для сотрудника времени. Но клиенту для расчета авансов понадобилось начислять зарплату не по фактическому, а по плановому времени. Вид времени определялся из вида документа - аванс или окончательная оплата. А после этого должен был как-то попадать в функцию расчета зарплаты. Тут и пригодилась методика.
В некоторых печатных формах нужно было изменить наименование.
В процедуре, где выводилось наименование не было сведений о том, из какого документа печатается наименование, а наименование должно было обрабатываться исходя из вида документа (внутренний или клиенту). Я использовал передачу параметров описанным выше методом.
Что скажете насчет способа? Использовали? Или что можно предложить взамен? А может быть некоторые языки поддерживают такую передачу контекста вызова "играючи"? Хотелось бы взглянуть.
Комментарии (90)
allter
25.06.2022 02:34+1И использовали, и боролись с последствиями активного использования этого способа. Разные люди выбирают совершенно разные объекты, где они считают допустимым временно похранить какое-то значение, которое они ленятся прокидывать нормально, поэтому при доработке подобного кода приходится анализировать чуть ли не всю кодовую базу.
На самом деле, современные языки стараются по возможности затруднить то, что вы описали с целью повышения понятности [чужого] кода. В частности, возможностью описывания неизменяемых переменных и структур. Вместо этого даются альтернативные области видимости объектов, развитые системы типов. Ну и возможности IDE по рефакторингу сейчас достаточно мощные.
И так или иначе, большинство языковых решений для решения обозначенной проблемы - это вариации на тему структуры-контекста (который вы назвали k), И обычно это используется для внедрения зависимостей (DI). Для передачи входных данных обычно используются аргументы функций/методов.
Вот, например, как страшноватенько подобный подход выглядит в Scala с использованием библиотек cats и tofu. Зато всё, что делает этот код понятно отражено в типах.
import cats.data.ReaderT import cats.syntax.all._ import cats.{Applicative, Id} import tofu.WithContext import tofu.optics.Extract import tofu.optics.macros.ClassyOptics object ContextsAbuse extends App { // Макроаннотация генерит линзы для выдирания членов структурыs @ClassyOptics // Собственно, описание структуры высокоуровнего контекста final case class Ctx( c: Int, foo: String, bar: Byte ) /* Пояснение, какой тип в итоге имеют F в middle и down по сути, это тип функции Ctx => A (т.е. f(ctx: Ctx): A) */ type RunF[A] = ReaderT[Id, Ctx, A] def top(c: Int) = { /* Получаем результат middle с погруженным в него результатом (зависящим от c, которое внутри middle ещё неизвестно. По сути, здесь получается функция Ctx => Int, с результатом частичного применения математической операции внутри down. */ val middleF = middle[RunF] // На верхнем уровне формируем глобальный контекст нужного вида val ctx = Ctx(c, "foo", 42) // По сути, просто доприменяем частично-применённую middleF middleF.run(ctx) } val get1 = 42 val get2 = 108 /* Здесь нет никакого знания того, что в контексте есть c: Int (но есть знание всей структуры контекста) при разработке в промежуточном коде, которого может быть много, править ничего не потребуется */ def middle[F[_]: Applicative: WithContext[*[_], Ctx]] = { val y = get1 val z = get2 down(y, z) } /* Страшненькая сигнатура говорит о том функция определена для любых типов-"эффектов" F, инкапсулирующих понятие контекста Ctx, для любых типов A (с "числовыми" операциями) и возможностью "выдрать" значение типа A из контекста Ctx */ def down[F[_]: Applicative: WithContext[*[_], Ctx], A: Numeric: Extract[Ctx, *]](y: A, z: A) = { val num = Numeric[A] import num._ /* Отображем выдираемое из контекста Int поле на результат операции, использующей это значение. Результирующее значение остаётся "обрамлено" в типе эффекта F Получено оно будет уже в top в .run */ WithContext[F, Ctx].extract(implicitly[Ctx Extract A]).context.map { c => (y + z) * c } } println(top(19)) }
fixin Автор
25.06.2022 08:42Извините, не осилил. Вы бы кратенько принцип изложили.
allter
25.06.2022 12:40Совсем кратко - это как в вашем примере со структурой k, только лениво - контекст (k) остается аргументом итоговой функции, складываемой в значение middleF в верхнем компоненте вычислений. Чуть более подробно - пояснено комментарием. Для ещё более подробного понимания, увы, придется изучить основы функционального программирования на Scala или Haskell
Выше в комментариях уже упоминали Scala. Но в той статье для передачи структуры контекста используется фича имплицитных параметров. Это совсем не то, что в моем примере.
fixin Автор
25.06.2022 13:37ну это вы про функциональное программирование, все же большинство языков программирования не функциональные. Так что Scala тут не спасет.
Я не знаю Scala, я знаю Lisp, например. Но не вижу как это можно сделать в Lisp
lair
25.06.2022 15:36WithContext[*[_], Ctx]
Я скалу читать почти не умею, поэтому короткий вопрос: я же правильно понимаю, что все функции, вплоть до нижней (в нашем примере — и
Middle
, иDown
), явно декларируют что им требуется конкретный контекстCtx
?allter
25.06.2022 15:53+1Да. Если бы этой информации не требовалось, то это была бы плохая строгая типизация. :)
Но ссылается только на общий объект контекста, а не на то, что в нём должен быть какой-то конкретный элемент c.
Иногда (в контексте DI) уже есть блоки, которые работают с более узким контекстом. Тогда их можно скомпозировать. К примеру, down можно ещё больше разбить:
// функция, которая умеет работать с общим контекстом def down[F[_]: Applicative: WithContext[*[_], Ctx], A: Numeric: Extract[Ctx, *]]( y: A, z: A ) = { // указываем способ работы с контекстом типа A implicit val wc2: WithContext[F, A] = WithContext[F, Ctx].extract(implicitly[Ctx Extract A]) bottom(y, z) } /* функция, которая умеет работать с более конкретным контектом. плюс используется синтаксис для более компактного доступа к контексту */ def bottom[F[_]: Applicative: WithContext[*[_], A], A: Numeric]( y: A, z: A ) = { val num = Numeric[A] import num._ import tofu.syntax.context._ for { c <- context } yield (y + z) * c }
lair
25.06.2022 15:56Если бы этой информации не требовалось, то это была бы плохая строгая типизация
Да, я понимаю достоинства этой информации, я просто хотел удостовериться, что я правильно понимаю происходящее.
lair
25.06.2022 05:22+2Что скажете насчет способа? Использовали?
Скажем, что использовали, и модифицировать это со временем становится невозможно: одна процедура меняет глобальное значение, потом другая, потом третья...
Или что можно предложить взамен?
Взамен можем предложить декомпозицию задачи по бизнес-сценариям. Если сценарий поменялся — должен поменяться и код.
fixin Автор
25.06.2022 08:43А у меня ровно наоборот, успешный случай применения, в двух случаях. Как я писал выше, мы не можем поменять типовой легаси-код, поэтому приходится применять удачный заплатки.
lair
25.06.2022 14:29А у меня ровно наоборот, успешный случай применения, в двух случаях.
Про свой опыт вы уже написали в статье. Вы спросили про мнение читателей? Вот я вам озвучил свое мнение, основанное на своем опыте.
мы не можем поменять типовой легаси-код
Если вы не можете поменять типовой легаси-код, то и задача не имеет решения.
приходится применять удачный заплатки
Недостатки предложенного вами решения никуда не деваются от того, что в каких-то (весьма редких) случаях иначе сделать нельзя.
fixin Автор
25.06.2022 14:54-1ваше мнение я услышал.
ну как же не имеет решения, если я решил?
Да, иногда так лучше и проще. и надежнее
lair
25.06.2022 14:56ну как же не имеет решения, если я решил?
Не поменяв ни строчки кода?
Да, иногда так лучше и проще. и надежнее
Лучше, проще и надежнее чем что?
fixin Автор
25.06.2022 21:13да, расширения в 1С не меняют ни строчки исходного кода.
Они замещают или добавляются в исходный код.
Весь вопрос в том, чтобы затронуть не так много исходного кода, как в первом способе, когда дополнительный параметр протаскивается через все функции.
lair
25.06.2022 23:48Ну то есть вы можете поменять типовой легаси-код, просто не хотите?
fixin Автор
27.06.2022 12:28скажем так - типовой легаси код часто меняется. и если я внесу много правок, их будет сложно поддерживать. Поэтому я могу замещениями менять легаси-код, но нужно стремиться уменьшать количество таких правок.
lair
27.06.2022 13:48Это какая-то специфическая проблема 1С.
В других (известных мне) языках программирования легаси-код не меняется сам по себе, на то он и легаси.
funca
25.06.2022 07:56+2Проблему можно решить передачей дополнительного параметра c, но если цепочка процедур длинная, это вызовет слишком обширные изменения во многих промежуточных процедурах
Погуглите ReactJS property drilling (здесь props это фактически аргументы функции). Например. Проблема с которой многомиллионная индустрия пытается бороться разными способами уже лет 10. Изговорена масса слов, исписаны тонны бумаги. Пробовали и решения влоб (менять так менять), и глобальные переменные как у вас (state), и ООП (функции превращать в методы объекта, а общую для всех них настройку делать атрибутом этого объекта), и монады из функционального программирования (Reader), и контексты (глобальный обьект, изменения в свойствах которого видны только вглубь по стеку вызовов, но не наружу). На практике используется всё вот это вот в перемешку (ну может быть кроме монад - которые для эстетов). А вообще Scope.
fixin Автор
25.06.2022 08:44-2все это весело и полезно, но увы, ничего нового своим комментарием вы не сказали, кроме того, что проблема застарелая.
Есть еще АОП (аспектно-ориентированное-программирование), может там с контекстами получше?
Red_Nose
25.06.2022 08:44" Вообще-то я программирую в 1С, на языке, похожем на Visual Basic" - более мерзкого языка, чем ВБ я не видел (утрирую). Но потом я увидел 1С. Да, это личное мнение, но хуже "язЫка", чем 1С просто нет. Ибо это го.но не язык
fixin Автор
25.06.2022 08:44Язык как язык, я имел опыт программирования на Visual Basic. По сути один в один.
DmitryBurykin
25.06.2022 10:15+1Один в один?
Опыт можно иметь разный...
Visual Basic обеспечивает поддержку объектно-ориентированного программирования, включая инкапсуляцию, наследование и полиморфизм.
1С - нет.
...1С - среда быстрой разработки в рамках классов/объектов жестко заданных в конфигураторе с помощью мышки.fixin Автор
25.06.2022 13:39-3ООП - это божок, на который молятся?
На Visual Basic как и на PHP, где имеется ООП, оно не дает особых преимуществ.
А насчет "разработки мышкой" у вас серьезные иллюзии. Там вполне полноценный код а-ля Basic пишется + некий аналог SQL. Но я тут не за холиваром об 1с.
michael_v89
26.06.2022 06:24На PHP ООП дает просто дофига преимуществ.
fixin Автор
27.06.2022 12:28не знаю, когда писал свой сайт на PHP, не исползовал ООП, но все может быть.
DmitryBurykin
26.06.2022 08:50Скорее мои серъезные иллюзии были про то, что Вы поймете что речь про объекты медатанных 1С - справочники, документы и т.д. Извините.
lair
25.06.2022 14:32Вот вам несколько вопросов на размышление:
- что произойдет, если
Middle
илиDown
будут запущены самостоятельно, не в рамках вызоваTop
? - что произойдет, если
Top
будет запущена несколько раз параллельно в рамках одного "сеанса"?
fixin Автор
25.06.2022 14:55что будет если метеорит упадет в Золотой Мост? Рассуждения примерно одного порядка. Эти функции так глубоко зашиты в легаси, что отдельного вызова не предусматривают. Я об этом не упомянул в статье, каюсь. Уточню, что middle, Down и Top пишутся поставщиком кода и они закостенели в своем разгуле и разврате легаси.
lair
25.06.2022 14:57что будет если метеорит упадет в Золотой Мост? Рассуждения примерно одного порядка.
Нет, это типичные проблемы, которые возникают при работе с подобным решением.
Уточню, что middle, Down и Top пишутся поставщиком кода
Тогда вы не можете поправить ни одну из них, и ваше решение неприменимо, не правда ли?
fixin Автор
27.06.2022 12:29нет там таких типичных проблем.
Вмешиваться могу, но в ограниченном объеме. Нельзя добавить параметр в каждую функцию, это будет тяжело сопровождать при очередном релизе легаси-кода.
lair
27.06.2022 13:49нет там таких типичных проблем.
В общем случае есть. А то, что их нет у вас, как раз и говорит, что ваша проблема далека от общего случая.
fixin Автор
27.06.2022 15:30не понимаю о чем вы
lair
27.06.2022 15:37О том, что в общем случае программист смотрит на проблемы повторного использования кода. А вы — нет.
fixin Автор
27.06.2022 19:00я? скорее авторы легаси не смотрят. Я как раз очень забочусь о тех, кто будет потом мой код сопровождать. Потому что насмотрелся на то, что приходило ко мне на сопровождение
lair
27.06.2022 19:01Я как раз очень забочусь о тех, кто будет потом мой код сопровождать.
Ваше понимание заботы кажется мне странным. Я бы очень не хотел сопровождать код, подобный тому, который в статье.
lair
25.06.2022 15:04+1Эти функции так глубоко зашиты в легаси, что отдельного вызова не предусматривают.
Вообще, с каждой подобной вашей оговоркой ("отдельного вызова не предусматривают", "нельзя поменять") ваша проблема все дальше уходит от общего случая (который часто встречается, имеет типовые решения и так далее) к конкретной узкой проблеме, которая интересна только в ограниченном контексте.
fixin Автор
25.06.2022 21:14не понял, в чем разница из-за этой оговорки и о каком общем случае речь?
lair
25.06.2022 23:49в чем разница из-за этой оговорки
В том, что есть разница между кодом, предназначенным для повторного использования, и кодом, для такого использования не предназначенным.
о каком общем случае речь?
О вопросе "как передавать контекст в функцию".
- что произойдет, если
VaalKIA
26.06.2022 04:22Какой-то лютый треш.
Во первых, если уж вы поставили тег 1С, то и пишите код на 1С, а не «вот это вот всё».
Во вторых, в 1С нет описанной вами проблемы, поскольку есть параметры по умолчанию, то есть, вы легко можете добавить ещё один параметр в Down и протащить его в своей ветке, а все остальные «типовые» вызовы, будут работать как и раньше, вызывая с двумя параметрами, а вместо третьего придёт «Неопределено».
В третьих, ваш пример это просто говно: процедура с побочными эффектами, к которой вы лепите сбоку глобальное состояние чего-то там…
P. S. ЗУП писали криворукие идиоты, но надеюсь, вам дадут по рукам за то, что вы там сделали. По поводу печатной формы: полный бред, везде в 1С в процедуры печати передаётся ссылка на документ, а значит и — тип, либо это будет куча данных, а значит и тип туда допихнуть — не проблема.fixin Автор
27.06.2022 12:30если я добавлю параметр по-умолчанию, то как это будет сопровождаться при выходе нового типового релиза, вы подумали? Если нет, то у вас маленький опыт работы с сопровождением 1С. Проблема налицо.
michael_v89
26.06.2022 06:10Или что можно предложить взамен?
Писать код нормальноДекомпозиция и разделение ответственности. Вычисление в одной функции, вывод в другой.public sub Top() Input c echo CalcMiddle * c end sub public sub Middle() echo CalcMiddle end sub private sub CalcMiddle() y = get1 z = get2 return CalcDown(y, z) end sub private sub CalcDown(y, z) return y + z end sub
fixin Автор
27.06.2022 12:33ну пример был довольно условным. Так что то, что вы как-то перетасовали порядок, не особо имеет смысл - на практике там не перетасуешь, тем более в базовом легаси-коде.
michael_v89
27.06.2022 13:20Ага, вы начинаете понимать, почему программистам на других языках не нравится 1С.
fixin Автор
27.06.2022 15:30думаю, в каждой работе достаточно заморочек. И опять же, мы говорим о типовых конфигурациях 1С или об 1с как платформе? Разница.
michael_v89
27.06.2022 17:24Мы говорим про ваш пример кода. Чтобы не надо было пробрасывать коэффициенты через глобальные переменные в процедуру, которая что-то вычисляет и выводит, надо разделять вычисление (бизнес-логику) и вывод, тогда композировать вычисления будет проще.
Поэтому нет, "затронутый в этой теме вопрос" касается не всех языков программирования, а только 1С и аналогично написанного плохого кода. В остальных языках нет необходимости использовать глобальные переменные, и не в последнюю очередь в этом помогает ООП.
fixin Автор
27.06.2022 19:01-1я вас умоляю, как будто на условном С# или на чем там пишут бизнес логигу (Кобол?) нельзя написать код так, что в него нельзя будет вмешаться. Не идеализируйте языки программирования. Все в руках архитектора системы
Я вижу единственное преимущество ООП что тут если бы был глобальный класс-контекст вычисления, можно было бы добавить в него переменную. Не в глобальный контекст, а как свойство класса. Это да. Но разница не принципиальна.
michael_v89
27.06.2022 20:04Можно. Но на нем можно написать код, в который можно будет вмешаться, и довольно несложно. В отличие от 1С.
Вы почему-то решили подменять понятие возможности написания гарантией ненаписания. Отсутствие гарантий ненаписания глобальных переменных никак не отменяет возможности в других языках легко обойтись без их использования.
Я вижу единственное преимущество ООП что тут если бы был глобальный класс-контекст вычисления
Преимущество ООП тут в пробросе зависимостей в конструктор с возможностью их легкой замены и наследовании. Также с ООП проще разделять код на независимые компоненты.
Я так понимаю, в вашем примере Top() пишете вы, Middle() это код платформы, который иногда обновляется, и он вызывает Down(), который снова пишете вы.
С ООП вообще можно сделать так.class PlatformLogicService { public function middle(): void { y = this->getY(); z = this->getZ(); echo this->calcDown(y, z); } protected function calcDown(int y, int z): int { return y * z; } } class OurLogicService extends PlatformLogicService { public function __construct( private CoefficientStorage coefficientStorage ) {} public function calcDown(int y, int z): int { c = this->coefficientStorage->getCoefficientValue(); return parent::calcDown(y, z) * c; } }
В дочернем классе надо переопределить всего одну функцию без всяких глобальных переменных.
Или так
class PlatformLogicService { public function calcMiddle(): int { y = this->getY(); z = this->getZ(); return this->calcDown(y, z); } private function calcDown(int y, int z): int { return y * z; } } class OurLogicService { public function __construct( private CoefficientStorage coefficientStorage, private PlatformLogicService platformLogicService, ) {} public function top(): int { c = this->coefficientStorage->getCoefficientValue(); return this->platformLogicService->calcMiddle() * c; } }
Тут мы не копируем реализацию системной логики в свой класс и ничего не переопределяем, меньше риск сломать поведение других системных функций, которые завязаны на calcDown(). Первый подход в умных книжках называется наследование, второй композиция.
tzlom
Предложенный вариант - сплошная боль для тестирования и отладки.
Вы (или коллега) глобальный С или затрете,или забудете его корректно инициализировать. И рекурсивные вызовы будут увлекательными.
Правильное решения - пересмотреть архитектуру, использовать каррирование использовать dependency injection, а иногда и куча параметров - это примлемое решение
Ну и тут статейка про Scala была недавно, но у вас же не скала.
fixin Автор
Обычно 1С-ники сопровождают типовой код и поменять его нет возможности.
То что сама 1С пишет криво свои конфигурации, это факт, но мы вынуждены с этим жить.
DmitryBurykin
Механизм расширений 1С позволяет модифицировать типовой код...
fixin Автор
да, но вот я привел пример, что можно модифицировать много промежуточных процедур, а можно сделать заплатку только в одну процедуру. Выигрыш решения в том числе и в прозрачности. Чем больше процедур затрагивает расширение, тем хуже в 1С.
lair
В моем понимании "прозрачность" — это когда легко понятно происходящее.
Когда мы в процедуре выставляем какое-то глобальное значение, мы не знаем, на что это повлияет. Это решение как раз менее прозрачно, чем явная передача параметра в другую процедуру.
fixin Автор
не знаете почему? Потому что вам комментарий не написали? так я пишу.
lair
Во-первых, если к коду нужно писать комментарий, это намекает на его непрозрачность.
А во-вторых, проблема комментариев к коду в том, что рано или поздно они с ним расходятся. Вот вы написали комментарий в месте присвоения — а потом в месте чтения поменяли код, и теперь комментарий не соответствует реальности. Надо пойти и поправить все места, где вы делали присвоение. Рано или поздно люди забывают это делать (если вообще делали изначально).
fixin Автор
но лучше если комментарий есть, чем если его нет.
lair
Если комментарий устарел и не соответствует действительности — лучше, чтобы его не было.
fixin Автор
ну почему же, история тоже интересна. А так то конечно да, но на мой взгляд чаще код страдает от нехватки комментариев а не от их избытка.
lair
Потому что когда я смотрю на код, я понятия не имею, что этот комментарий устарел, и верю ему на слово.
Истории место в системе хранения версий, а не в коде.
fixin Автор
ну что же вы так доверчиво. Бдительность надо проявлять
lair
А если к комментарию надо "проявлять бдительность", то он мне бесполезен — я все равно иду и читаю код.
fixin Автор
ну это ваши личные тараканы. Мне комментарии помогают. Любые - актуальные или нет
tzlom
Вы спросили как это делать - я ответил.Как это вписать в 1С это уже другой вопрос, я с 1С не работаю.
Приведенный пример решается например замыканием C в Down, после чего вы передаёте/ замыкаете получившийся Down+C в Middle
fixin Автор
что такое замыкание? Можно как-то передавать по стеку дополнительные параметры?
Naf2000
не нужно вам это, в 1С даже типов-функций нет. Какие тут замыкания?
Замыкания функций позволяют скрыть внутри себя контекст места создания и использовать его в месте вызова.
fixin Автор
неплохо, неплохо. Но попробуйте объяснить это научно-популярно. Считайте что Лисп я знаю.
Naf2000
у нас тут ка-бы не ликбез. Это общепринятая терминология https://ru.wikipedia.org/wiki/Замыкание_(программирование)
fixin Автор
Если что-то нельзя объяснить простыми словами, то оно и не стоит изучения.
lair
Замыкание можно объяснить простыми словами. Другое дело, что никто не обязан конкретно вам что-то объснять.
fixin Автор
я не против. Если нет желания дискуссировать, можно и закруглиться. Но ваша позиция будет мною не понята. Правда, может кто-то другой ее поймет и в этом ваш вклад. ок.
tzlom
Как можно знать Лисп и не знать замыкания?
Лямбда в Лиспе захватывающая переменную из контекста объявления и есть замыкание.
Прочтите что-нибудь об этом, хотя бы википедию.
fixin Автор
я лисп давно изучал. Лямбды проходили, но они успешно забыты.
lair
Вы, вроде бы, в своей статье сам пишете:
Вы уж определитесь, ваша проблема растет из ограничений 1С, или касается всех языков программирования?
fixin Автор
Как это в 1С я знаю, хочется понять как эта проблема решается в других языках программирования - это основной посыл статьи. Я определился еще когда писал.
lair
… а потом ответили "обычно 1С-ники сопровождают типовой код и поменять его нет возможности". Ну вот в других языках программирования такой проблемы обычно нет.
fixin Автор
ну раз нет, то я рад за другие языки программирования, но т.к. я их знаю штук 10, сдается мне, что вы лукавите
lair
Зачем вы вообще тогда что-то спрашиваете, если сам знаете штук 10 языков программирования, а сторонние мнения вам кажутся лукавством?
fixin Автор
лукавство - это ваша попытка манипуляций. Я спрашиваю конкретно по данной теме, а не по языкам программирования.
lair
Ну вот "конкретно данная тема" обычно решается не так, как вы предложили.
fixin Автор
а как?
lair
Передачей параметра. Передачей делегата. Передачей стратегии (что снова делегат, только другими средствами). Выделением переопределяемых методов.
Короче, либо явной модификацией кода, либо таким дизайном кода, чтобы он позволял последющие расширения без модификации.
fixin Автор
вот в моем случае легаси написано так, что не подразумевает вмешательство в этом участке кода. Об этом просто не подумали заранее. Приходится "шлифовать напильником".
lair
Если не подразумевает — не вмешивайтесь. Переписывайте полностью.
Потому что иначе вы ставите ПО в ситуацию, на которую оно не рассчитано, и последствия исключительно за собственный счет.