methchod chaining w javascrip, OOP, klasy

0

Cześć mam pytanie. Mam problem które zajmuje mi więcej czasu niż wydaje mi się, że powinno chciałbym napisać klasę, która posiadała by metody statyczne dzięki którym mógłbym wywoływać funckje tej klasy bez tworzenia obiektu (chyba wlasnie napisalem denificje metod statycznych (uczę sie )). Problem w tym, że nie do końca wiem jak to zrobić. Już wam pokazuję o co mi chodzi.

class ValidNumber {
    constructor(input){
        this.input = input;
    }

     isNumber(){
        const numberTypeCondition = typeof this.input === "number";
        if(!numberTypeCondition) throw new Error("input is not type number");
        return this;
    }
    
     isGraterThanTen(){
        const isGraterThanTenCondition = this.input >= 10;
        if(!isGraterThanTenCondition) throw new Error("input is not greater than ten ");
        return this;
    }

     isOddNumber(){
        const isOddCondition = this.input % 2 !== 0;
        if(!isOddCondition) throw new Error("even number ");
        return this;
    } 

}


const number = 13;
let str = "stringSDadasdDSd        "
new ValidNumber(number).isNumber().isOddNumber().isGraterThanTen();

const changed = str.toUpperCase().trim();
console.log(changed);

chciałbym aby mój obiekt pozwalał na wywoływanie metod i działał tak jak np. metody string albo nie wiem Array.isArray(tablica). Mógłby mnie ktoś naprowadzić bo szukałem podobnoego zagadnienia i ciężko znaleźć. Pozdrawiam

0

Odnośnie static - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/static

A co do chaining:

przykład str.toUpperCase().trim() ma sens bo metody zwracają instancję tego samego typu, tj. toUpperCase operuje na stringu i zwraca string, tak samo jak trim

W przypadku ValidNumber.isNumber raczej chcesz zwrócić boolean , to jest spodziewany rezultat w idiomatycznym JS (i nie tylko) tak jak zresztą sam zademonstrowałeś przykładem Array.isArray. Jeżeli bardzo chcesz zaimplementować validator z użyciem chainingu to spróbuj napisać API w takim stylu:

new NumberValidator(number).number().odd().greaterThan(13).isValid()

isValid zwraca boolean, wszystkie inne zwracajś this.

0

dobra to może jeszcz nadam kontekst chcę stworzyć klasę walidacyjną bo chcę sprawdzać wartości kluczy jakie otrzymuje przekazanej do obiektu wywołując metody tej klasy czyli chciałbym coś takiego.

const REQUIRED_KEYS = ["name", "price", "category"];

const nike ={
    name: "nike",
    price: 199, 
    category: ["sport", "kids", "football"]
}

class ShopShelf{
    constructor(productObj){
        const inputKeys = Object.keys(productObj);
        const areRequiredKeysExist = REQUIRED_KEYS.every((key) => inputKeys.includes(key));
        if (!areRequiredKeysExist) throw new Error("required keys didn't exist");
        const {name, price, category } = productObj

        new Validator(price).isNumber().isGreatherThanZeroValidation()
        new Validator(name).isStringValidation();
        new Validator(category).isArray();

        // no i tamn pozniej dalej robię this.name = name itp itd
    }
}

i jeszcze mam pytanie czy dobrze sprawdzam klucze czy może da się to prościej

1
not Michal napisał(a):

Jeżeli bardzo chcesz zaimplementować validator z użyciem chainingu to spróbuj napisać API w takim stylu:

new NumberValidator(number).number().odd().greaterThan(13).isValid()

isValid zwraca boolean, wszystkie inne zwracajś this.

właśnie chodzi mi o to, że nie chcę tworzyć obiekty tylko importować funkcje z klasy

0
mcbrewa napisał(a):

dobra to może jeszcz nadam kontekst chcę stworzyć klasę walidacyjną bo chcę sprawdzać wartości kluczy jakie otrzymuje przekazanej do obiektu wywołując metody tej klasy czyli chciałbym coś takiego.

const REQUIRED_KEYS = ["name", "price", "category"];

const nike ={
    name: "nike",
    price: 199, 
    category: ["sport", "kids", "football"]
}

class ShopShelf{
    constructor(productObj){
        const inputKeys = Object.keys(productObj);
        const areRequiredKeysExist = REQUIRED_KEYS.every((key) => inputKeys.includes(key));
        if (!areRequiredKeysExist) throw new Error("required keys didn't exist");
        const {name, price, category } = productObj

        new Validator(price).isNumber().isGreatherThanZeroValidation()
        new Validator(name).isStringValidation();
        new Validator(category).isArray();

        // no i tamn pozniej dalej robię this.name = name itp itd
    }
}

i jeszcze mam pytanie czy dobrze sprawdzam klucze czy może da się to prościej

Ok, myślałem, że to jakiś projekt edukacyjny (napisałeś że się uczysz). Nie ma co w takim razie wymyślać koła na nowo:

  1. https://lodash.com/docs/4.17.15 wpisz sobie w search is i będziesz miał masę gotowych (statycznych) metod do prostej walidacji
  2. możesz też użyć JSON schema, są gotowe validatory które pokrywają twój case https://json-schema.org/implementations#validators-javascript (ajv to mój go-to)
  3. ostatnio bardzo popularny jest też zod https://zod.dev/?id=basic-usage i chyba ten najbardziej pasuje w twoim przypadku
0

mógłbyś to zrobić w funkcyjny sposób, gdzie miałbyś różne funkcje walidatory, które przyjmowały wartość i zwracały by true albo false np.

function isOddNumber(input){
  return input % 2 !== 0;
}

i sprawdzał w taki sposób:

const isValid = [isNumber, isGreaterThatTen, isOdd].every(func => func(2137))
// albo 
const isValid = [isNumber, isGreaterThatTen, isOdd].every(func) 

albo w proceduralny sposób na wyjątkach

function isOddNumber(input){
  if (input % 2 !== 0) return;
  throw new Error(`number ${input} is not odd`);
}
//...
[isNumber, isGreaterThatTen, isOdd].forEach(func)

0
not Michal napisał(a):
mcbrewa napisał(a):

dobra to może jeszcz nadam kontekst chcę stworzyć klasę walidacyjną bo chcę sprawdzać wartości kluczy jakie otrzymuje przekazanej do obiektu wywołując metody tej klasy czyli chciałbym coś takiego.

const REQUIRED_KEYS = ["name", "price", "category"];

const nike ={
    name: "nike",
    price: 199, 
    category: ["sport", "kids", "football"]
}

class ShopShelf{
    constructor(productObj){
        const inputKeys = Object.keys(productObj);
        const areRequiredKeysExist = REQUIRED_KEYS.every((key) => inputKeys.includes(key));
        if (!areRequiredKeysExist) throw new Error("required keys didn't exist");
        const {name, price, category } = productObj

        new Validator(price).isNumber().isGreatherThanZeroValidation()
        new Validator(name).isStringValidation();
        new Validator(category).isArray();

        // no i tamn pozniej dalej robię this.name = name itp itd
    }
}

i jeszcze mam pytanie czy dobrze sprawdzam klucze czy może da się to prościej

Ok, myślałem, że to jakiś projekt edukacyjny (napisałeś że się uczysz). Nie ma co w takim razie wymyślać koła na nowo:

  1. https://lodash.com/docs/4.17.15 wpisz sobie w search is i będziesz miał masę gotowych (statycznych) metod do prostej walidacji
  2. możesz też użyć JSON schema, są gotowe validatory które pokrywają twój case https://json-schema.org/implementations#validators-javascript (ajv to mój go-to)
  3. ostatnio bardzo popularny jest też zod https://zod.dev/?id=basic-usage i chyba ten najbardziej pasuje w twoim przypadku

no właśnie się uczę i mam za zadanie to koło wymyślić i przy okazji nauczyć sie tak zaprojektować klasę która mi umożliwi wywoływanie metod bez tworzenia obiektu xdddd. ale utknąłem bo jedyne co rozumiem i umiem to coś w tym stylu co napisałem

0

no ale potrzebujesz przekazywać dane o tym, jaką wartość ma sprawdzić walidator. Albo będziesz przekazywać doraźnie do każdej funkcji (tak jak w moich przykładach w poście wyżej, gdzie konkretna liczba zostawała przekazywana bezpośrednio do każdej funkcji w momencie jej wywołania), albo ją zapiszesz na stałe.

Jednak jeśli chcesz ją zapisać na stałe, to potrzebujesz gdzieś to zapisać, czyli właśnie np. utworzyć obiekt.
Chociaż niekoniecznie musi to być przez klasę. Możesz użyć domknięcia:

function createValidators(value) {
    return {
        isOdd: () => value % 2 == 1,
        isGreaterThanMillion: () => value > 1000_000,
    }
};

ale tak czy siak wtedy utworzysz obiekt z metodami, które będą miały dostęp do tych kontekstowych danych.

(dałoby się jeszcze w zmiennych globalnych albo w zmiennej statycznej klasy, ale nie idź tą drogą)

0

trochę sie pogubiłem ale ok w takim razie jak mam np.

const exampleString = "exam PleStRinG";
const printResult = exampleString.toLowerCase().toUpperCase().trim()

i chciałbym zeby moje metody isGraterThanTen() mialy mozliwosc chainowania xd a wartość początkowa była przekazywana tylko raz

2
mcbrewa napisał(a):

chciałbym zeby moje metody isGraterThanTen() mialy mozliwosc chainowania xd a wartość początkowa była przekazywana tylko raz

Twój pierwszy przykład prawie to robił, tylko powinieneś zapisać wynik metod do dodatkowej zmiennej i zwrócić obiekt

class Validator {
    static value;
    static errors = [];

    static input(value) {
        // Ustawiamy wartość i resetujemy tablicę errors

        this.value = value;
        this.errors = [];

        return this;
    }

    static number() {
        // Sprawdzamy, czy wartość jest liczbą

        if (typeof this.value !== "number") {
          this.errors.push({ name: 'number', message: '...' });
        }

        return this;
    }

    static greaterThan(value) {
      // Sprawdzamy, czy wartość jest większe od X

      if (this.value <= value) {
        this.errors.push({ name: 'greaterThan', message: '...' });
      }

      return this;
    }

    static result() {
      // Zwracamy wynik

      return this.errors;
    }
}

const a = Validator.input(1050).number().greaterThan(999).result();
const b = Validator.input('abcdef').number().result();

ale w takiej wersji za każdym razem musisz wywołać result, bo inaczej nowa wartość nadpisze poprzedni wynik

class Validator {
    constructor(value) {
        this.value = value;
        this.errors = [];
    }

    static input(value) {
        return new Validator(value);
    }

    number() {
       // zawartość bez zmian
    }

    greaterThan(value) {
       // ...
    }

    result() {
        // ...
    }
}

const b = Validator.input('abcdef');
const c = Validator.input('ghijklm');

if (...) {
  b.number();
  c.number();
  
  b.greaterThan(500);
  c.greaterThan(999);
}

const resultA = Validator.input(1050).number().greaterThan(999).result();
const resultB = b.result();
const resultC = c.result();

ta wersja jest podobna, ale pozwala na podzielenie walidacji na kilka etapów.

0

Panowie ślicznie dziękuję szkoda, że sam nie potrafiłem tego napisać ehhh jeszcze długa droga przedmeną xddd ale rozwiązanie które mi podał pan Xarviel w pełni mnie satysfakcjonuje. Dzięki !!

1

Pytanie zasadnicze: to po co Ci klasa w ogóle? Zrób z tego funkcje i tyle.

0
Riddle napisał(a):

Pytanie zasadnicze: to po co Ci klasa w ogóle? Zrób z tego funkcje i tyle.

Noo np. zamiast importować pojedyńcze funkcje z pliku importuje sobie obiekt który te funkcje zawiera

0
mcbrewa napisał(a):
Riddle napisał(a):

Pytanie zasadnicze: to po co Ci klasa w ogóle? Zrób z tego funkcje i tyle.

Noo np. zamiast importować pojedyńcze funkcje z pliku importuje sobie obiekt który te funkcje zawiera

I zyskasz tym, co konkretnie?

Jeśli ilość tych funkcji jest tak duża, że nie chcesz importować ich osobno, to odpowiedzią na to nie jest "upchać wszystko do god objectu", bo wtedy zrobisz śmietnik.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.