Jak wywołać tę samą funkcję w zwracanym obiekcie na nim samym?

0

Opis problemu w tytule mnie samego bawi, ale inaczej nie umiem sformułować. Może wcale problemu nie ma, a tylko ja przeinżynierowywuję?

Idzie to tak: mam ciąg znaków:

to-jest-ciąg-znaków

Chcę stworzyć drugi ciąg znaków – reprezentujący pudełko – który wynikowo będzie tak wyglądał:

to-je
st-ci
ąg-zn
aków

Kluczowe jest, że po stworzeniu tego pudełka chciałbym, żeby można było je "opakować" – by wyglądało wynikowo tak:

#######
#to-je#
#st-ci#
#ąg-zn#
#aków #
#######

W tym celu stworzyłem dwie metody: createBox oraz wrap. Żeby utworzyć powyższe, opakowane pudełko, wywoływałoby się je w ten sposób (mój wymarzony, elegancki sposób):

const content = "to-jest-ciąg-znaków";
const boxConfig = {
  ... // coś tam
};
const cell = "#";
const frameSize = 1;
const box = createBox(boxConfig, content).wrap(cell, frameSize);
console.log(box.data.map(row => row.join("")).join("\n"));

W pliku create-box.js mam moduł z funkcją, która tworzy pudełko:

const wrap = require("./wrap").wrap;

exports.createBox = function (config, content) {
  const data = [];
  ... // dużo operacji na tablicy data
  return {
    data: data,
    ..., // pozostałe właściwości
    wrap: (cell, frameSize) => wrap(???, cell, frameSize)
  };
}

Metoda wrap jest zdefiniowana w module, który jest w pliku wrap.js, i wygląda tak:

exports.wrap = function (box, cell, frameSize) {
  const wrappedData = [];
  ... // dużo operacji na tablicy wrappedData
  return {
    data: wrappedData,
    ..., // pozostałe właściwości
    wrap: (cell, frameSize) => module.exports.wrap(???, cell, frameSize)
  };
}

Względem powyższego kodu mój problem można zdefiniować krótko: co umieścić w miejscu oznaczonym "???"?

Myślałem o słowie kluczowym this, bo w końcu zwracany jest obiekt, który mamy właśnie opakować. Jednak nie działa – w momencie wywołania this evaluates do globalnego obiektu (nie wiem, jak po polsku powiedzieć evaluates).


PS.

  • Całość powinna działać w Node.js.
  • Przez "moment wywołania" wspomniany wyżej mam na myśli wywołanie w pliku, powiedzmy, index.js, który wykonywany jest poleceniem node index.js.

PS2.

W powyższej implementacji mojego pomysłu chodzi o to, żeby metodę wrap można było wywoływać na tym, co zwraca. Dzięki temu można by osiągnąć na przykład taki ładny (?) ciąg wywołań:

const box = createBox(...).wrap("#", 1).wrap("$", 2).wrap("@", 1);

PS3. Jeśli macie jakieś uwagi na temat powyższej architektury, chętnie poznam. Może da się osiągnąć prościej, co sobie zamierzyłem?

0

Wydaje się działać (nie mam testów jednostkowych do tego) z użyciem this, jeśli zamiast arrow function expression użyć zwykłego function expression.

Tutaj wyjaśnienie: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#No_separate_this

Przez zadaniem pytania nawet próbowałem zmienić => na function, ale zapomniałem, że trzeba w dwóch miejscach zmienić. Ech. :)


PS. Ale jeśli ktoś miałby nadal uwagi do tego kodu czy pomysłu, to bardzo proszę pisać. :)

1

Możesz też podejść do tego problemu w sposób bardziej OOP (chociaż w JS to nadal ciężka sprawa), przy okazji stosując https://sourcemaking.com/design_patterns/decorator, pseudo kod wyglądałby tak (przydały by się interefejsy, więc TypeScript też, ew. javascript z jakimś opisem i nadzieją, że programista który z tego korzysta go przeczyta):

const box = new Box(config, content);
const wrapped = new Wrapper(box);
const superWrapped = new SuperWrapper(wrapped);
const notSoSuperWrapped = new SuperWrapper(box);
const dollarWrapped = new CustomWrapper(box, "$", 2)

zalętą takiego rozwiązania jest to, że poza dostarczeniu użytkownikowi możliwości wrapowania jak chce (CustomWrapper) może też wrapować po Twojemu (Wrapper/SuperWrapper), może też rozszerzać system bez ingerencji w Twój kod (może łatwo stworzyć SuperHiperWrapper). Dodatkowo jeżeli będziesz chciał usunąć jakiś konkretny wrapper to usuwasz 1 klasę + ew. użycia. Jak byś chciał rozbudowywać tą metodę wrap i dodać np. superWrap to musisz zmienić co najmniej 2 pliki + użycia w kodzie (1 plik z definicją, 1 plik z tym createBox), możesz też umieszczać to w 1, ale jeżeli powstanie dużo takich customowych metod to plik się rozrośnie.

0

@Markuz: to wydaje się w rzeczy samej rozwiązanie bardziej elastyczne i "low-couplingowe" od mojego. O wzorcu decorator chętnie poczytam.

Póki co jednak idę w kierunku "musi działać", więc staram się ograniczać zmiany, hm, "podejść". M.in. słowo kluczowe new ani razu nie występuje w moim projekcie (co nie oznacza, że za jakiś czas go nie dodam).

1 użytkowników online, w tym zalogowanych: 0, gości: 1