JavaScript에서 관찰자 모드의 위력

41461 단어 webdevnodejavascript
medium에서 나를 찾았다
이 글은 Observer 모드를 소개하고 자바스크립트로 구현할 것입니다. 특히 이 개념을 이해할 때 더욱 잘 이해할 수 있기를 바랍니다.
관찰자 모델은 여전히 결합 해체 시스템을 설계하는 가장 좋은 실천 중 하나이며, 자바스크립트 개발자라면 누구나 사용할 수 있는 중요한 도구가 되어야 한다.
관찰자 모델은 디자인 모델이다. 이런 모델에서 주체(방법을 가진 대상)는 곧 다가올 소식을 알리기 위해'등록된'관찰자 목록을 유지한다.그들이 소속된 주제로부터 어떤 일에 대한 통지 사건을 받았을 때, 그들은 이 기회를 이용하여 유용한 일을 할 수 있으며, 구체적으로는 그들에게서 받은 정보에 달려 있다.
상태가 최근에 변경되었다는 알림을 여러 개의 대상이 동시에 받아야 할 때 이 모드가 가장 유용합니다.따라서 긴밀하게 결합된 클래스를 사용하지 않고 여러 개의 대상이 전체 응용 프로그램에서 일치성을 유지해야 할 때 이러한 모델의 힘이 나타난다.이 때문에 서로 직접적으로 관련되지 않은 몇 명의 대상이 동시에 일치하게 될 수도 있다.
관찰자는 연결된 후에 자신을 제거할 수 있기 때문에 한 관찰자와 다음 관찰자의 출입을 유연하게 선택할 수 있다. 반대로도 마찬가지다.이 모든 기능을 결합하면 주체와 관찰자 간의 동적 관계를 구축하여 강력한 기능을 구성할 수 있다.
이 개념은 다음과 같다.
관찰자가 수험자의 상태를 주목하고'관찰'이 다가오는 상태 업데이트를 선택할 때 그들은 등록하거나 연락해서 다가오는 정보를 받을 수 있다.그리고 일이 변할 때, 이 관찰자들은 이후의 업데이트를 포함하여 통지를 받을 수 있을 것이다.피험자가 어떤 방송 방법을 사용하여 연결된 관찰자에게 알림 메시지를 보낼 때 이 동작을 완성합니다.이 알림 메시지 중 하나는 그것들을 받은 한 명 이상의 관찰자에게 유용한 데이터를 포함할 수 있다.notify 메시지의 발송 방식은 보통 어떤 notify 방법을 사용해서 관찰자 목록에서 순환하고, 순환마다 관찰자의 업데이트 방법을 사용합니다.관찰자가 더 이상 주체와 관계를 원하지 않을 때 그들은 분리할 수 있다.
다음은 이 모드를 구성하는 모든 일반 참여자를 나열하는 간단하면서도 정확한 테이블입니다.
성함
묘사
메시지
관찰자가 있다.관찰자를 늘리거나 삭제하는 것을 권장할 수 있습니다
관찰자
객체의 상태 변화를 알려야 하는 객체에 대한 업데이트 인터페이스 제공
구체적 목표
관찰자에게 상태 변경 통지를 방송하고 관찰자의 상태를 저장합니다
구체적 조사자
ConcreteSubject에 대한 참조를 저장하고 관찰자의 인터페이스를 업데이트하여 상태가 Subject의 상태와 일치하도록 합니다
이제 코드에서 이게 어떤 모습인지 계속 봅시다.
우리가 해야 할 첫 번째 일은 주제를 만들기 시작하는 것이다. 주제는 관찰자를 관리하는 인터페이스를 가지게 될 것이다.이를 위해 우리는 실제적으로 하나의 단독 함수ObserversList에 구조 함수를 정의해야 한다.
function ObserversList() {
  this.observers = []
}

ObserversList.prototype.add = function(observer) {
  return this.observers.push(observer)
}

ObserversList.prototype.get = function(index) {
  if (typeof index !== number) {
    console.warn('the index passed in to getObserver is not a number')
    return
  }
  return this.observers[index]
}

ObserversList.prototype.removeAt = function(index) {
  this.observers.splice(index, 1)
}

ObserversList.prototype.count = function() {
  return this.observers.length
}

ObserversList.prototype.indexOf = function(observer, startIndex = 0) {
  let currentIndex = startIndex

  while (currentIndex < this.observers.length) {
    if (this.observers[currentIndex] === observer) {
      return currentIndex
    }
    currentIndex++
  }

  return -1
}

ObserversList.prototype.notifyAll = function(data) {
  const totalObservers = this.observers.length
  for (let index = 0; index < totalObservers; index++) {
    this.observers(index).update(data)
  }
}
그런 다음 이 인터페이스를 바디의 속성에 직접 연결합니다.
function Subject() {
  this.observers = new ObserversList()
}
우리는 주제에 직접적으로 원형 방법을 정의할 수 있지만, 우리가 이렇게 하지 않는 이유는 주제는 보통 현실 세계의 용례 중 어떤 물건의 임의적인 실례이기 때문이다. observer 인터페이스를 계승한 다음에 그 기능을 확장하거나 포장을 만들 수 있기 때문이다.
이제 우리는 관찰자를 계속 정의할 것이다.
function Observer() {
  this.update = function() {}
}
서로 다른 대상이 관찰자를 계승할 때, 통상적으로 발생하는 상황은, 관찰자가 찾고 있는 일부 데이터에 대해 흥미를 느끼는 update 함수를 덮어쓰는 것이다.
이것은 주체가 notifyAll 방법을 호출할 때 관찰자의 업데이트 프로그램 함수가 순환마다 사용되기 때문이다.
당신은 위에서 이 점을 볼 수 있다.
ObserversList.prototype.notifyAll = function(data) {
  const totalObservers = this.observers.length
  for (let index = 0; index < totalObservers; index++) {
    // HERE
    this.observers(index).update(data)
  }
}

현실 세계의 예


이제 현실 세계의 예를 살펴보자.
만약 우리가 이 위치Alhambra에서 DMV를 조작하고 있다면.우리는 observer 모드를 사용하여 티켓 호출 시스템을 실현할 것이다.
DMV의 전형적인 매표 시스템에서 사람들이 대기 명단에 오르면 보통 차표 번호를 주고 자신의 번호가 호출될 때까지 기다린다.
그들이 차표 번호를 받기 전에, 차관소는 빈자리가 있는지 확인한 후에 그것을 그들에게 건네줄 것이다.빈자리가 없으면 이들은 후보 명단에 배치돼 지정된 승차권 번호를 첨부한다.
한 사람이 전시대에서 그들의 회의를 끝냈을 때, 우리는 그들이 오늘 이미 끝난 것처럼 가장했다.이것은 그들의 표 번호를 더 이상 사용하지 않고 나중에 다시 사용할 수 있다는 것을 가리킨다.우리의 예시에서, 우리는 대기 명단에 들어갈 다른 사람에게 즉시 분배할 수 있도록 표 번호를 표시할 것이다.
우리가 해야 할 첫 번째 일은 정의DMV 구조 함수이다.
function DMV(maxTicketsToProcess = 5) {
  this.ticketsFree = new Array(40).fill(null).map((_, index) => index + 1)
  this.ticketsProcessing = []
  this.maxTicketsToProcess = maxTicketsToProcess
  this.waitingList = new WaitingList()
}
우리의 예시에서 DMV는 관리자와 표 번호의 목록을 주제로 하기 때문이다.maxTicketsToProcess 인자를 설정했습니다. 인자가 없으면 대기 목록이 항상 비어 있습니다. 대기 목록에 한 사람을 넣는 것이 언제 적합한지 알 수 없기 때문입니다.도착maxTicketsToProcess에 도착했을 때 this.ticketsFree에 표가 남아 있다면 우리는 표 번호로 사람들을 대기 명단에 넣기 시작할 것이다.
현재, 우리가 DMV 구조 함수를 보았을 때, 이것은 this.waitingListWaitingList 실례를 부여하고 있다.이 WaitingList는 기본적으로 ObserversList이다. 왜냐하면 거의 같은 인터페이스를 제공하여 인원 목록을 관리하기 때문이다.
function WaitingList() {
  this.waitingList = []
}

WaitingList.prototype.add = function(person) {
  this.waitingList.push(person)
}

WaitingList.prototype.removeAt = function(index) {
  this.waitingList.splice(index, 1)
}

WaitingList.prototype.get = function(index) {
  return this.waitingList[index]
}

WaitingList.prototype.count = function() {
  return this.waitingList.length
}

WaitingList.prototype.indexOf = function(ticketNum, startIndex) {
  let currentIndex = startIndex

  while (currentIndex < this.waitingList.length) {
    const person = this.waitingList[currentIndex]
    if (person.ticketNum === ticketNum) {
      return currentIndex
    }
    currentIndex++
  }
  return -1
}

WaitingList.prototype.broadcastNext = function(ticketNum) {
  const self = this
  this.waitingList.forEach(function(person) {
    person.notifyTicket(ticketNum, function accept() {
      const index = self.waitingList.indexOf(person)
      self.waitingList.removeAt(index)
      delete person.processing
      delete person.ticketNum
      self.ticketsProcessing.push(ticketNum)
    })
  })
}
broadcastNextnotifyAll 예시의 ObserversList 방법과 맞먹는다.그러나 우리는 .update를 호출하지 않고 개인 실례에 정의된 .notifyTicket를 호출하고 accept 리셋 함수를 두 번째 매개 변수로 제공한다. 이것은 현실 생활의 장면을 모의하기 때문에 한 사람이 그들의 표 번호를 볼 때 그들이 분배한 번호가 호출되고 있음을 깨닫고 그들의 전시 위치로 향하게 된다.
우리는 Person 구조 함수를 정의하여 모든 사람을 실례화할 것이다.
function Person(name) {
  this.name = name
}
이 방법notifyTicket이 누락되었음을 인지하고 계실 수 있습니다. 이 방법은 다음과 같습니다.
person.notifyTicket(ticketNum, function accept() {
대기 목록의 인터페이스와 유니버설 People 인터페이스를 혼동하고 싶지 않기 때문이다.
따라서, 우리는 명단에 있는 사람들을 기다리는 데 사용할 인터페이스를 포함하는 WaitingListPerson 구조 함수를 만들 것이다. 왜냐하면, 이 기능들은 명단에서 가져온 후에 아무 소용이 없다는 것을 알기 때문이다.그래서 우리는 일을 조리 있고 간단하게 한다.
우리는 Person라는 유틸리티를 통해 extend의 실례를 확장할 것이다.
function extend(target, extensions) {
  for (let ext in extensions) {
    target[ext] = extensions[ext]
  }
}
다음은 WaitingListPerson의 정의입니다.
function WaitingListPerson(ticketNum) {
  this.ticketNum = ticketNum

  this.notifyTicket = function(num, accept) {
    if (this.ticketNum === num) {
      accept()
    }
  }
}
위대하다우리가 해야 할 마지막 일은 인원, 표 번호 등을 실제로 추가하거나 삭제할 수 있도록 최종적으로 실현하는 방법 DMV 이다.
function DMV(maxTicketsToProcess = 5) {
  this.ticketsFree = new Array(40).fill(null).map((_, index) => index + 1)
  this.ticketsProcessing = []
  this.maxTicketsToProcess = maxTicketsToProcess

  this.waitingList = new WaitingList()
}

// Extracts ticket # from this.ticketsFree
// Adds extracted ticket # to this.ticketsProcessing
// Or add to this.waitingList
DMV.prototype.add = function(person) {
  if (this.ticketsProcessing.length < this.maxTicketsToProcess) {
    const ticketNum = this.ticketsFree.shift()
    console.log(`Taking next ticket #${ticketNum}`)
    this.processNext(person, ticketNum)
  } else {
    this.addToWaitingList(person)
  }
}

// Appends "processing" and "ticketNum" to person
// Inserts ticket # to this.ticketsProcessing if holding ticketNum
DMV.prototype.processNext = function(person, ticketNum) {
  person.processing = true
  if (ticketNum !== undefined) {
    person.ticketNum = ticketNum
    this.ticketsProcessing.push(ticketNum)
  }
}

// Extracts ticket # from this.ticketsFree
// Adds extracted ticket # to this.waitingList
DMV.prototype.addToWaitingList = function(person) {
  const ticketNum = this.ticketsFree.splice(0, 1)[0]
  extend(person, new WaitingListPerson(ticketNum))
  this.waitingList.add(person)
}

// Extracts ticket # from this.ticketsProcessing
// Adds extracted ticket to this.ticketsFree
DMV.prototype.complete = function(person) {
  const index = this.ticketsProcessing.indexOf(person.ticketNum)
  this.ticketsProcessing.splice(index, 1)[0]
  this.ticketsFree.push(person.ticketNum)
  delete person.ticketNum
  delete person.processing
  if (this.waitingList.count() > 0) {
    this.waitingList.broadcastNext(this.ticketsFree.shift())
  }
}
현재 우리는 충분한 DMV 티켓 관리 시스템을 가지고 있으며 관찰자 모드가 지원한다!
이 앱을 살펴보자.
const alhambraDmv = new DMV()

const michael = new Person('michael')
const ellis = new Person('ellis')
const joe = new Person('joe')
const jenny = new Person('jenny')
const clarissa = new Person('clarissa')
const bob = new Person('bob')
const lisa = new Person('lisa')
const crystal = new Person('crystal')

alhambraDmv.add(michael)
alhambraDmv.add(ellis)
alhambraDmv.add(joe)
alhambraDmv.add(jenny)
alhambraDmv.add(clarissa)
alhambraDmv.add(bob)
alhambraDmv.add(lisa)
alhambraDmv.add(crystal)

const ticketsFree = alhambraDmv.ticketsFree
const ticketsProcessing = alhambraDmv.ticketsProcessing

console.log(`waitingNum: ${alhambraDmv.waitingList.count()}`)
console.log(
  `ticketsFree: ${ticketsFree.length ? ticketsFree.map((s) => s) : 0}`,
)
console.log(`ticketsProcessing: ${ticketsProcessing.map((s) => s)}`)

console.log(michael)
console.log(ellis)
console.log(joe)
console.log(jenny)
console.log(clarissa)
console.log(bob)
console.log(lisa)
console.log(crystal)

alhambraDmv.complete(joe)

console.log(`waitingNum: ${alhambraDmv.waitingList.count()}`)
console.log(
  `ticketsFree: ${ticketsFree.length ? ticketsFree.map((s) => s) : 0}`,
)
console.log(`ticketsProcessing: ${ticketsProcessing.map((s) => s)}`)

alhambraDmv.complete(clarissa)

console.log(michael)
console.log(ellis)
console.log(joe)
console.log(jenny)
console.log(clarissa)
console.log(bob)
console.log(lisa)
console.log(crystal)

현재 우리는 observer 모드가 당신의 응용 프로그램을 어느 정도까지 가져올 수 있는지 보았습니다.우리는 그것을 이용하여 기능이 완비된 DMV 매표 시스템을 세웠다.자신의 등을 두드려라!

결론


이 글은 이것으로 끝냅니다!나는 네가 이것이 매우 가치가 있다고 생각하고 미래에 더욱 많은 것을 기대하길 바란다.
medium에서 나를 찾았다

좋은 웹페이지 즐겨찾기