JavaScript’te Bağdaştırıcı Modeli – JSManifest

JavaScript'te Bağdaştırıcı Modeli – JSManifest

Bir işlevin son ekinin eklendiği kodla hiç karşılaştınız mı? Adapter? Bağdaştırıcı Modelini kullanarak iki arabirim arasındaki davranış uyumluluğunu içeren kodu okuyor olmanız büyük bir olasılıktır. Bu gönderi JavaScript’teki Bağdaştırıcı Kalıbını gözden geçirecek ve kalıbın neden önemli olduğunu ve modern programlamanın neden faydalı olabileceğini açıklayacaktır.

Bağdaştırıcının çalışma şekli, bir işlev (bir sınıf olabilir, ancak bir sınıf olarak uygulanması gerekmez) oluşturmanızdır. uyum sağlar arabiriminin özelliklerini ve yöntemlerini yeni bir sınıfa sorunsuz bir şekilde, bağdaştırıcı hala tanımlanabiliyor ve orijinal gibi davranırken aynı zamanda yeni arabirimle çalışabiliyormuş gibi.

JavaScript’teki web teknolojisi sürekli geliştiğinden, bugün aşağıdaki gibi açık kaynak projelerinde kullanılan bu kalıbı görmek kolaydır. aksios-sahte-adaptör örneğin.

Nasıl Görünüyor

JavaScript’te bir bağdaştırıcı kalıbı yazmanın doğru bir yolu yoktur. Ancak hepsi aynı hedefi paylaşır: bir arayüzden diğerine uyumluluk sağlamak.

Örneğin, nesnelerin listesini alan bir fonksiyonumuz olsaydı (her birine eylem nesneleri gelecek örnekte) ve bunları şuna dönüştürür: Action örnekleri, her birini çağıran başka bir işleve geçirmeden önce bir diziye execute yöntemi, daha sonra, uyumluluğu korumak istiyorlarsa, uygulamaya yönelik herhangi bir yeni giriş, bu benzer arabirimi izlemelidir.

Örneğin, ortak bir özelliği paylaşan bu eylem nesnelerini alalım. actionType Emlak:

const pageJumpActionObject = {
  actionType: 'pageJump',
  destination: '/about',
}

const refreshPageActionObject = {
  actionType: 'refresh',
}

const saveActionObject = {
  actionType: 'save',
  data: { fruits: ['apple'] },
}

const actions = [
  pageJumpActionObject,
  refreshPageActionObject,
  saveActionObject,
]

Şimdi bu sınıflar verildiğinde tanımlıyoruz:

class Action {
  constructor(action) {
    this.action = action
  }
}

class PageJumpAction extends Action {
  constructor(action) {
    super(action)
    this.destination = action.destination
  }
  execute() {
    console.log(`Redirecting to: ${this.destination}`)
  }
}

class SaveAction extends Action {
  constructor(action) {
    super(action)
    this.data = action.data
  }
  generateFormData() {
    const formData = {}
    Object.keys(this.data).forEach((key) => {
      formData[key] = this.data[key]
    })
    return formData
  }
  execute() {
    console.log(`Saving data`, this.generateFormData())
  }
}

class RefreshAction extends Action {
  execute() {
    console.log('Refreshing the page')
  }
}

diyelim ki bizde var ActionChain eylem nesneleri dizisini alır ve bunları yürütmeye hazırlar:

class ActionChain {
  constructor(actions) {
    this.actions = actions.reduce((arr, action) => {
      switch (action.actionType) {
        case 'save':
          return arr.concat(new SaveAction(action))
        case 'update':
          return arr.concat(new UpdateAction(action))
        case 'pageJump':
          return arr.concat(new PageJumpAction(action))
        case 'refresh':
          return arr.concat(new RefreshAction(action))
        default:
          return arr
      }
    }, [])
  }
  execute() {
    for (let index = 0; index < this.actions.length; index++) {
      this.actions[index].execute()
    }
  }
}

function runActionChain(actionChain) {
  actionChain.execute()
}

const actionChain = new ActionChain(actions)
runActionChain(new ActionChain(actions))

console.log('ran all actions')

Her eylem, hepsi uyumlu bir arayüzü paylaştığı için uygulamamızda sorunsuz çalışır.

Uygulamaya yeni bir eylem eklemek istiyor, ancak daha özlü ve sağlam bir sözdizimi kullanıyorsak, yeni uygulama çalıştırıldığında mevcut programınızı bozarsa bu bilgiyi aklımızda tutmalıyız.

Diyelim ki bir şirket için ön uçta çalışıyoruz ve ekip yöneticimiz değiştirmek/kullanımdan kaldırmak istiyor. pageJump nesneler (gibi { actionType: "pageJump", destination: "/contact" }) gibi daha kısa bir sözdizimine { goto: "/contact" }. Bu değişikliği uygulamamızda yaparsak kodumuz bozulur. Bunun nedeni, koşucumuz runActions yürütmek onların execute yeni sözdiziminde bulunmayan yöntem.

destek vererek bu sorunu aşabiliriz. goto ile eylem adaptör Böylece program aynı şekilde davranabilir.

Tabanı genişleten bir sınıf oluşturarak başlayabiliriz. Action:

class GotoAction extends Action {
  constructor(action) {
    super(action)
    this.url = action.goto
    this.history = []
  }
  setUrl(url) {
    this.url = url
  }
  execute() {
    console.log(`Navigating to ${this.url}`)
    this.history.push(this.url)
    
  }
}

const actionChain = new ActionChain([
  new SaveAction({ actionType: 'save', data: 'abc' }),
  new GotoAction({ goto: 'https://google.com' }),
])

Veya, goto temelde sadece pageJump’ın başka bir sürümü nihai hedefleri aynı olduğu için, ikisinden birini alabilen ve içinde çalıştırılabilecek bir şey döndürebilen farklı bir adaptör tanımlayabiliriz. ActionChain:



class GotoAdapter extends GotoAction {
  constructor(action) {
    const gotoActionObject = {
      actionType: 'goto',
      url: action.destination || action.goto,
    }
    this.action = gotoActionObject
  }
}

Bu şekilde pageJump eylemlerimizin aynı şekilde davranmasını sağlayabiliriz. Sadece bu değil, aynı zamanda goto’nun erişmeyi sevdiği genişletilmiş yöntemleri kullanmalarını da sağlayabiliriz. this.history ve istediğimiz zaman yeni URL’ler ayarlıyoruz.


const pageJumpActionObject = { actionType: 'pageJump', destination: '/faq' }
const pageJump = new PageJumpAction(pageJumpActionObject)


const pageJumpActionObject = { actionType: 'pageJump', destination: '/faq' }
const pageJump = new GotoAdapter(pageJumpActionObject)
pageJump.execute()
console.log(pageJump.history) 
pageJump.setUrl('https://www.apple.com')
pageJump.execute()
console.log(pageJump.history) 


const gotoActionObject = { goto: '/faq' }
const goto = new GotoAdapter(gotoActionObject) 
goto.execute()
console.log(goto.history) 
goto.setUrl('https://www.apple.com')
goto.execute()
console.log(goto.history) 

Artık eski kodun bazılarını yenilerine geçirmek için yapılan değişiklikleri bozmadan uygulamayı daha da geliştirmeye devam edebiliriz:

const someApi = (function () {
  class ActionChain {
    constructor(actions) {
      this.actions = actions.reduce((arr, action) => {
        switch (action.actionType) {
          case 'save':
            return arr.concat(new SaveAction(action))
          case 'update':
            return arr.concat(new UpdateAction(action))
          case 'goto':
            return arr.concat(new GotoAction(action))
          case 'pageJump':
            return arr.concat(new PageJumpAction(action))
          case 'refresh':
            return arr.concat(new RefreshAction(action))
          default:
            return arr
        }
      }, [])
    }
    execute() {
      for (let index = 0; index < this.actions.length; index++) {
        this.actions[index].execute()
      }
    }
  }

  return {
    createActionChain(actionObjects) {
      return new ActionChain(actionObjects)
    },
  }
})()

Diğer örnekler

Farklı perspektiflerde daha fazla örneğin, geliştiricilerin kavramları daha fazla anlamasına yardımcı olduğuna inanıyorum, bu yüzden işte bu kalıbı kullanan birkaç örnek daha.

Hesap makinesi

İşte bir örnekten alınan bir örnek öz:


class OldCalculator {
  constructor() {
    this.operations = function (term1, term2, operation) {
      switch (operation) {
        case 'add':
          return term1 + term2
        case 'sub':
          return term1 - term2
        default:
          return NaN
      }
    }
  }
}


class NewCalculator {
  constructor() {
    this.add = function (term1, term2) {
      return term1 + term2
    }
    this.sub = function (term1, term2) {
      return term1 - term2
    }
  }
}

Bir hesap makinesi uygulaması kullanılarak yazılmışsa OldCalculator ile çalışması için bir yol sağlamak istiyor. NewCalculator arayüz, eşleştirmek için bir yola ihtiyaçları var NewCalculator davranışlarının aynı şekilde çalıştığı yerde.

Aşağıdaki örnek, bu sorunu çözdüğü bir Adaptör kullanılarak yazılmıştır:


class CalcAdapter {
  constructor() {
    const newCalc = new NewCalculator()

    this.operations = function (term1, term2, operation) {
      switch (operation) {
        case 'add':
          
          return newCalc.add(term1, term2)
        case 'sub':
          return newCalc.sub(term1, term2)
        default:
          return NaN
      }
    }
  }
}


const oldCalc = new OldCalculator()
console.log(oldCalc.operations(10, 5, 'add')) 

const newCalc = new NewCalculator()
console.log(newCalc.add(10, 5)) 

const adaptedCalc = new CalcAdapter()
console.log(adaptedCalc.operations(10, 5, 'add')) 

Axios adaptörü

Daha önce bahsettiğim yazıda aksios-sahte-adaptör vaatlerin kullanımını ve orijinal geri arama yaklaşımını destekleyerek uyumluluk sağlamak için kodlarında Adaptör modelini kullanan:

function adapter() {
  return function (config) {
    var mockAdapter = this
    
    
    if (arguments.length === 3) {
      handleRequest(mockAdapter, arguments[0], arguments[1], arguments[2])
    } else {
      return new Promise(function (resolve, reject) {
        handleRequest(mockAdapter, resolve, reject, config)
      })
    }
  }.bind(this)
}

Bu harika bir örnek çünkü sözdizimleri gözle görülür şekilde farklı, ancak ikisi de nihayetinde hedeflerini karşılıyor. Bu güzelce kurulur!

Çözüm

Ve bu yazının sonu burada bitiyor! Umarım bunu değerli bulmuşsunuzdur ve gelecekte daha fazlasını ararsınız!

Bir cevap yazın

E-posta hesabınız yayımlanmayacak.