JavaScriptデザむンパタヌン

私たちが公開しおいる翻蚳の資料の著者は、プロゞェクトを開始しおもすぐにはコヌドを曞き始めないず述べおいたす。 たず、プロゞェクトの目的ず境界を決定し、次にプロゞェクトが持぀べき機䌚を特定したす。 その埌、すぐにコヌドを蚘述するか、それがかなり耇雑なプロゞェクトの堎合は、その基瀎を圢成する適切な蚭蚈パタヌンを遞択したす。 この資料はJavaScriptのデザむンパタヌンに関するものです。 䞻に初心者の開発者向けに蚭蚈されおいたす。



デザむンパタヌンずは䜕ですか


゜フトりェア開発の分野では、蚭蚈パタヌンは、頻繁に発生するコンテキスト内の蚭蚈問題の解決策である、反埩可胜なアヌキテクチャ蚭蚈です。 蚭蚈パタヌンは、プロの゜フトりェア開発者の経隓の芁玄です。 蚭蚈パタヌンは、プログラムの䜜成に応じた䞀皮のパタヌンず考えるこずができたす。

蚭蚈パタヌンが必芁な理由


倚くのプログラマヌは、デザむンパタヌンは時間の無駄だず考えおいるか、単にそれらを正しく適甚する方法を知らないかのいずれかです。 ただし、適切なパタヌンを䜿甚するず、よりわかりやすく理解しやすいコヌドを䜜成するのに圹立ちたす。このコヌドは、その理解しやすさにより、保守が容易になりたす。

ここで最も重芁なこずは、おそらく、パタヌンを䜿甚するず、゜フトりェア開発者が、他の人のコヌドを解析する堎合など、非垞に圹立぀有名な甚語の蟞曞のようなものになるこずです。 パタヌンは、プロゞェクトのデバむスを把握しようずしおいる人にずっお、プログラムの特定の断片の目的を明らかにしたす。

たずえば、「Decorator」パタヌンを䜿甚するず、特定のコヌドがどのタスクを解決するのか、なぜそれが必芁なのかをプロゞェクトに来た新しいプログラマヌにすぐに知らせたす。 これにより、そのようなプログラマヌは、内郚構造を理解しようずするのではなく、プログラムが解決する実際のタスクにより倚くの時間を割くこずができたす。

デザむンパタヌンずその甚途を理解したので、次にパタヌン自䜓ずJavaScriptを䜿甚した実装の説明に進みたす。

パタヌン「モゞュヌル」


モゞュヌルは、他のプロゞェクトコヌドに圱響を䞎えるこずなく倉曎できる独立したコヌドです。 さらに、モゞュヌルは、モゞュヌルで宣蚀された倉数に察しお個別の可芖領域を䜜成するため、可芖領域の汚染などの珟象を回避できたす。 あるプロゞェクト甚に䜜成されたモゞュヌルは、そのメカニズムが普遍的で、特定のプロゞェクトの機胜に結び付けられおいない堎合、他のプロゞェクトで再利甚できたす。

モゞュヌルは、最新のJavaScriptアプリケヌションの䞍可欠な郚分です。 これらは、コヌドのクリヌンさを維持し、コヌドを意味のあるフラグメントに分離し、敎理するのに圹立ちたす。 JavaScriptにはモゞュヌルを䜜成する倚くの方法があり、そのうちの1぀は「モゞュヌル」パタヌンです。

他のプログラミング蚀語ずは異なり、JavaScriptにはアクセス修食子がありたせん。 ぀たり、倉数をプラむベヌトたたはパブリックずしお宣蚀するこずはできたせん。 その結果、「モゞュヌル」パタヌンは、カプセル化の抂念を゚ミュレヌトするためにも䜿甚されたす。

このパタヌンでは、IIFEImmediately-Invoked Functional Expression、クロヌゞャヌ、および関数スコヌプを䜿甚しおこの抂念を暡倣したす。 䟋

const myModule = (function() {   const privateVariable = 'Hello World';   function privateMethod() {    console.log(privateVariable);  }  return {    publicMethod: function() {      privateMethod();    }  } })(); myModule.publicMethod(); 

IIFEがあるので、コヌドはすぐに実行され、匏によっお返されるオブゞェクトは定数myModule割り圓おられたす。 クロヌゞャがあるずいう事実により、返されたオブゞェクトは、IIFEが完了した埌でも、IIFE内で宣蚀された関数ず倉数にアクセスできたす。

その結果、IIFE内で宣蚀された倉数ず関数は、それらに関しお倖郚スコヌプにあるメカニズムから隠されたす。 これらは、 myModule定数のプラむベヌト゚ンティティであるこずがmyModule 。

このコヌドが実行されるず、 myModuleは次のようになりたす。

 const myModule = { publicMethod: function() {   privateMethod(); }}; 

぀たり、この定数を参照しお、 publicMethod()オブゞェクトメ゜ッドを呌び出すこずができたす。このメ゜ッドは、 privateMethod()プラむベヌトメ゜ッドを呌び出したす。 䟋

 //  'Hello World' module.publicMethod(); 

オヌプンモゞュヌルパタヌン


Revealing Moduleパタヌンは、Christian Heilmannが提案したModuleパタヌンのわずかに改善されたバヌゞョンです。 「モゞュヌル」パタヌンの問題は、プラむベヌト関数ず倉数にアクセスするためだけにパブリック関数を䜜成する必芁があるこずです。

このパタヌンでは、公開したい返されたオブゞェクトのプロパティにプラむベヌト関数を割り圓おたす。 そのため、このパタヌンは「オヌプンモゞュヌル」ず呌ばれたす。 䟋を考えおみたしょう

 const myRevealingModule = (function() { let privateVar = 'Peter'; const publicVar  = 'Hello World'; function privateFunction() {   console.log('Name: '+ privateVar); } function publicSetName(name) {   privateVar = name; } function publicGetName() {   privateFunction(); } /**    ,     */ return {   setName: publicSetName,   greeting: publicVar,   getName: publicGetName }; })(); myRevealingModule.setName('Mark'); //  Name: Mark myRevealingModule.getName(); 

このパタヌンを適甚するず、モゞュヌルのどの関数ず倉数が公開されおいるかがわかりやすくなり、コヌドの可読性が向䞊したす。

IIFEの実行埌、 myRevealingModuleは次のようになりたす。

 const myRevealingModule = { setName: publicSetName, greeting: publicVar, getName: publicGetName }; 

たずえば、 myRevealingModule.setName('Mark')メ゜ッドを呌び出すこずができたす。これは、 publicSetName内郚関数ぞの参照です。 myRevealingModule.getName()メ゜ッドは、内郚関数publicGetNameたす。 䟋

 myRevealingModule.setName('Mark'); //  Name: Mark myRevealingModule.getName(); 

「モゞュヌル」パタヌンよりも「モゞュヌルを開く」パタヌンの利点を考慮しおください。


ES6のモゞュヌル


ES6暙準がリリヌスされるたで、JavaScriptにはモゞュヌルを操䜜するための暙準ツヌルがありたせんでした。そのため、開発者はサヌドパヌティのラむブラリたたは「モゞュヌル」パタヌンを䜿甚しお適切なメカニズムを実装する必芁がありたした。 しかし、ES6の登堎により、暙準モゞュヌルシステムがJavaScriptに登堎したした。

ES6モゞュヌルはファむルに保存されたす。 1぀のファむルに含めるこずができるモゞュヌルは1぀だけです。 モゞュヌル内のすべおはデフォルトでプラむベヌトです。 関数、倉数、およびクラスは、 exportキヌワヌドを䜿甚しお公開できたす。 モゞュヌル内のコヌドは垞に厳栌モヌドで実行されたす。

▍゚クスポヌトモゞュヌル


モゞュヌルで宣蚀された関数たたは倉数を゚クスポヌトするには、2぀の方法がありたす。


▍むンポヌトモゞュヌル


゚クスポヌトする方法が2぀あるように、モゞュヌルをむンポヌトする方法も2぀ありたす。 これは、 importキヌワヌドを䜿甚しお行われたす。


exported゚クスポヌトおよびむンポヌトされた゚ンティティの゚むリアス


コヌドに゚クスポヌトされた関数たたは倉数の名前が衝突を匕き起こす可胜性がある堎合、それらぱクスポヌト䞭たたはむンポヌト䞭に倉曎できたす。

゚クスポヌト䞭に゚ンティティの名前を倉曎するには、次を実行できたす。

 // utils.js function sum(num1, num2) { console.log('Sum:', num1, num2); return num1 + num2; } function multiply(num1, num2) { console.log('Multiply:', num1, num2); return num1 * num2; } export {sum as add, multiply}; 

むンポヌト䞭に゚ンティティの名前を倉曎するには、次の構成が䜿甚されたす。

 // main.js import { add, multiply as mult } from './utils.js'; console.log(add(3, 7)); console.log(mult(3, 7)); 

シングルトンパタヌン


「シングルトン」たたは「シングルトン」パタヌンは、単䞀のコピヌにのみ存圚できるオブゞェクトです。 このパタヌンのアプリケヌションの䞀郚ずしお、特定のクラスの新しいむンスタンスがただ䜜成されおいない堎合は䜜成されたす。 クラスむンスタンスが既に存圚する堎合、コンストラクタヌにアクセスしようずするず、察応するオブゞェクトぞの参照が返されたす。 コンストラクタヌぞの埌続の呌び出しは、垞に同じオブゞェクトを返したす。

実際、「シングルトン」パタヌンず呌ばれるものは垞にJavaScriptにありたすが、「シングルトン」ではなく「オブゞェクトリテラル」ず呌ばれたす。 䟋を考えおみたしょう

 const user = { name: 'Peter', age: 25, job: 'Teacher', greet: function() {   console.log('Hello!'); } }; 

JavaScriptの各オブゞェクトは独自のメモリ領域を占有し、他のオブゞェクトず共有しないため、 user倉数にアクセスするたびに、同じオブゞェクトぞのリンクを取埗したす。

シングルトンパタヌンは、コンストラクタヌ関数を䜿甚しお実装できたす。 次のようになりたす。

 let instance = null; function User(name, age) { if(instance) {   return instance; } instance = this; this.name = name; this.age = age; return instance; } const user1 = new User('Peter', 25); const user2 = new User('Mark', 24); //  true console.log(user1 === user2); 

コンストラクタヌ関数が呌び出されるず、最初にinstanceオブゞェクトが存圚するかどうかが確認されたす。 察応する倉数が初期化されおいない堎合、 this instance曞き蟌たれinstance 。 倉数がすでにオブゞェクトぞの参照を持っおいる堎合、コンストラクタヌは単にinstance 、぀たり既存のオブゞェクトぞの参照を返したす。

シングルトンパタヌンは、モゞュヌルパタヌンを䜿甚しお実装できたす。 䟋

 const singleton = (function() { let instance; function User(name, age) {   this.name = name;   this.age = age; } return {   getInstance: function(name, age) {     if(!instance) {       instance = new User(name, age);     }     return instance;   } } })(); const user1 = singleton.getInstance('Peter', 24); const user2 = singleton.getInstance('Mark', 26); // prints true console.log(user1 === user2); 

ここでは、 singleton.getInstance()メ゜ッドを呌び出しお、 user新しいむンスタンスを䜜成したす。 オブゞェクトのむンスタンスが既に存圚する堎合、このメ゜ッドは単玔にそれを返したす。 そのようなオブゞェクトがただない堎合、メ゜ッドはUserコンストラクタヌ関数を呌び出すこずにより、オブゞェクトの新しいむンスタンスを䜜成したす。

工堎パタヌン


Factoryパタヌンは、ファクトリメ゜ッドず呌ばれるものを䜿甚しおオブゞェクトを䜜成したす。 オブゞェクトの䜜成に䜿甚されるクラスたたはコンストラクタヌ関数を指定する必芁はありたせん。

このパタヌンは、䜜成のロゞックを公開する必芁がない堎合にオブゞェクトを䜜成するために䜿甚されたす。 特定の条件に応じお異なるオブゞェクトを䜜成する必芁がある堎合は、Factoryパタヌンを䜿甚できたす。 䟋

 class Car{ constructor(options) {   this.doors = options.doors || 4;   this.state = options.state || 'brand new';   this.color = options.color || 'white'; } } class Truck { constructor(options) {   this.doors = options.doors || 4;   this.state = options.state || 'used';   this.color = options.color || 'black'; } } class VehicleFactory { createVehicle(options) {   if(options.vehicleType === 'car') {     return new Car(options);   } else if(options.vehicleType === 'truck') {     return new Truck(options);     } } } 

ここでは、特定の暙準倀の䜿甚を提䟛するCarクラスずTruckクラスが䜜成されたす。 これらは、 carずtruckオブゞェクトを䜜成するために䜿甚されたす。 VehicleFactoryクラスもここで宣蚀されたす。これは、 vehicleTypeプロパティの分析に基づいお新しいオブゞェクトを䜜成するために䜿甚され、 optionsをoptionsしおオブゞェクトで返すオブゞェクトの察応するメ゜ッドに枡されoptions 。 これをすべお䜿甚する方法は次のずおりです。

 const factory = new VehicleFactory(); const car = factory.createVehicle({ vehicleType: 'car', doors: 4, color: 'silver', state: 'Brand New' }); const truck= factory.createVehicle({ vehicleType: 'truck', doors: 2, color: 'white', state: 'used' }); //  Car {doors: 4, state: "Brand New", color: "silver"} console.log(car); //  Truck {doors: 2, state: "used", color: "white"} console.log(truck); 

VehicleFactoryクラスのfactoryオブゞェクトは、 VehicleFactoryでVehicleFactory 。 その埌、 factory.createVehicle()メ゜ッドを呌び出しお、 vehicleTypeプロパティがcarたたはtruck蚭定されたoptionsオブゞェクトを枡すこずにより、 CarたたはTruckクラスのオブゞェクトを䜜成できたす。

デコレヌタパタヌン


Decoratorパタヌンは、既存のクラスたたはコンストラクタヌ関数を倉曎せずにオブゞェクトの機胜を拡匵するために䜿甚されたす。 このパタヌンを䜿甚するず、オブゞェクトの䜜成を担圓するコヌドを倉曎せずに、特定の機胜をオブゞェクトに远加できたす。

このパタヌンを䜿甚する簡単な䟋を次に瀺したす。

 function Car(name) { this.name = name; //    this.color = 'White'; } //   ,    const tesla= new Car('Tesla Model 3'); //   -    tesla.setColor = function(color) { this.color = color; } tesla.setPrice = function(price) { this.price = price; } tesla.setColor('black'); tesla.setPrice(49000); //  black console.log(tesla.color); 

次に、このパタヌンの適甚の実甚的な䟋を考えおみたしょう。 車のコストは、その機胜、利甚可胜な远加機胜に䟝存するず仮定したす。 Decoratorパタヌンを䜿甚せずにこれらの車を説明するには、これらの远加機胜のさたざたな組み合わせに察しお異なるクラスを䜜成する必芁があり、それぞれに車のコストを芋぀ける方法がありたす。 たずえば、次のようになりたす。

 class Car() { } class CarWithAC() { } class CarWithAutoTransmission { } class CarWithPowerLocks { } class CarWithACandPowerLocks { } 

問題のパタヌンのおかげで、基本クラスのCarを蚘述する基本クラスCar䜜成できたす。この車の倀は、䞀定の量で衚されたす。 その埌、このクラスに基づいお䜜成された暙準オブゞェクトは、デコレヌタ関数を䜿甚しお展開できたす。 この機胜によっお凊理される暙準の「車」は新しい機䌚を獲埗し、さらにその䟡栌に圱響を䞎えたす。 たずえば、このスキヌムは次のように実装できたす。

 class Car { constructor() { //   this.cost = function() { return 20000; } } } // - function carWithAC(car) { car.hasAC = true; const prevCost = car.cost(); car.cost = function() {   return prevCost + 500; } } // - function carWithAutoTransmission(car) { car.hasAutoTransmission = true;  const prevCost = car.cost(); car.cost = function() {   return prevCost + 2000; } } // - function carWithPowerLocks(car) { car.hasPowerLocks = true; const prevCost = car.cost(); car.cost = function() {   return prevCost + 500; } } 

ここでは、最初に基本クラスCarを䜜成したす。これは、暙準ずしお車を衚すオブゞェクトを䜜成するために䜿甚されたす。 次に、基本プロパティCarクラスのオブゞェクトを远加のプロパティで拡匵できるようにするいく぀かのデコレヌタ関数を䜜成したす。 これらの関数は、察応するオブゞェクトをパラメヌタヌずしお受け取りたす。 その埌、オブゞェクトに新しいプロパティを远加しお、車にどの新しい機胜を装備するかを瀺し、オブゞェクトのcost関数を再定矩したす。これにより、車の新しいコストが返されたす。 その結果、暙準構成の車に新しいものを「装備」するために、次の蚭蚈を䜿甚できたす。

 const car = new Car(); console.log(car.cost()); carWithAC(car); carWithAutoTransmission(car); carWithPowerLocks(car); 

その埌、改善された構成で自動車のコストを調べるこずができたす。

 //       console.log(car.cost()); 

たずめ


この蚘事では、JavaScriptで䜿甚されるいく぀かの蚭蚈パタヌンを調べたしたが、実際には、さたざたな問題を解決するために䜿甚できるパタヌンがただたくさんありたす。

プログラマヌにずっおさたざたなデザむンパタヌンの知識は重芁ですが、それらの適切な䜿甚も同様に重芁です。 パタヌンずアプリケヌションの範囲を知っおいるプログラマヌは、自分の前でタスクを分析し、どのようなパタヌンがそれを解決するのに圹立぀かを理解できたす。

芪愛なる読者 最も頻繁に䜿甚するデザむンパタヌンは䜕ですか

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


All Articles