Вообще-то я программирую в 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С - это параметры сеанса.

Такой подход требует внимательной проверки на реентерабельность, но в целом, довольно часто успешно решает проблему.

Непосредственно методику использовал в двух случаях:

  1. В процедуре рассчитывалась зарплата исходя из учтенного для сотрудника времени. Но клиенту для расчета авансов понадобилось начислять зарплату не по фактическому, а по плановому времени. Вид времени определялся из вида документа - аванс или окончательная оплата. А после этого должен был как-то попадать в функцию расчета зарплаты. Тут и пригодилась методика.

  2. В некоторых печатных формах нужно было изменить наименование.
    В процедуре, где выводилось наименование не было сведений о том, из какого документа печатается наименование, а наименование должно было обрабатываться исходя из вида документа (внутренний или клиенту). Я использовал передачу параметров описанным выше методом.

Что скажете насчет способа? Использовали? Или что можно предложить взамен? А может быть некоторые языки поддерживают такую передачу контекста вызова "играючи"? Хотелось бы взглянуть.

Комментарии (90)


  1. tzlom
    24.06.2022 23:46
    +6

    Предложенный вариант - сплошная боль для тестирования и отладки.

    Вы (или коллега) глобальный С или затрете,или забудете его корректно инициализировать. И рекурсивные вызовы будут увлекательными.

    Правильное решения - пересмотреть архитектуру, использовать каррирование использовать dependency injection, а иногда и куча параметров - это примлемое решение

    Ну и тут статейка про Scala была недавно, но у вас же не скала.


    1. fixin Автор
      25.06.2022 08:41

      Обычно 1С-ники сопровождают типовой код и поменять его нет возможности.

      То что сама 1С пишет криво свои конфигурации, это факт, но мы вынуждены с этим жить.


      1. DmitryBurykin
        25.06.2022 09:52

        Механизм расширений 1С позволяет модифицировать типовой код...


        1. fixin Автор
          25.06.2022 13:36

          да, но вот я привел пример, что можно модифицировать много промежуточных процедур, а можно сделать заплатку только в одну процедуру. Выигрыш решения в том числе и в прозрачности. Чем больше процедур затрагивает расширение, тем хуже в 1С.


          1. lair
            25.06.2022 14:35

            Выигрыш решения в том числе и в прозрачности.

            В моем понимании "прозрачность" — это когда легко понятно происходящее.


            Когда мы в процедуре выставляем какое-то глобальное значение, мы не знаем, на что это повлияет. Это решение как раз менее прозрачно, чем явная передача параметра в другую процедуру.


            1. fixin Автор
              25.06.2022 14:51
              -1

              не знаете почему? Потому что вам комментарий не написали? так я пишу.


              1. lair
                25.06.2022 14:54
                +1

                Во-первых, если к коду нужно писать комментарий, это намекает на его непрозрачность.


                А во-вторых, проблема комментариев к коду в том, что рано или поздно они с ним расходятся. Вот вы написали комментарий в месте присвоения — а потом в месте чтения поменяли код, и теперь комментарий не соответствует реальности. Надо пойти и поправить все места, где вы делали присвоение. Рано или поздно люди забывают это делать (если вообще делали изначально).


                1. fixin Автор
                  25.06.2022 21:02

                  но лучше если комментарий есть, чем если его нет.


                  1. lair
                    25.06.2022 23:45

                    Если комментарий устарел и не соответствует действительности — лучше, чтобы его не было.


                    1. fixin Автор
                      27.06.2022 12:22

                      ну почему же, история тоже интересна. А так то конечно да, но на мой взгляд чаще код страдает от нехватки комментариев а не от их избытка.


                      1. lair
                        27.06.2022 13:46

                        ну почему же, история тоже интересна

                        Потому что когда я смотрю на код, я понятия не имею, что этот комментарий устарел, и верю ему на слово.


                        Истории место в системе хранения версий, а не в коде.


                      1. fixin Автор
                        27.06.2022 15:31
                        -1

                        ну что же вы так доверчиво. Бдительность надо проявлять


                      1. lair
                        27.06.2022 15:37

                        А если к комментарию надо "проявлять бдительность", то он мне бесполезен — я все равно иду и читаю код.


                      1. fixin Автор
                        27.06.2022 18:59
                        -1

                        ну это ваши личные тараканы. Мне комментарии помогают. Любые - актуальные или нет


      1. tzlom
        25.06.2022 10:20
        +2

        Вы спросили как это делать - я ответил.Как это вписать в 1С это уже другой вопрос, я с 1С не работаю.

        Приведенный пример решается например замыканием C в Down, после чего вы передаёте/ замыкаете получившийся Down+C в Middle


        1. fixin Автор
          25.06.2022 13:36

          что такое замыкание? Можно как-то передавать по стеку дополнительные параметры?


          1. Naf2000
            25.06.2022 14:23

            не нужно вам это, в 1С даже типов-функций нет. Какие тут замыкания?

            Замыкания функций позволяют скрыть внутри себя контекст места создания и использовать его в месте вызова.


            1. fixin Автор
              25.06.2022 14:52

              неплохо, неплохо. Но попробуйте объяснить это научно-популярно. Считайте что Лисп я знаю.


              1. Naf2000
                25.06.2022 18:48

                у нас тут ка-бы не ликбез. Это общепринятая терминология https://ru.wikipedia.org/wiki/Замыкание_(программирование)


                1. fixin Автор
                  25.06.2022 21:03
                  -1

                  Если что-то нельзя объяснить простыми словами, то оно и не стоит изучения.


                  1. lair
                    25.06.2022 23:46

                    Замыкание можно объяснить простыми словами. Другое дело, что никто не обязан конкретно вам что-то объснять.


                    1. fixin Автор
                      27.06.2022 12:23

                      я не против. Если нет желания дискуссировать, можно и закруглиться. Но ваша позиция будет мною не понята. Правда, может кто-то другой ее поймет и в этом ваш вклад. ок.


              1. tzlom
                25.06.2022 23:18
                +1

                Как можно знать Лисп и не знать замыкания?

                Лямбда в Лиспе захватывающая переменную из контекста объявления и есть замыкание.

                Прочтите что-нибудь об этом, хотя бы википедию.


                1. fixin Автор
                  27.06.2022 12:23

                  я лисп давно изучал. Лямбды проходили, но они успешно забыты.


      1. lair
        25.06.2022 14:34

        Вы, вроде бы, в своей статье сам пишете:


        Но мне кажется, что затронутый в этой теме вопрос касается всех языков программирования, т.к. затрагивает проблему, с которой рано или поздно сталкивается программист при работе со сложным кодом.

        Вы уж определитесь, ваша проблема растет из ограничений 1С, или касается всех языков программирования?


        1. fixin Автор
          25.06.2022 14:52

          Как это в 1С я знаю, хочется понять как эта проблема решается в других языках программирования - это основной посыл статьи. Я определился еще когда писал.


          1. lair
            25.06.2022 14:55

            эта проблема решается в других языках программирования

            … а потом ответили "обычно 1С-ники сопровождают типовой код и поменять его нет возможности". Ну вот в других языках программирования такой проблемы обычно нет.


            1. fixin Автор
              25.06.2022 21:03

              ну раз нет, то я рад за другие языки программирования, но т.к. я их знаю штук 10, сдается мне, что вы лукавите


              1. lair
                25.06.2022 23:48

                Зачем вы вообще тогда что-то спрашиваете, если сам знаете штук 10 языков программирования, а сторонние мнения вам кажутся лукавством?


                1. fixin Автор
                  27.06.2022 12:27

                  лукавство - это ваша попытка манипуляций. Я спрашиваю конкретно по данной теме, а не по языкам программирования.


                  1. lair
                    27.06.2022 13:47

                    Ну вот "конкретно данная тема" обычно решается не так, как вы предложили.


                    1. fixin Автор
                      27.06.2022 15:31

                      а как?


                      1. lair
                        27.06.2022 15:40

                        Передачей параметра. Передачей делегата. Передачей стратегии (что снова делегат, только другими средствами). Выделением переопределяемых методов.


                        Короче, либо явной модификацией кода, либо таким дизайном кода, чтобы он позволял последющие расширения без модификации.


                      1. fixin Автор
                        27.06.2022 18:59

                        вот в моем случае легаси написано так, что не подразумевает вмешательство в этом участке кода. Об этом просто не подумали заранее. Приходится "шлифовать напильником".


                      1. lair
                        27.06.2022 19:01

                        Если не подразумевает — не вмешивайтесь. Переписывайте полностью.


                        Потому что иначе вы ставите ПО в ситуацию, на которую оно не рассчитано, и последствия исключительно за собственный счет.


  1. xadd
    25.06.2022 01:35
    +1

    А может быть некоторые языки поддерживают такую передачу контекста вызова "играючи"?

    Например, классы и вложенные функции


    1. fixin Автор
      25.06.2022 08:41
      +1

      Можно пример?


  1. 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))
    }
    
    


    1. fixin Автор
      25.06.2022 08:42

      Извините, не осилил. Вы бы кратенько принцип изложили.


      1. allter
        25.06.2022 12:40

        Совсем кратко - это как в вашем примере со структурой k, только лениво - контекст (k) остается аргументом итоговой функции, складываемой в значение middleF в верхнем компоненте вычислений. Чуть более подробно - пояснено комментарием. Для ещё более подробного понимания, увы, придется изучить основы функционального программирования на Scala или Haskell

        Выше в комментариях уже упоминали Scala. Но в той статье для передачи структуры контекста используется фича имплицитных параметров. Это совсем не то, что в моем примере.


        1. fixin Автор
          25.06.2022 13:37

          ну это вы про функциональное программирование, все же большинство языков программирования не функциональные. Так что Scala тут не спасет.

          Я не знаю Scala, я знаю Lisp, например. Но не вижу как это можно сделать в Lisp


          1. Naf2000
            25.06.2022 14:25

            Идеи функциональных языков проникают все больше и больше в те, что функциональными никогда не были. Например С++/Java/C#. И эта тенденция неспроста


            1. fixin Автор
              25.06.2022 14:53

              Все же попробуйте.


    1. lair
      25.06.2022 15:36

      WithContext[*[_], Ctx]

      Я скалу читать почти не умею, поэтому короткий вопрос: я же правильно понимаю, что все функции, вплоть до нижней (в нашем примере — и Middle, и Down), явно декларируют что им требуется конкретный контекст Ctx?


      1. 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
          }
        


        1. lair
          25.06.2022 15:56

          Если бы этой информации не требовалось, то это была бы плохая строгая типизация

          Да, я понимаю достоинства этой информации, я просто хотел удостовериться, что я правильно понимаю происходящее.


  1. lair
    25.06.2022 05:22
    +2

    Что скажете насчет способа? Использовали?

    Скажем, что использовали, и модифицировать это со временем становится невозможно: одна процедура меняет глобальное значение, потом другая, потом третья...


    Или что можно предложить взамен?

    Взамен можем предложить декомпозицию задачи по бизнес-сценариям. Если сценарий поменялся — должен поменяться и код.


    1. fixin Автор
      25.06.2022 08:43

      А у меня ровно наоборот, успешный случай применения, в двух случаях. Как я писал выше, мы не можем поменять типовой легаси-код, поэтому приходится применять удачный заплатки.


      1. lair
        25.06.2022 14:29

        А у меня ровно наоборот, успешный случай применения, в двух случаях.

        Про свой опыт вы уже написали в статье. Вы спросили про мнение читателей? Вот я вам озвучил свое мнение, основанное на своем опыте.


        мы не можем поменять типовой легаси-код

        Если вы не можете поменять типовой легаси-код, то и задача не имеет решения.


        приходится применять удачный заплатки

        Недостатки предложенного вами решения никуда не деваются от того, что в каких-то (весьма редких) случаях иначе сделать нельзя.


        1. fixin Автор
          25.06.2022 14:54
          -1

          ваше мнение я услышал.

          ну как же не имеет решения, если я решил?

          Да, иногда так лучше и проще. и надежнее


          1. lair
            25.06.2022 14:56

            ну как же не имеет решения, если я решил?

            Не поменяв ни строчки кода?


            Да, иногда так лучше и проще. и надежнее

            Лучше, проще и надежнее чем что?


            1. fixin Автор
              25.06.2022 21:13

              да, расширения в 1С не меняют ни строчки исходного кода.

              Они замещают или добавляются в исходный код.

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


              1. lair
                25.06.2022 23:48

                Ну то есть вы можете поменять типовой легаси-код, просто не хотите?


                1. fixin Автор
                  27.06.2022 12:28

                  скажем так - типовой легаси код часто меняется. и если я внесу много правок, их будет сложно поддерживать. Поэтому я могу замещениями менять легаси-код, но нужно стремиться уменьшать количество таких правок.


                  1. lair
                    27.06.2022 13:48

                    Это какая-то специфическая проблема 1С.


                    В других (известных мне) языках программирования легаси-код не меняется сам по себе, на то он и легаси.


                    1. Ta_Da
                      27.06.2022 14:40

                      Это специфическая проблема ТСа. Он регулярно находит "гениальные" решения придуманных им проблем, игнорируя решения "общепринятые", просто раньше это не выходило за рамки 1С-форумов.


                      1. fixin Автор
                        27.06.2022 15:30

                        Жду вашего "гениального" решения


                    1. fixin Автор
                      27.06.2022 15:30

                      есть многое на свете, друг Горацио.


  1. funca
    25.06.2022 07:56
    +2

    Проблему можно решить передачей дополнительного параметра c, но если цепочка процедур длинная, это вызовет слишком обширные изменения во многих промежуточных процедурах

    Погуглите ReactJS property drilling (здесь props это фактически аргументы функции). Например. Проблема с которой многомиллионная индустрия пытается бороться разными способами уже лет 10. Изговорена масса слов, исписаны тонны бумаги. Пробовали и решения влоб (менять так менять), и глобальные переменные как у вас (state), и ООП (функции превращать в методы объекта, а общую для всех них настройку делать атрибутом этого объекта), и монады из функционального программирования (Reader), и контексты (глобальный обьект, изменения в свойствах которого видны только вглубь по стеку вызовов, но не наружу). На практике используется всё вот это вот в перемешку (ну может быть кроме монад - которые для эстетов). А вообще Scope.


    1. fixin Автор
      25.06.2022 08:44
      -2

      все это весело и полезно, но увы, ничего нового своим комментарием вы не сказали, кроме того, что проблема застарелая.

      Есть еще АОП (аспектно-ориентированное-программирование), может там с контекстами получше?


  1. Red_Nose
    25.06.2022 08:44

    " Вообще-то я программирую в 1С, на языке, похожем на Visual Basic" - более мерзкого языка, чем ВБ я не видел (утрирую). Но потом я увидел 1С. Да, это личное мнение, но хуже "язЫка", чем 1С просто нет. Ибо это го.но не язык


    1. fixin Автор
      25.06.2022 08:44

      Язык как язык, я имел опыт программирования на Visual Basic. По сути один в один.


      1. DmitryBurykin
        25.06.2022 10:15
        +1

        Один в один?
        Опыт можно иметь разный...
        Visual Basic обеспечивает поддержку объектно-ориентированного программирования, включая инкапсуляцию, наследование и полиморфизм.
        1С - нет.
        ...1С - среда быстрой разработки в рамках классов/объектов жестко заданных в конфигураторе с помощью мышки.


        1. fixin Автор
          25.06.2022 13:39
          -3

          ООП - это божок, на который молятся?

          На Visual Basic как и на PHP, где имеется ООП, оно не дает особых преимуществ.

          А насчет "разработки мышкой" у вас серьезные иллюзии. Там вполне полноценный код а-ля Basic пишется + некий аналог SQL. Но я тут не за холиваром об 1с.


          1. michael_v89
            26.06.2022 06:24

            На PHP ООП дает просто дофига преимуществ.


            1. fixin Автор
              27.06.2022 12:28

              не знаю, когда писал свой сайт на PHP, не исползовал ООП, но все может быть.


          1. DmitryBurykin
            26.06.2022 08:50

            Скорее мои серъезные иллюзии были про то, что Вы поймете что речь про объекты медатанных 1С - справочники, документы и т.д. Извините.


            1. fixin Автор
              27.06.2022 12:28

              Извиняю, конечно.


  1. lair
    25.06.2022 14:32

    Вот вам несколько вопросов на размышление:


    • что произойдет, если Middle или Down будут запущены самостоятельно, не в рамках вызова Top?
    • что произойдет, если Top будет запущена несколько раз параллельно в рамках одного "сеанса"?


    1. fixin Автор
      25.06.2022 14:55

      что будет если метеорит упадет в Золотой Мост? Рассуждения примерно одного порядка. Эти функции так глубоко зашиты в легаси, что отдельного вызова не предусматривают. Я об этом не упомянул в статье, каюсь. Уточню, что middle, Down и Top пишутся поставщиком кода и они закостенели в своем разгуле и разврате легаси.


      1. lair
        25.06.2022 14:57

        что будет если метеорит упадет в Золотой Мост? Рассуждения примерно одного порядка.

        Нет, это типичные проблемы, которые возникают при работе с подобным решением.


        Уточню, что middle, Down и Top пишутся поставщиком кода

        Тогда вы не можете поправить ни одну из них, и ваше решение неприменимо, не правда ли?


        1. fixin Автор
          27.06.2022 12:29

          нет там таких типичных проблем.

          Вмешиваться могу, но в ограниченном объеме. Нельзя добавить параметр в каждую функцию, это будет тяжело сопровождать при очередном релизе легаси-кода.


          1. lair
            27.06.2022 13:49

            нет там таких типичных проблем.

            В общем случае есть. А то, что их нет у вас, как раз и говорит, что ваша проблема далека от общего случая.


            1. fixin Автор
              27.06.2022 15:30

              не понимаю о чем вы


              1. lair
                27.06.2022 15:37

                О том, что в общем случае программист смотрит на проблемы повторного использования кода. А вы — нет.


                1. fixin Автор
                  27.06.2022 19:00

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


                  1. lair
                    27.06.2022 19:01

                    Я как раз очень забочусь о тех, кто будет потом мой код сопровождать.

                    Ваше понимание заботы кажется мне странным. Я бы очень не хотел сопровождать код, подобный тому, который в статье.


      1. lair
        25.06.2022 15:04
        +1

        Эти функции так глубоко зашиты в легаси, что отдельного вызова не предусматривают.

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


        1. fixin Автор
          25.06.2022 21:14

          не понял, в чем разница из-за этой оговорки и о каком общем случае речь?


          1. lair
            25.06.2022 23:49

            в чем разница из-за этой оговорки

            В том, что есть разница между кодом, предназначенным для повторного использования, и кодом, для такого использования не предназначенным.


            о каком общем случае речь?

            О вопросе "как передавать контекст в функцию".


            1. fixin Автор
              27.06.2022 12:29

              извините, я утратил нить дискуссии.


  1. VaalKIA
    26.06.2022 04:22

    Какой-то лютый треш.
    Во первых, если уж вы поставили тег 1С, то и пишите код на 1С, а не «вот это вот всё».
    Во вторых, в 1С нет описанной вами проблемы, поскольку есть параметры по умолчанию, то есть, вы легко можете добавить ещё один параметр в Down и протащить его в своей ветке, а все остальные «типовые» вызовы, будут работать как и раньше, вызывая с двумя параметрами, а вместо третьего придёт «Неопределено».
    В третьих, ваш пример это просто говно: процедура с побочными эффектами, к которой вы лепите сбоку глобальное состояние чего-то там…

    P. S. ЗУП писали криворукие идиоты, но надеюсь, вам дадут по рукам за то, что вы там сделали. По поводу печатной формы: полный бред, везде в 1С в процедуры печати передаётся ссылка на документ, а значит и — тип, либо это будет куча данных, а значит и тип туда допихнуть — не проблема.


    1. fixin Автор
      27.06.2022 12:30

      если я добавлю параметр по-умолчанию, то как это будет сопровождаться при выходе нового типового релиза, вы подумали? Если нет, то у вас маленький опыт работы с сопровождением 1С. Проблема налицо.


  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


    1. fixin Автор
      27.06.2022 12:33

      ну пример был довольно условным. Так что то, что вы как-то перетасовали порядок, не особо имеет смысл - на практике там не перетасуешь, тем более в базовом легаси-коде.


      1. michael_v89
        27.06.2022 13:20

        Ага, вы начинаете понимать, почему программистам на других языках не нравится 1С.


        1. fixin Автор
          27.06.2022 15:30

          думаю, в каждой работе достаточно заморочек. И опять же, мы говорим о типовых конфигурациях 1С или об 1с как платформе? Разница.


          1. michael_v89
            27.06.2022 17:24

            Мы говорим про ваш пример кода. Чтобы не надо было пробрасывать коэффициенты через глобальные переменные в процедуру, которая что-то вычисляет и выводит, надо разделять вычисление (бизнес-логику) и вывод, тогда композировать вычисления будет проще.


            Поэтому нет, "затронутый в этой теме вопрос" касается не всех языков программирования, а только 1С и аналогично написанного плохого кода. В остальных языках нет необходимости использовать глобальные переменные, и не в последнюю очередь в этом помогает ООП.


            1. fixin Автор
              27.06.2022 19:01
              -1

              я вас умоляю, как будто на условном С# или на чем там пишут бизнес логигу (Кобол?) нельзя написать код так, что в него нельзя будет вмешаться. Не идеализируйте языки программирования. Все в руках архитектора системы

              Я вижу единственное преимущество ООП что тут если бы был глобальный класс-контекст вычисления, можно было бы добавить в него переменную. Не в глобальный контекст, а как свойство класса. Это да. Но разница не принципиальна.


              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(). Первый подход в умных книжках называется наследование, второй композиция.