Распаковка JavaScript 02: OOJS часть 4 — Паттерны проектирования

Чем сложнее становятся классы или объекты, тем труднее ими управлять и/или расширять. Паттерны проектирования — это общие решения этих общих проблем в ООП.

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

Паттерны проектирования делятся на структурные, креативные и поведенческие.

Креативные — эффективное и гибкое создание объектов

Creational предоставляют средства/паттерны для создания эффективных, гибких и многократно используемых объектов. Я рассмотрю только фабричный метод, один паттерн из этой категории может быть вам уже знаком — метод прототипов — создание объектов из других объектов,

фабричный метод

клиент делегирует создание объекта фабрике

думайте о клиенте как о пользователе объекта, а о фабрике как о поставщике.

Клиент знает только, какой объект ему нужен, и может поручить фабрике создать и вернуть именно этот объект, клиенту не нужно знать, как и где был создан объект, пока он соответствует ожидаемому клиентом API. В некотором смысле это настоящая фабрика.

Допустим, наш клиент — магазин Nike, а метод Factory — фабрика Nike, магазин отдает фабрике заказ на конкретную обувь Nike, что делает фабрика, магазин не знает, да ему и не важно, однако магазин ожидает получить тип Nike, который соответствует его описанию и потребностям в соответствии с заказом.

  // Factory 
 // create Nike Airforce 1 object
class Nike_Air_Force1 {

   constructor(name, type){
      this.name = name;
      this.type = type;

    }

 print(){

  console.log(this.name, this.type)

 }

}



// class to create Air Max object 

class Nike_Air_MAX1{

 constructor(name, type){

  this.name = name

  this.type = type

 }

 print(){

 console.log(this.name, this.type)

 }

}

// Factory, receives a message from the client, and decide which shoe(object to create and return)

// actual factory method

class factory{

 // factory api, all the client provides is the type of object needed

// the factory handles the rest and return a relevant object

 createNike = (type) => {

 switch(type){

   case "AirForce":

      return new Nike_Air_Force1("Air force 1", "low")

   case "Max":

      return new Nike_Air_MAX1("Air Max 1", "THE ORIGINAL")

   default:

     break

   }

 }

}


// end of factory 


// client 

let fact = new factory()  // client access to the factory

let force = fact.createNike("AirForce")  // place order and a
// relevant object(created from the class) is returned

force.print();

Вход в полноэкранный режим Выход из полноэкранного режима

поведенческий — облегчает коммуникацию между объектами

вероятно, самые полезные паттерны, я часто их использовал, они открывают новые возможности при работе с несколькими объектами, которые должны общаться друг с другом, не зная друг о друге (loosely coupled)

паттерн «Наблюдатель» —

нашел это объяснение в Вики, и реализация также следует этой спецификации — определяется как паттерн, в котором объект, названный «субъектом», поддерживает список зависимых объектов, названных наблюдателями, и автоматически уведомляет их о любых изменениях состояния, вызывая один из их методов.

Реализации вики немного не хватает, мы собираемся сделать ее немного лучше здесь.

class Subject{
    subs = [] // maintains a list of its dependents called observers

    state = {name: "The 100"}  // when it changes subs will be notified

     subscribe(fn){

        this.subs.push(fn) // subscribing

      // we return a function that has a reference to our function we

      //subscribed with(fn), the returned function can be used to unsubscribe

   // which is basically removing the pushed function out of the subs list

  return (

      () => {

     console.log(fn, 'unsubing')

       this.subs = this.subs.filter(l => l !== fn) // unsubscribing

       }

  )

 }


// notifying the subscribers of any change in the state

 change(){

   this.subs.forEach((subscriberFn)=> {

     subscriberFn(this.state); // notifying all subs and passing new state

 })

 }

// changing/setting state 
setName(name){

 this.state.name = name // change state

 this.change()  // notify all subs of the change

}

}


// usage 
 let sub = new Subject()

// returns an unsubing function

let unsub = sub.subscribe((state)=> {

 console.log(state.name) // code here will be executed when the state of "subject" changes

})

// changing state

sub.setName("GOT") // GOT will be logged

sub.setName("House of The Dragon") // same

// unsubscribing

unsub()

console.log(sub.subs) // should log an empty array

Вход в полноэкранный режим Выход из полноэкранного режима

Просто, но мощно. На основе вышеизложенного вы можете создать простой менеджер состояний, конечно, есть свои нюансы, такие как подписка на непрерывные потоки и т.д., и есть пакеты, такие как RxJS, предназначенные для этого.

Структурные —

объединение различных объектов в осмысленную, целостную и гибкую структуру.

Шаблон фасада —

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

Façade группирует связанные объекты, объекты, которые обычно взаимодействуют друг с другом или вызываются последовательно.

Для полного объяснения я предлагаю эту статью, в которой также реализован фасад на react hooks.

Если взять этот пример, то как насчет объектов, которые работают вместе, чтобы приготовить идеальный кофе.


// objects to be called/used in sequence
 class Ratio{

  coffee_ratio(){

    console.log("picking perfect coffee ration")

  }

 }

  class Brewing{

     pick_method(){

        console.log("picking brewing method")

    }

}


 class Water{

     Ideal_temperature(){

      console.log("reached ideal temp")

      this.brew_with_ideal_temp()

    }

    brew_with_ideal_temp(){

        console.log("brewing with ideal water temp")

    }

 }

 class Time{

    brew_time(){

    console.log("brewing the correct amount of time")

    }

 }


Вход в полноэкранный режим Выход из полноэкранного режима

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

 class CoffeeFacade{
                 // con take objects to be used
        constructor(Ratio, Brewing, Water, Time ){

                this.Ratio = Ratio;

                this.Brewing = Brewing;

                this.Water = Water;

                this.Time = Time

        }

        // using the objects in sequence
    makeCoffe(){

            this.Ratio.coffee_ratio();

            this.Brewing.pick_method();

            this.Water.Ideal_temperature();

            this.Time.brew_time()

    }

 }


// usage

Вход в полноэкранный режим Выход из полноэкранного режима

Чтобы приготовить кофе, вы просто вызываете единственный метод make coffee в фасаде в любое время, когда вам нужен кофе, вместо того чтобы начинать заново и создавать все необходимые объекты каждый раз, это мощно.


 let coffeMaker = new CoffeeFacade(new Ratio(), new Brewing(), new Water(), new Time())

 coffeMaker.makeCoffe()  // to make a new coffee

Вход в полноэкранный режим Выход из полноэкранного режима

Заключение

Паттерны проектирования значительно улучшат ваш код, я рекомендую ознакомиться с ними.

В следующей части серии мы рассмотрим наследование как последнюю статью в OOJS, а затем перейдем к асинхронному коду.

Оцените статью
devanswers.ru
Добавить комментарий