Является ли использование строковых миксинов для повторного использования кода антипаттерном? - PullRequest
7 голосов
/ 21 июля 2010

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

Считаете ли вы антипаттерном использовать строковые миксины литералов в качестве средства простого повторного использования кода, когда другие методы факторинга не совсем подходят? С одной стороны, это в основном автоматическое компилятором буквальное копирование и вставка, что означает, что однажды смешанные в экземплярах не имеют ничего общего друг с другом. Плохие вещи произойдут (хотя во время компиляции, а не во время выполнения), если символ в строке mixin сталкивается с символом в области действия mixed. Он относительно неструктурирован в том смысле, что можно, например, смешать строку в середине функции, которая будет работать тогда и только тогда, когда переменные в области именуются в соответствии с определенным соглашением. Миксины также могут объявлять переменные, которые затем могут использовать внешние области по своему усмотрению.

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

Ответы [ 3 ]

10 голосов
/ 21 июля 2010

Все ваши критические замечания верны.

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

На самом деле, в моей библиотеке инструментов работает нечто подобное, расширение таблицы строк.Пример кода из реализации динамического значения трассировщика пути:

  T to(T)() {
    static if (!is(T == Scope)) {
      T value;
      if (flatType == FlatType.ScopeValue) value = sr.value().to!(T);
    }
    const string Table = `
                 | bool          | int         | string               | float   | Scope
      -----------+---------------+-------------+----------------------+---------+----------
      Boolean    | b             | b           | b?q{true}p:q{false}p | ø       | ø
      Integer    | i != 0        | i           | Format(i)            | i       | ø
      String     | s == q{true}p | atoi(s)     | s                    | atof(s) | ø
      Float      | ø             | cast(int) f | Format(f)            | f       | ø
      ScopeRef   | !!sr          | ø           | (sr?sr.fqn:q{(null:r)}p) | ø   | sr
      ScopeValue | value         | value       | value                | value   | sr`;
    mixin(ctTableUnrollColMajor(Table,
      `static if (is(T == $COL))
        switch (flatType) {
          $BODY
          default: throw new Exception(Format("Invalid type: ", flatType));
        }
      else `,
      `case FlatType.$ROW:
        static if (q{$CELL}p == "ø")
          throw new Exception(q{Cannot convert $ROW to $COL: }p~to!(string)~q{! }p);
        else return $CELL;
      `
    ).litstring_expand() ~ `static assert(false, "Unsupported type: "~T.stringof); `);
  }

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

3 голосов
/ 21 июля 2010

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

Проблема со строковыми миксинами состоит в том, что их сложнее контролировать, чем рукописный код, в том смысле, что он не представлен физически в вашем источнике таким же образом, что номера строк четко прослеживаются до ошибок, и это может быть сложнее отлаживать. Например, возьмите hello world со строкой mixin:

import std.stdio;

void main()
{
    mixin(hello());
}

string hello()
{
    return "
    writeln(\"hello world\");
";
}

Если бы мы удалили точку с запятой после writeln(), то мы получили бы ошибку

d.d(7): found 'EOF' when expecting ';' following statement

Миксин выполняется в строке 5. Строка 7 - пустая строка. Таким образом, номер строки имеет ограниченную полезность здесь. Теперь этот миксин достаточно короткий, чтобы мы могли поместить его в одну строку и заставить его сказать, что ошибка была в той же строке, что и миксин, но с более сложными миксинами, что, очевидно, не сработает. Таким образом, используя строковый миксин, ваша способность выяснить, где находится ошибка, снижается. Если код генерируется с использованием CTFE, то будет гораздо сложнее выяснить, как именно выглядит код, чтобы выяснить, что с ним не так. Это очень похоже на выяснение того, в какой код превращается макрос в стиле C, за исключением того, что это может быть хуже, потому что они могут быть сгенерированы, а не прямой заменой. Однако они не заменяют, за исключением случаев, когда вы явно указываете их, поэтому они намного безопаснее, чем макросы в стиле C.

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

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

Лично я считаю, что они фантастичны, если вы хотите сгенерировать код, избавляя себя от необходимости писать этот код вручную и, возможно, упрощая генерацию правильного кода для различных ситуаций и избегая рискуя создавать новые ошибки, как вы могли бы написать сами в каждом из тех мест, где вы использовали миксин. Это также один из способов прямого повторного использования кода без необходимости беспокоиться о стоимости вызова функции или проблемах с ограничениями одиночного наследования или чего-либо еще, что делает повторное использование кода более сложным путем вызова функций или наследования. Вы просто копируете и вставляете код в каждое место таким образом, что если вы измените код, изменения будут правильно вставлены везде, и вам не придется беспокоиться об их отслеживании, как если бы у вас было ручное копирование и вставили.

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

1 голос
/ 22 июля 2010

Струнный миксин подобен goto: его следует избегать, когда это возможно, и использовать там, где это необходимо.

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