Друкарня від WE.UA

Паттерн “Специфікація“ в JS чи потрібен він?

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

Що це таке?

Специфікація - це патерн проектування який дозволяє комбінувати бізнес-правила за допомогою булевої логіки. Складно? От і мені так здалось на перший погляд. Насправді найпростіша реалізація цього патерну виглядає ось так

class ByIdSpecification {
  constructor(private id: string) {}

  isSatisfiedBy(candidate: any): boolean {
    return candidate.id === this.id
  }

}

Як це використовувати?

const array = [{ id: 1, name: "Viktor" }, {id: 2, name: "Oleg"}]

const specification = new ByIdSpecification(1)

const result = array.find(specification.isSatisfiedBy)

Тепер виглядає простіше. Але для чого це? Виглядає як оверінженірінг. Бо цю саму задачу можно зробити за допомогою набагато меншого коду.

Що ж подивимось туди де це використовується найчастіше С#. Тут цей паттерн використовують для того щоб уніфікувати запити до бази данних. Так як фреймворк Entity спілкується з базою за допомогою функцій подібно функції filter або find в JS

Але в JS а конкретно в Node.js коли мі спілкуємось с базою за допомогою query обєктів. Наприклад в одній з найпопулярніших баз данний MongoDB звичайний запит до моделі виглядає от так

model.find({ something: 2 })

Я пропоную свою реалізацію

Я не претендую на безальтернативний варіант, але в мене є одна реалізація яка допомагає використовувати цей паттерн навіть в наших умовах

class ByIdSpecification {
  constructor(private id: string) {}

  isSatisfiedBy() {
    return {
      id: this.id
    }
  }

}

model.find(new ByIdSpecification(1).isSatisfiedBy())

Розумію що все ще виглядає як оверінженірінг. Але навіть зараз ми вже маємо 2 переваги.

  1. Збільшена читаємість

  2. Можливість перевикористання

Тобто купка незрозумілих параметрів в квері стають осмисленими данними. Наприклад от так можно зробити специфікацію для пошука географічних данних

class GeoSpecification {
  constructor(private lat: number, private lng: number){}

  isSatisfiedBy() {
    return {
      lat: this.lat,
      lng: this.lng
    }
  }

}

Але головна фішка ціього паттерну попереду.

Булева логіка в кущах

Доки я повністю не зрозумів цю частину, я не бу наскільки закоханим у цей паттерн. Специфікації можно комбінувати. В визначенні патерна фігурує фраза про булеву логіку. Це найголовніша перевага цього патерну. Подивимось на реалізацію цього в JS. На прикладі одніє булевої функції AND

interface ISpecification {
  isSatifiedBy()
  and(specification: ISpecification): ISpecification
}

class CompositeSpecification implements ISpecification {
  
  isSatisfiedBy() {
    throw NotImplementError()
  }

  and(specification: ISpecification): ISpecification {
    return new AndSpecification(this, specification)
  }

}


class AndSpecification extends CompositeSpecification {
  constructor(left: ISpecification, right specification) {
    super()
    this.left = left;
    this.right = right;
  }

  isSatisfiedBy(): {
    return this.left.isSatisfiedBy() && this.right.isSatifiedBy()
  
  }

}


class ByIdSpecification extends CompositeSpecification {
  constructor(private id: string) {}
  
  isSatisfiedBy() {
    return {
      id: this.id
  } 
}

class GeoSpecification extends CompositeSpecification {
  constructor(private lat: number, private lng: number) {}
  
  isSatisfiedBy() {
    return {
      lat: this.lat,
      lng: this.lng
    }
  }

}

const query = new ByIdSpecification(1)
  .and(new GeoSpecification(123, 321))
  .isSatisfiedBy()

model.find(query)

Тепер ми можемо використовувати комбінації цих специфікації що дозволить нам покращити перевикористовування. Та зменшити повторення. Також можно реалізовувати і інші булеві функції наприклад or але є одне обмеження. Туди пролазять деталі реалізації бази. Наприклад

class OrSpecification extends CompositeSpecification {
  constructor(left: ISpecification, right: ISpecificaition) {
    super()
    this.left = left;
    this.reight = right
  }

  isSatisfiedBy() {
    return {
      $or: [this.left.isSatifiedBy(), this.right.isSatifiedBy()]
    }
  }

}

Але є і хороша частина в цьому. Якщо вам захочеться змінити базу посеред проекту(це буває рідко, а ле хто вас бешкетників знає). То ви зможете змінити спосіб комбінації лише в одному місці

Висновки

Стандартну реалізацію цього патерно можно використовувати наприклад для фронтенда з редаксом. Або будь де де в нас є масив inmemory який ми можемо перебирати за допомогою функцій filter або find. Мою реалізацію можно використовувати на повну силу для того щоб робити пошук по базі. Як це наприклад робиться в С#. Буду радий почути коментарі або критику такого підходу.

Статті про вітчизняний бізнес та цікавих людей:

  • Чохли для iPhone 15: повний гід по кольорах, матеріалах і виробниках

    Перед тим як вибрати чохли для iPhone 15, варто визначитися, що саме ви хочете отримати від аксесуара. Комусь потрібен тонкий прозорий корпус, інший покупець шукає посилений захист, а для когось вирішальним стане колір або підтримка MagSafe

    Теми цього довгочиту:

    Чохли Для Iphone
  • Як побудована програма Meest China Academy

    Курс про товарний бізнес охоплює різні етапи роботи: від пошуку ідеї до перевірки товару, логістики та масштабування. Програма Meest China Academy містить 17 модулів, які послідовно розкривають ці теми без зведення всього навчання до однієї універсальної поради.

    Теми цього довгочиту:

    Meest
  • Які технології використані в Айфон 17

    Айфон 17 поєднує OLED-дисплей із частотою до 120 Гц, чип A19, дві камери Fusion 48 Мп і швидке заряджання через USB-C. У COMFY можна купити Айфон 17 з накопичувачем на 256 або 512 ГБ, вибравши конфігурацію відповідно до обсягу фото, відео та застосунків

    Теми цього довгочиту:

    Iphone 17
  • Як вибрати вживаний iPhone: коротке керівництво для розумної покупки

    Вторинний ринок смартфонів Apple активно зростає, і питання, як вибрати вживаний iPhone, стає актуальним для дедалі більшої кількості користувачів. Правильний підхід дозволяє отримати бу iPhone з актуальною iOS і доброю камерою за помітно нижчу ціну, ніж у нових пристроїв.

    Теми цього довгочиту:

    Iphone
  • Як подолати жіночу безплідність за допомогою донорських яйцеклітин

    Діагноз "безпліддя" рідко звучить як вирок одразу. Для багатьох жінок саме в цей момент вперше звучить словосполучення "донорські яйцеклітини" — і це часто не кінець шляху до материнства, а якраз новий, реальний шанс.

    Теми цього довгочиту:

    Донорство
Поділись своїми ідеями в новій публікації.
Ми чекаємо саме на твій довгочит!
Владислав Лєсной
Владислав Лєсной@unnsin

5Довгочити
796Перегляди
17Підписники
На Друкарні з 15 квітня 2023

Більше від автора

  • Clear Architecture для бекенда на JS

    Кожен хто займається розробкою певний час приходить до слова архітектура. Сьогодні ми подивимось на одну з парадигм проектування архітектури через призму типового JS бекенду.

    Теми цього довгочиту:

    Clean Architecture
  • Паттерн “Репозиторій“ в JS

    В попередній статті ми розібрались з паттерном Специфікація Сьогодні ми поговоримо про логічне продовження паттерн репозиторій.

    Теми цього довгочиту:

    Js

Це також може зацікавити:

Коментарі (0)

Підтримайте автора першим.
Напишіть коментар!

Це також може зацікавити: