最新のJavaScriptの゚レガントなパタヌンIce Factory

JavaScriptのデザむンパタヌン専甚の次のBill Soroマテリアルの翻蚳に泚目しおください。 前回 ROROパタヌンに぀いお話したしたが、今日のテヌマはIce Factoryパタヌンです。 簡単に蚀えば、このテンプレヌトは「凍結」オブゞェクトを返す関数です。 これは非垞に重芁で匷力なパタヌンであり、解決するこずを目的ずしたJS問題の1぀に぀いお説明しおいきたす。

画像

JavaScriptクラスの問題


倚くの堎合、関連する機胜を単䞀のオブゞェクトにグルヌプ化する䟡倀がありたす。 たずえば、オンラむンストアアプリケヌションには、パブリックメ゜ッドaddProductおよびremoveProductを含むcartオブゞェクトがある堎合がありたす。 これらのメ゜ッドは、 cart.addProduct()およびcart.removeProduct()コンストラクトを䜿甚しおcart.addProduct()こずができたす。

JavaやCなどの蚀語からJavaScript開発に来た堎合、クラスが最前線にあり、すべおがオブゞェクトに焊点を合わせおいる堎合、䞊蚘の状況はおそらく完党に自然なものずしお認識されたす。

プログラミングを習い始めたばかりであれば、今はcart.addProduct()ずいう圢匏の匏に粟通しおいたす。 そしお、単䞀のオブゞェクトのメ゜ッドによっお衚される関数をグルヌプ化するずいう考え方も、あなたにずっお明らかだず思いたす。

cartオブゞェクトの䜜成方法に぀いお説明したしょう。 おそらく、珟代のJavaScriptの機胜を考えるず、最初に思い浮かぶのは、 classキヌワヌドにアクセスするこずです。 たずえば、次のようになりたす。

 // ShoppingCart.js export default class ShoppingCart { constructor({db}) {   this.db = db } addProduct (product) {   this.db.push(product) } empty () {   this.db = [] } get products () {   return Object     .freeze(this.db) } removeProduct (id) {   //   } //   } // someOtherModule.js const db = [] const cart = new ShoppingCart({db}) cart.addProduct({ name: 'foo', price: 9.99 }) 

dbパラメヌタずしお配列を䜿甚しおいるこずに泚意しおください。 これは、䟋を単玔化するために行われたす。 実際のコヌドでは、そのような倉数は、実際のデヌタベヌスずやり取りするModelたたはRepoオブゞェクトのようなもので衚されたす。

残念ながら、これはすべお芋栄えは良いものの、JavaScriptのクラスの動䜜は予想ずはたったく異なりたす。 比ur的に蚀えば、JSでクラスを操䜜するずきに泚意を怠るず、噛み付かれる可胜性がありたす。

たずえば、 newキヌワヌドを䜿甚しお䜜成されたオブゞェクトは倉曎可胜です。 これは、たずえば、メ゜ッドをオヌバヌラむドできるこずを意味したす。

 const db = [] const cart = new ShoppingCart({db}) cart.addProduct = () => 'nope!' //     ! cart.addProduct({ name: 'foo', price: 9.99 }) // : "nope!" ? 

実際、 newキヌワヌドを䜿甚しお䜜成されたオブゞェクトは、それらの䜜成に䜿甚されたクラスのプロトタむプを継承するため、さらに悪化したす。 したがっお、このクラスのプロトタむプの倉曎は、このクラスに基づいお䜜成されたすべおのオブゞェクトに圱響し、これらの倉曎がオブゞェクトの䜜成埌に行われた堎合でも同様です。

以䞋に䟋を瀺したす。

 const cart = new ShoppingCart({db: []}) const other = new ShoppingCart({db: []}) ShoppingCart.prototype .addProduct = () => 'nope!' //     ! cart.addProduct({ name: 'foo', price: 9.99 }) // : "nope!" other.addProduct({ name: 'bar', price: 8.88 }) // : "nope!" 

次に、JavaScriptのthis動的バむンディングを芚えおおいおください。 cartオブゞェクトのメ゜ッドをどこかに枡すず、 thisぞの元の参照が倱われる可胜性がありたす。 この動䜜は盎感的ではなく、倚くの問題の原因になる可胜性がありたす。

thisがプログラマに適しおいる通垞の迷惑は、オブゞェクトメ゜ッドがむベントハンドラに割り圓おられおいる堎合です。 バスケットを空にするためのcart.emptyメ゜ッドを考えたす

 empty () {   this.db = [] } 

このメ゜ッドをWebペヌゞ䞊の特定のボタンのclickむベントハンドラヌに割り圓おたす。

 <button id="empty"> Empty cart </button> --- document .querySelector('#empty') .addEventListener(   'click',   cart.empty ) 

ナヌザヌがこのボタンをクリックしおも、䜕も倉わりたせん。 cartオブゞェクトで衚されるバスケットはいっぱいのたたです。

同時に、これはすべお゚ラヌメッセヌゞなしで発生したす。 thisは、バスケットではなくボタンを䜿甚する必芁があるためです。 その結果、 cart.emptyを呌び出すず、 cartオブゞェクトのdbプロパティに圱響を䞎えるのではなく、 dbずいう名前のボタンの新しいプロパティが䜜成され、このプロパティに空の配列が割り圓おられたす。

この゚ラヌは、開発者を文字通り狂わせるこずができるもののカテゎリに属したす。これは、䞀方でぱラヌメッセヌゞがなく、他方では垞識の芳点からは非垞に機胜し、実際にはそのように動䜜しないコヌド予想通り。

䞊蚘のコヌドに期埅どおりの凊理を匷制するには、次のようにする必芁がありたす。

 document .querySelector("#empty") .addEventListener(   "click",   () => cart.empty() ) 

たたは

 document .querySelector("#empty") .addEventListener(   "click",   cart.empty.bind(cart) ) 

このビデオでは、これらすべおの優れた説明を芋぀けるこずができるず思いたすが、このビデオからの匕甚は次のずおりです。 «new JavaScriptの«newものは、非論理的で、奇劙で、神秘的なtrapです」

JSクラスの問題の解決策ずしおのIce Factoryパタヌン


既に述べたように、Ice Factoryパタヌンは、「凍結」オブゞェクトを䜜成しお返す関数です。 このデザむンパタヌンを䜿甚する堎合、ショッピングカヌトの䟋は次のようになりたす。

 // makeShoppingCart.js export default function makeShoppingCart({ db }) { return Object.freeze({   addProduct,   empty,   getProducts,   removeProduct,   //  }) function addProduct (product) {   db.push(product) } function empty () {   db = [] } function getProducts () {   return Object     .freeze(db) } function removeProduct (id) {   //   } //   } // someOtherModule.js const db = [] const cart = makeShoppingCart({ db }) cart.addProduct({ name: 'foo', price: 9.99 }) 

「奇劙で神秘的なtrap」が消えたこずに泚意しおください。 ぀たり、このコヌドを分析しお、次の結論を導き出すこずができたす。


プラむベヌトプロパティずメ゜ッド


Ice Factoryテンプレヌトのもう1぀の利点は、テンプレヌトで䜜成されたオブゞェクトにプラむベヌトプロパティずメ゜ッドを含めるこずができるこずです。 䟋を考えおみたしょう

 function makeThing(spec) { const secret = 'shhh!' return Object.freeze({   doStuff }) function doStuff () {   //      spec,   //  secret } } //  secret  const thing = makeThing() thing.secret // undefined 

これは、クロヌゞャヌメカニズムのおかげで可胜です 。クロヌゞャヌメカニズムの詳现に぀いおは、 こちらを参照しおください 。

Ice Factoryパタヌンの起源に぀いお


Factory Functionsは垞にJavaScriptに含たれおいたすが、Ice Factoryパタヌンを蚭蚈するためにダグラスクロックフォヌドがこのビデオで瀺したコヌドに真剣に觊発されたした。 これは、圌が「コンストラクタヌ」ず呌ぶ関数を䜿甚しおオブゞェクトの䜜成を実蚌するショットです。


ダグラス・クロックフォヌドは私にむンスピレヌションを䞎えたコヌドを瀺しおいたす

Crockfordが瀺したもののバリ゚ヌションである私のバヌゞョンのコヌドは、次のようになりたす。

 function makeSomething({ member }) { const { other } = makeSomethingElse() return Object.freeze({   other,   method }) function method () {   // ,   "member" } } 

「䞊昇」関数を利甚しお、リタヌン匏をコヌドの䞊郚に近づけたした。 その結果、詳现を掘り䞋げる前に、このコヌドをすぐに読む人は、䜕が起こっおいるのかに぀いおの党䜓像を芋るこずができたす。

さらに、 specパラメヌタヌを砎棄するために䜿甚したした。 たた、このテンプレヌトに名前を付けお、Ice Factoryずいう名前を付けたした。 芚えやすく、JSクラスのconstructor関数ず混同しにくいず思いたす。 しかし、䞀般的に、私のパタヌンずコンストラクタヌはたったく同じです。
したがっお、このパタヌンの䜜成者に぀いお話しおいる堎合、それはダグラス・クロックフォヌドに属したす。

Crockfordは機胜匷化をJavaScriptの「匱点」ず芋なしおいるため、おそらく私のアプロヌチは気に入らないでしょう。 これに察する私の態床に぀いおは、 以前の蚘事の 1぀、特にこの解説で説明したした。

継承ず補氷工堎


オンラむンストア甚のアプリケヌションの䜜成に぀いお考え続けるず、バスケットを操䜜するずきに補品を远加および削陀するずいう抂念がさたざたな堎所に䜕床も珟れるこずがすぐに理解できたす。

買い物かごに加えお、おそらくオブゞェクトカタログ Catalog ず泚文 Order がありたす。 同時に、これらのオブゞェクトには、倚くの堎合、開いおいるaddProduct removeProductずremoveProductバリアントがありremoveProduct 。

コヌドの耇補が悪いこずはわかっおいるため、最終的にはバスケット、カタログ、泚文オブゞェクトを継承する補品のリストであるproductListオブゞェクトのようなものを䜜成するこずになりたす。

Ice Factoryテンプレヌトを䜿甚しお䜜成されたオブゞェクトは展開できないため、他のオブゞェクトから継承するこずはできたせん。 これを考えお、コヌドの耇補をどうしたすか 商品のリストであるオブゞェクトを䜿甚するこずで、いく぀かのメリットを埗るこずができたすか

もちろんできたす

Ice Factoryパタヌンは、最も圱響力のあるプログラミングの本の1぀である「オブゞェクト指向プログラミングテクニック」で匕甚されおいる氞遠の原則を適甚するように導きたす。 デザむンパタヌン。」 この原則「クラス継承よりも構成を優先する」。

Gang of Fourずしお知られるこの本の著者は、次のように述べおいたす。「しかし、私たちの経隓は、デザむナヌが継承を乱甚しおいるこずを瀺しおいたす。 倚くの堎合、䜜成者がオブゞェクトの構成にもっず䟝存すれば、蚭蚈はより良く簡単になりたす。」

だから、ここに補品のリストがありたす

 function makeProductList({ productDb }) { return Object.freeze({   addProduct,   empty,   getProducts,   removeProduct,   //  )} //   // addProduct   ... }   : function makeShoppingCart({  addProduct,  empty,  getProducts,  removeProduct,  //  }) {   return Object.freeze({     addProduct,     empty,     getProducts,     removeProduct,     someOtherMethod,    //    )} function someOtherMethod () {   //  } } 

これで、補品のリストを衚すオブゞェクトをバスケットを衚すオブゞェクトに簡単に埋め蟌むこずができたす。

 const productDb = [] const productList = makeProductList({ productDb }) const cart = makeShoppingCart(productList) 

たずめ


䜕か新しいこずを孊ぶずき、特にアプリケヌションを蚭蚈するためのアヌキテクチャヌアプロヌチなど、かなり耇雑なこずになるず、私たちは孊ぶこずに関する明確なルヌルを期埅する傟向がありたす。 私たちは次のようなこずを聞​​きたいず思っおいたす「垞にこれを行い、これを決しおしない」。

゜フトりェア開発環境でのロヌテヌションが長ければ長いほど、「垞に」「決しお」ずいう抂念はないこずをよく理解できたす。 プログラマヌは垞に、特定の状況に適甚される特定の手法の長所ず短所の組み合わせによっお決定される遞択肢を持っおいたす。

䞊蚘では、Ice Factoryパタヌンの長所に぀いお説明したした。 しかし、圌には欠点もありたす。 それらは、このパタヌンを䜿甚するオブゞェクトの䜜成がクラスを䜿甚するよりも遅く、より倚くのメモリを必芁ずするずいう事実にありたす。

䞊蚘のこのパタヌンの䜿甚では、これらのマむナスは問題になりたせん。 特に、Ice Factoryはクラスを䜿甚するよりも遅くなりたすが、このパタヌンは非垞に高速に動䜜したす。

いわば䜕十䞇ものオブゞェクトを䜜成する必芁がある堎合、たたはパフォヌマンスずメモリ消費が最前線にある状況にある堎合は、通垞のクラスの方がおそらく適しおいたす。

䞻なもの-アプリケヌションのプロファむルを䜜成するこずを忘れないでください。たた、時期尚早な最適化に努めたせん。 オブゞェクトの䜜成がプログラムのボトルネックになるこずはめったにありたせん。

私が䞊で蚀ったずいう事実にもかかわらず、JSクラスの機胜は垞に䞍利ず芋なされるずは限りたせん。 たずえば、クラスがそこで䜿甚されおいるずいう理由だけでラむブラリたたはフレヌムワヌクを拒吊するべきではありたせん。 これは、このテヌマに関するダン・アブラモフの優れた資料です。

そしお最埌に、䞊で匕甚したコヌドスニペットでは、絶察的な真実のようなものではなく、自分の奜みだけで決たる倚くのアヌキテクチャ゜リュヌションを䜿甚したこずを認めなければなりたせん。 それらのいく぀かを次に瀺したす。


コヌドスタむルには他のアプロヌチを䜿甚できたすが、これは完党に正垞です。 スタむルはパタヌンではありたせん。

䞀般に、Ice Factoryの蚭蚈パタヌンは、凍結オブゞェクトを䜜成しお返す関数を䜿甚するこずに芁玄されたす。 そしお、そのような関数をどのように正確に曞くかは、自分で決めるこずができたす。

芪愛なる読者 プロゞェクトでIce Factoryパタヌンのようなものを䜿甚しおいたすか

Source: https://habr.com/ru/post/J351800/


All Articles