アダプターのJavaScriptにおける体現

5941 ワード

アダプター設計モードはJavaScriptにおいて非常に有用であり、ブラウザ間の互換性の問題を処理し、複数の第三者SDKを統合する呼び出しには、その姿が見られます.実は日常の開発の中で、気をつけないである種類の設計のモードに合うコードを書き出してしまうことが多いです.結局のところ、デザインのモードは先輩達がまとめて抽出したいくつかの開発効率を高めるための模版です.日常の開発の中からです. は、実はJavaScriptにおいて比較的一般的なものであるはずである.
ウィキペディアでは、アダプターモードの定義は以下の通りである.
ソフトウェア工学では、アダプタモードはソフトウェア設計モードであり、他のインターフェースから既存の種類のインターフェースを使用することができる.これは通常、既存のクラスを他のクラスと一緒に動作させるために使用され、ソースコードを変更する必要がない.
生活中の例
生活の中で最も一般的なのは電源プラグのアダプターです.世界各国のコンセントの標準はそれぞれ違っています.各国の基準によって対応する電源プラグを買うなら、それはあまりにもお金を浪費しすぎます.もし自分がコンセントを持っていて、壁を壊して、再び接続するというのは現実的ではないはずです.だからプラグのアダプターがあります.プラグを別のプラグに変えて、コンセントとあなたの電源の間を中継するものです.これはアダプターです.
コードに反映される
プログラムに移行して、私個人はこう理解しています.
あなたが見たくないコードを隠してください.これはアダプターです.
複数の第三者SDKにアクセスする
日常開発の例を挙げると、WeChat公衆号の開発にはWeChatの支払いモジュールが使われています.長い間の連絡を経て、やっと流れが通りました.楽しい梱包ラインコードを準備しているところに、新たな需要がありました.宝公衆番号のSDKにアクセスしたいです.お支払いの流れもあります.
コードを多重化するために、シナリオにこのような論理を書くことができる.
if (platform === 'wechat') {
  wx.pay(config)
} else if (platform === 'alipay') {
  alipay.pay(config)
}

//           
しかし、一般的には、各工場のSDKが提供しているインターフェースの呼び出し方式はどれぐらいの違いがありますか?
したがって、上記のコードに対しては、このようなものがあり得る.
//           ,      
const config = {
  price: 10,
  goodsId: 1
}

//                 
if (platform === 'wechat') {
  config.appId = 'XXX'
  config.secretKey = 'XXX'
  wx.pay(config).then((err, data) => {
    if (err) // error

    // success
  })
} else if (platform === 'alipay') {
  config.token = 'XXX'
  alipay.pay(config, data => {
    // success
  }, err => {
    // error
  })
}
今のところ、コードインターフェースはまだはっきりしています.コメントを書いてさえいれば、これはあまりにも悪いコードではありません.しかし、生活はいつも意外に満ちています.私たちはまたQQを追加する必要があるSDK、美団のSDK、小米のSDK、あるいは銀行のSDKを受け取りました.
あなたのコードはこのようなものかもしれません.
switch (platform) {
  case 'wechat':
    //        
  break
  case 'QQ':
    // QQ     
  break
  case 'alipay':
    //         
  break
  case 'meituan':
    //        
  break
  case 'xiaomi':
    //        
  break
}
これはもういくつかの注釈で補える問題ではありません.このようなコードはますます維持しにくくなります.各種のSDKの多彩な呼び出し方式があります.もし他の人が同じような需要をするなら、またこのようなコードを書き直す必要があります.それは資源を浪費することに違いないです.
だから、私たちの業務ロジックが明確であることを保証するために、同時に後代の人がこのピットを踏むことを避けるために、私たちはそれを分解して公共の関数として存在します.その中のあるSDKの呼び出し方法または約束したルールを基准として探します.呼び出し側に教えます.どうすればいいですか?どのようにデータを返してもらえますか?そして関数の内部でこれらの汚い判断を行います.
function pay ({
  price,
  goodsId
}) {
  return new Promise((resolve, reject) => {
    const config = {}

    switch (platform) {
      case 'wechat':
        //        
        config.price = price
        config.goodsId = goodsId
        config.appId = 'XXX'
        config.secretKey = 'XXX'
        wx.pay(config).then((err, data) => {
          if (err) return reject(err)

          resolve(data)
        })
      break
      case 'QQ':
        // QQ     
        config.price = price * 100
        config.gid = goodsId
        config.appId = 'XXX'
        config.secretKey = 'XXX'
        config.success = resolve
        config.error = reject
        qq.pay(config)
      break
      case 'alipay':
        //         
        config.payment = price
        config.id = goodsId
        config.token = 'XXX'
        alipay.pay(config, resolve, reject)
      break
    }
  })
}
このように、私たちはどんな環境においても、アダプターがサポートされている限り、私たちが約束したユニバーサルルールに従って呼び出すことができます.具体的にどんなSDKを実行するかについては、アダプターに関心が必要です.
// run anywhere
await pay({
  price: 10,
  goodsId: 1
})
SDK提供者については、自分の必要なパラメータを知るだけで、自分のやり方でデータを返します.SDKコールルームについては、私たちが約束した共通のパラメータだけでなく、約束した方式でモニター・コール処理を行います.
複数の第三者SDKを統合するとアダプターに任せます.そしてアダプターのコードを圧縮して混淆します.見えない隅に置いてください.このようなコードロジックがはっきりします.)
アダプターは大体このような役割です.アダプターは銀弾ではないことを明確にしてください.それらの煩雑なコードは常に存在しています.業務を書いている時には見えないだけです.目が見えなくても心は煩わさない.
いくつかの他の例
個人的には、jQueryには多くのアダプターの例があります.最も基本的な$('selector').onを含めて、これは明らかなアダプターモードではないですか?
一歩ずつのレベルを下げて、いくつかのブラウザの違いを平らにしました.簡単なonを通じて、メインブラウザでイベントの傍受を行うことができます.
//           
function on (target, event, callback) {
  if (target.addEventListener) {
    //          
    target.addEventListener(event, callback)
  } else if (target.attachEvent) {
    // IE        
    target.attachEvent(event, callback)
  } else {
    //                
    target[`on${event}`] = callback
  }
}
あるいは、Nodeでは、このような例が一般的である.Promiseは、以前はなかったので、ほとんどの非同期はcallbackによって達成され、約束された規則がある.
const fs = require('fs')

fs.readFile('test.txt', (err, data) => {
  if (err) //     

  //       
})
私達の新しい機能は全部Error-first callbackの方式を採用して行って、私達がいくつかの古いプロジェクトの中の機能を多重化する必要がある時、直接に古いプロジェクトのコードを修正してもいけません.このような互換性のある処理は、パーティを呼び出して行う必要があるので、論理コードがあまり乱れていないように見えるように、async/awaitのバージョンにこのようなコールバックを便利に変換することができます.
const fs = require('fs')

function readFile (fileName) {
  return new Promise((resolve, reject) => {
    fs.readFile(fileName, (err, data) => {
      if (err) reject(err)

      resolve(data)
    })
  })
}

await readFile('test.txt')
前にも述べたように、このPromiseは約束された形ですので、共通のアダプターを簡単に実現できます.
function promisify(func) {
  return (...args) => new Promise((resolve, reject) => {
    func(...args, (err, data) => {
      if (err) reject(err)

      resolve(data)
    })
  })
}
そして、使用前に対応する変換を行うと、私達が予想した方法でコードを実行できます.
const fs = require('fs')

const readFile = promisify(fs.readFile)

await readFile('test.txt')
Node 8において、公式はすでにこのようなツール関数を実現しました.
結び目
個人的な観点:すべてのデザインパターンは、無駄な発想ではなく、開発の過程で、まとめて抽出した効率的な方法です.これはつまり、最初にこれらのデザインを生かじる必要がないかもしれません.本の中で言っているシーンは全面的ではないかもしれません.また、ある言語に対して、より良い解決方法があるかもしれません.だから、無理に当てはめても、魂のコードが書かれないかもしれません.)
紙の上にあるものは結局浅いです.このことは必ずお辞儀をします.――『冬夜読書示子嵐』、陸遊