Функция использует объект для запуска и уничтожения ресурса. Как я могу переписать это как HOF, который предоставляет ресурс? - PullRequest
0 голосов
/ 28 мая 2020

Вот с чего я начинаю. convert использует svgInjector для инициирования и уничтожения ресурса.

export async function convert(
  serializedSvg: string,
  svgSourceId: string,
  containerId: string
): Promise<string> {
  const svgInjector = new SvgInjector(serializedSvg, containerId).inject();
  if (!svgInjector.injectedElement) {
    throw new Error("Svg not injected");
  }

  const doc = new TargetDocument({});
  const xml = convertRecursively(
    svgInjector.injectedElement,
    doc,
    {
      svgSourceId,
    }
  );

  svgInjector.remove();

  return doc.saveXML();
}

Как я могу переписать это, чтобы вместо этого инициировала функция более высокого порядка, предоставила и уничтожила ресурс svgInjector.injectedElement для функции преобразования?

EDIT:

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

var svg = '<svg xmlns="http://www.w3.org/2000/svg"><text x="20" y="20">I am made available in DOM</text></svg>'

function convert(
  serializedSvg,
  containerId
) {
  // make resource available (cross-cutting convern)
  var container = document.getElementById(containerId);
  var resource = new DOMParser().parseFromString(serializedSvg, "image/svg+xml").documentElement;
  container.appendChild(resource);

  // core convert functionality does things with resource
  console.log(resource.getBBox())
  
  // clean up resource (cross-cutting concern)
  resource.remove()
}

convert(svg, "container")
<!DOCTYPE html>
<html>
<head>
  <title>Minimal</title>
</head>
<body>
<div id="container">
</div>
</body>
</html>

EDIT 2

Вот версия TypeScript JavaScript в предыдущем редактировании

var svg = '<svg xmlns="http://www.w3.org/2000/svg"><text x="20" y="20">I am made available in DOM</text></svg>'

function convert(
  serializedSvg: string,
  containerId: string
) {
  // make resource available (cross-cutting convern)
  var container = document.getElementById(containerId);
  if (!(container instanceof HTMLDivElement)) {
    throw new Error("Extpected a div element")
  }
  var resource = new DOMParser().parseFromString(serializedSvg, "image/svg+xml").documentElement;
  if (!(resource instanceof SVGSVGElement)) {
    throw new Error("Extpected a svg element")
  }
  container.appendChild(resource);

  // core convert functionality does things with resource
  console.log(resource.getBBox())

  // clean up resource (cross-cutting concern)
  resource.remove()
}

convert(svg, "container")

Ответы [ 2 ]

1 голос
/ 31 мая 2020

Я не уверен, что это именно то, что вы ищете, но я был бы склонен инвертировать поток управления, чтобы convert() использовал или передавал «диспетчер ресурсов», который заботится о о создании, оснащении и удалении ресурса. A ResourceManager может быть просто функцией, например:

type ResourceManager<T, I> = <R>(initProps: I, cb: (resource: T) => R) => R;

Итак, ResourceManager<T, I> - это функция, которая принимает некоторый начальный пакет свойств типа I, чтобы указать, какой ресурс типа T необходим. и функция обратного вызова, которая выполняет фактическую работу после того, как ресурс станет доступным, но до его уничтожения. Если функция обратного вызова возвращает результат, то это делает и менеджер ресурсов.

Этот ResourceManager<T, I> является общим контрактом c, который можно повторно использовать для различных типов ресурсов. Конечно, разные типы ресурсов нуждаются в собственных реализациях. Например, я бы вытащил из вашей convert() функции a ResourceManager<SVGSVGElement, { serializedSvg: string, containerId: string }> следующим образом:

const svgManager: ResourceManager<SVGSVGElement, { serializedSvg: string, containerId: string }> =
  (initProps, cb) => {

    // make resource available)
    var container = document.getElementById(initProps.containerId);
    if (!(container instanceof HTMLDivElement)) {
      throw new Error("Extpected a div element");
    }
    var resource = new DOMParser().parseFromString(initProps.serializedSvg, "image/svg+xml").documentElement;
    if (!(resource instanceof SVGSVGElement)) {
      throw new Error("Extpected a svg element")
    }
    container.appendChild(resource);

    // core functionality
    const ret = cb(resource);

    // clean up resource
    resource.remove()

    // return returned value if we have one
    return ret;
  }

Обратите внимание, как «основная функциональность» просто передается обратному вызову, возвращаемое значение которого сохраняется в в случае необходимости. Затем convert() упрощается до:

function convert(
  serializedSvg: string,
  containerId: string
) {
  svgManager({ serializedSvg, containerId }, (resource => console.log(resource.getBBox())));
}

Где resource => console.log(resource.getBBox()) - это функция, которая выполняет работу, не заботясь о том, как получить или избавиться от resource.


Надеюсь, это поможет или даст вам несколько идей. Удачи!

Детская площадка ссылка на код

0 голосов
/ 28 мая 2020

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

Я вижу две слабые стороны решения, которое я вижу ниже:

  • Тип усилителя не общий c, что затрудняет повторное использование
  • Энхансер требует связывания, препятствуя составу энхансера
    type Props = {
      svg: SVGSVGElement;
      svgSourceId: string;
      containerId: string;
    };

    async function convertBase(props: Props): Promise<string> {
      const doc = new TargetDocument({});
      const xml = convertRecursively(props.svg, doc, {
        svgSourceId: props.svgSourceId,
      });

      return doc.saveXML();
    }

    type EnhancerProps = {
      serializedSvg: string;
      svgSourceId: string;
      containerId: string;
    };

    type EnhancerPropsLight = {
      svgSourceId: string;
      containerId: string;
    };

    function enhancer(fn: Function, props: EnhancerProps) {
      const rest = omit(["serializedSvg"])(props) as EnhancerPropsLight;
      const svgInjector = new SvgInjector(
        props.serializedSvg,
        props.containerId
      ).inject();
      if (!svgInjector.injectedElement) {
        throw new Error("Svg not injected");
      }

      const res = convertToTgmlBase({ ...rest, svg: svgInjector.injectedElement });

      svgInjector.remove();

      return res;
    }

    const convert = enhancer.bind(null, convertBase);
    export { convert };

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