JavaScript では、さまざまな一般的なデザイン パターンを利用できます。以下に 13 の一般的な JavaScript 設計パターンを示します。
JavaScript デザインパターン
- シングルトンパターン
- ファクトリーパターン
- 抽象的な工場パターン
- 試作パターン
- ビルダーパターン
- アダプターのパターン
- デコレータパターン
- オブザーバーパターン
- パブリッシュ/サブスクライブ パターン
- 戦略パターン
- 状態パターン
- プロキシパターン
- イテレータパターン
簡単な説明
各デザイン パターンには独自の特徴と用途があります。以下に各デザイン パターンの簡単な説明を示します。
-
シングルトン パターン:
シングルトン パターンは、クラスを 1 つのインスタンスのみに制限し、グローバル アクセス ポイントを提供するために使用されます。 -
ファクトリ パターン:
ファクトリ パターンは、コンストラクターを直接呼び出すのではなく、ファクトリ メソッドを使用してオブジェクトを作成します。 -
抽象ファクトリ パターン:
抽象ファクトリ パターンは、具体的なクラスを指定せずに関連オブジェクトを作成するためのインターフェイスを提供します。 -
プロトタイプ パターン:
プロトタイプ パターンは、既存のオブジェクト クローンに基づいて新しいオブジェクトを作成し、直接インスタンス化を回避します。 -
ビルダー パターン:
ビルダー パターンは、オブジェクトを段階的に構築することで複雑なオブジェクトを作成するために使用されます。 -
アダプター パターン:
アダプター パターンは、互換性のないインターフェイスを互換性のあるインターフェイスに変換するために使用されます。 -
デコレーター パターン:
デコレーター パターンを使用すると、元のオブジェクトを変更せずに、オブジェクトに新しい機能を追加できます。 -
オブザーバー パターン (オブザーバー パターン):
オブザーバー パターンは 1 対多の依存関係を定義するため、複数のオブザーバー オブジェクトがサブジェクト オブジェクトを同時にリッスンできます。 -
パブリッシュ/サブスクライブ パターン:
パブリッシュ/サブスクライブ パターンを使用すると、複数のサブスクライバーが特定のイベントをサブスクライブでき、イベントが発生すると、パブリッシャーはすべてのサブスクライバーに通知します。 -
戦略パターン: 戦略
パターンは、相互に置き換えることができる一連のアルゴリズムを定義し、アルゴリズムの選択と使用をクライアントから独立させます。 -
状態パターン:
状態パターンを使用すると、オブジェクトの内部状態が変化したときにオブジェクトの動作を変更できるようになり、オブジェクトがそのクラスを変更したように見えます。 -
プロキシ パターン:
プロキシ パターンは、実際のオブジェクトへのアクセスを制御するプロキシ オブジェクトを提供し、プロキシ オブジェクトを通じて追加機能を追加できます。 -
イテレータ パターン:
イテレータ パターンは、集約オブジェクトの内部構造を公開せずに、集約オブジェクトの要素にアクセスする方法を提供します。
これらの設計パターンは、JavaScript コードをより適切に整理および設計し、コードの保守性とスケーラビリティを向上させるのに役立ちます。どの設計パターンを使用するかは、問題の性質と要件によって異なります。
コードの詳細な説明:
以下は、各設計パターンのコードの詳細な説明です。
- シングルトン パターン:
var Singleton = (function () {
var instance;
function createInstance() {
var object = new Object("I am the instance");
return object;
}
return {
getInstance: function () {
if (!instance) {
instance = createInstance();
}
return instance;
},
};
})();
var instance1 = Singleton.getInstance();
var instance2 = Singleton.getInstance();
console.log(instance1 === instance2); // Output: true
- 工場出荷時のパターン:
function ShapeFactory() {
}
ShapeFactory.prototype.createShape = function (type) {
if (type === "circle") {
return new Circle();
} else if (type === "rectangle") {
return new Rectangle();
} else if (type === "triangle") {
return new Triangle();
}
};
function Circle() {
this.type = "circle";
}
function Rectangle() {
this.type = "rectangle";
}
function Triangle() {
this.type = "triangle";
}
var factory = new ShapeFactory();
var circle = factory.createShape("circle");
var rectangle = factory.createShape("rectangle");
var triangle = factory.createShape("triangle");
console.log(circle.type); // Output: circle
console.log(rectangle.type); // Output: rectangle
console.log(triangle.type); // Output: triangle
- 抽象的な工場パターン:
function FurnitureFactory() {
}
FurnitureFactory.prototype.createChair = function () {
throw new Error("This method should be overridden");
};
FurnitureFactory.prototype.createTable = function () {
throw new Error("This method should be overridden");
};
function ModernFurnitureFactory() {
}
ModernFurnitureFactory.prototype = Object.create(FurnitureFactory.prototype);
ModernFurnitureFactory.prototype.constructor = ModernFurnitureFactory;
ModernFurnitureFactory.prototype.createChair = function () {
return new ModernChair();
};
ModernFurnitureFactory.prototype.createTable = function () {
return new ModernTable();
};
function VictorianFurnitureFactory() {
}
VictorianFurnitureFactory.prototype = Object.create(FurnitureFactory.prototype);
VictorianFurnitureFactory.prototype.constructor = VictorianFurnitureFactory;
VictorianFurnitureFactory.prototype.createChair = function () {
return new VictorianChair();
};
VictorianFurnitureFactory.prototype.createTable = function () {
return new VictorianTable();
};
function ModernChair() {
this.type = "modern chair";
}
function ModernTable() {
this.type = "modern table";
}
function VictorianChair() {
this.type = "victorian chair";
}
function VictorianTable() {
this.type = "victorian table";
}
var modernFactory = new ModernFurnitureFactory();
var modernChair = modernFactory.createChair();
var modernTable = modernFactory.createTable();
var victorianFactory = new VictorianFurnitureFactory();
var victorianChair = victorianFactory.createChair();
var victorianTable = victorianFactory.createTable();
console.log(modernChair.type); // Output: modern chair
console.log(modernTable.type); // Output: modern table
console.log(victorianChair.type); // Output: victorian chair
console.log(victorianTable.type); // Output: victorian table
- プロトタイプパターン:
function Shape() {
}
Shape.prototype.clone = function () {
throw new Error("This method should be overridden");
};
function Circle(radius) {
this.radius = radius;
}
Circle.prototype = Object.create(Shape.prototype);
Circle.prototype.constructor = Circle;
Circle.prototype.clone = function () {
return new Circle(this.radius);
};
var circle = new Circle(5);
var clonedCircle = circle.clone();
console.log(clonedCircle.radius); // Output: 5
- ビルダーパターン:
function Car() {
this.color = "";
this.model = "";
this.engine = "";
}
function CarBuilder() {
this.car = new Car();
}
CarBuilder.prototype.setColor = function (color) {
this.car.color = color;
return this;
};
CarBuilder.prototype.setModel = function (model) {
this.car.model = model;
return this;
};
CarBuilder.prototype.setEngine = function (engine) {
this.car.engine = engine;
return this;
};
CarBuilder.prototype.build = function () {
return this.car;
};
var car = new CarBuilder()
.setColor("red")
.setModel("sedan")
.setEngine("V8")
.build();
console.log(car.color); // Output: red
console.log(car.model); // Output: sedan
console.log(car.engine); // Output: V8
- アダプターのパターン:
function MediaPlayer() {
}
MediaPlayer.prototype.play = function (audioType, fileName) {
throw new Error("This method should be overridden");
};
function AudioPlayer() {
}
AudioPlayer.prototype = Object.create(MediaPlayer.prototype);
AudioPlayer.prototype.constructor = AudioPlayer;
AudioPlayer.prototype.play = function (audioType, fileName) {
if (audioType === "mp3") {
console.log("Playing mp3 file: " + fileName);
} else {
throw new Error("Unsupported audio type: " + audioType);
}
};
function MediaAdapter(audioType) {
if (audioType === "mp3") {
this.audioPlayer = new AudioPlayer();
}
}
MediaAdapter.prototype.play = function (audioType, fileName) {
if (audioType === "mp3") {
this.audioPlayer.play(audioType, fileName);
} else {
throw new Error("Unsupported audio type: " + audioType);
}
};
function AudioPlayerAdapter() {
}
AudioPlayerAdapter.prototype = Object.create(MediaPlayer.prototype);
AudioPlayerAdapter.prototype.constructor = AudioPlayerAdapter;
AudioPlayerAdapter.prototype.play = function (audioType, fileName) {
if (audioType === "mp3") {
var mediaAdapter = new MediaAdapter(audioType);
mediaAdapter.play(audioType, fileName);
} else {
throw new Error("Unsupported audio type: " + audioType);
}
};
var audioPlayer = new AudioPlayerAdapter();
audioPlayer.play("mp3", "song.mp3"); // Output: Playing mp3 file: song.mp3
- デコレータパターン:
function Pizza() {
}
Pizza.prototype.getDescription = function () {
throw new Error("This method should be overridden");
};
Pizza.prototype.cost = function () {
throw new Error("This method should be overridden");
};
function MargheritaPizza() {
}
MargheritaPizza.prototype = Object.create(Pizza.prototype);
MargheritaPizza.prototype.constructor = MargheritaPizza;
MargheritaPizza.prototype.getDescription = function () {
return "Margherita Pizza";
};
MargheritaPizza.prototype.cost = function () {
return 10;
};
function PizzaDecorator(pizza) {
this.pizza = pizza;
}
PizzaDecorator.prototype.getDescription = function () {
return this.pizza.getDescription();
};
PizzaDecorator.prototype.cost = function () {
return this.pizza.cost();
};
function ExtraCheeseDecorator(pizza) {
PizzaDecorator.call(this, pizza);
}
ExtraCheeseDecorator.prototype = Object.create(PizzaDecorator.prototype);
ExtraCheeseDecorator.prototype.constructor = ExtraCheeseDecorator;
ExtraCheeseDecorator.prototype.getDescription = function () {
return this.pizza.getDescription() + ", Extra Cheese";
};
ExtraCheeseDecorator.prototype.cost = function () {
return this.pizza.cost() + 2;
};
var margheritaPizza = new MargheritaPizza();
var extraCheesePizza = new ExtraCheeseDecorator(margheritaPizza);
console.log(extraCheesePizza.getDescription()); // Output: Margherita Pizza, Extra Cheese
console.log(extraCheesePizza.cost()); // Output: 12
- オブザーバーパターン:
function Subject() {
this.observers = [];
}
Subject.prototype.subscribe = function (observer) {
this.observers.push(observer);
};
Subject.prototype.unsubscribe = function (observer) {
var index = this.observers.indexOf(observer);
if (index !== -1) {
this.observers.splice(index, 1);
}
};
Subject.prototype.notify = function () {
for (var i = 0; i < this.observers.length; i++) {
this.observers[i].update();
}
};
function Observer(name) {
this.name = name;
}
Observer.prototype.update = function () {
console.log(this.name + " received an update");
};
var subject = new Subject();
var observer1 = new Observer("Observer 1");
var observer2 = new Observer("Observer 2");
var observer3 = new Observer("Observer 3");
subject.subscribe(observer1);
subject.subscribe(observer2);
subject.subscribe(observer3);
subject.notify();
// Output: Observer 1 received an update
// Output: Observer 2 received an update
// Output: Observer 3 received an update
- パブリッシュ/サブスクライブ パターン:
function PubSub() {
this.subscribers = {
};
}
PubSub.prototype.subscribe = function (event, callback) {
if (!this.subscribers[event]) {
this.subscribers[event] = [];
}
this.subscribers[event].push(callback);
};
PubSub.prototype.unsubscribe = function (event,callback) {
if (this.subscribers[event]) {
var index = this.subscribers[event].indexOf(callback);
if (index !== -1) {
this.subscribers[event].splice(index, 1);
}
}
};
PubSub.prototype.publish = function (event, data) {
if (this.subscribers[event]) {
this.subscribers[event].forEach(function (callback) {
callback(data);
});
}
};
var pubSub = new PubSub();
var callback1 = function (data) {
console.log("Callback 1 received data: " + data);
};
var callback2 = function (data) {
console.log("Callback 2 received data: " + data);
};
pubSub.subscribe("event1", callback1);
pubSub.subscribe("event1", callback2);
pubSub.publish("event1", "Hello World");
// Output: Callback 1 received data: Hello World
// Output: Callback 2 received data: Hello World
pubSub.unsubscribe("event1", callback2);
pubSub.publish("event1", "Hello Again");
// Output: Callback 1 received data: Hello Again
10. 状态模式 (State Pattern):
```javascript
function TrafficLight() {
this.currentState = new RedLightState(this);
}
TrafficLight.prototype.changeState = function (state) {
this.currentState = state;
};
TrafficLight.prototype.start = function () {
this.currentState.start();
};
function RedLightState(trafficLight) {
this.trafficLight = trafficLight;
}
RedLightState.prototype.start = function () {
console.log("Red Light - Stop");
this.trafficLight.changeState(new GreenLightState(this.trafficLight));
};
function GreenLightState(trafficLight) {
this.trafficLight = trafficLight;
}
GreenLightState.prototype.start = function () {
console.log("Green Light - Go");
this.trafficLight.changeState(new YellowLightState(this.trafficLight));
};
function YellowLightState(trafficLight) {
this.trafficLight = trafficLight;
}
YellowLightState.prototype.start = function () {
console.log("Yellow Light - Prepare to Stop");
this.trafficLight.changeState(new RedLightState(this.trafficLight));
};
var trafficLight = new TrafficLight();
trafficLight.start();
// Output: Red Light - Stop
trafficLight.start();
// Output: Green Light - Go
trafficLight.start();
// Output: Yellow Light - Prepare to Stop
trafficLight.start();
// Output: Red Light - Stop
- ヌルオブジェクトパターン:
function Animal(name) {
this.name = name;
}
Animal.prototype.makeSound = function () {
throw new Error("This method should be overridden");
};
function Dog(name) {
Animal.call(this, name);
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.makeSound = function () {
console.log(this.name + " barks");
};
function NullAnimal() {
}
NullAnimal.prototype.makeSound = function () {
console.log("No sound");
};
function AnimalFactory() {
}
AnimalFactory.prototype.createAnimal = function (name) {
if (name === "dog") {
return new Dog(name);
} else {
return new NullAnimal();
}
};
var animalFactory = new AnimalFactory();
var animal1 = animalFactory.createAnimal("dog");
animal1.makeSound(); // Output: dog barks
var animal2 = animalFactory.createAnimal("cat");
animal2.makeSound(); // Output: No sound
- テンプレートメソッドパターン:
function Beverage() {
}
Beverage.prototype.prepare = function () {
this.boilWater();
this.brew();
this.pourInCup();
this.addCondiments();
};
Beverage.prototype.boilWater = function () {
console.log("Boiling water");
};
Beverage.prototype.pourInCup = function () {
console.log("Pouring into cup");
};
Beverage.prototype.brew = function () {
throw new Error("This method should be overridden");
};
Beverage.prototype.addCondiments = function () {
throw new Error("This method should be overridden");
};
function Coffee() {
}
Coffee.prototype = Object.create(Beverage.prototype);
Coffee.prototype.constructor = Coffee;
Coffee.prototype.brew = function () {
console.log("Brewing coffee");
};
Coffee.prototype.addCondiments = function () {
console.log("Adding sugar and milk");
};
function Tea() {
}
Tea.prototype = Object.create(Beverage.prototype);
Tea.prototype.constructor = Tea;
Tea.prototype.brew = function () {
console.log("Steeping tea");
};
Tea.prototype.addCondiments = function () {
console.log("Adding lemon");
};
var coffee = new Coffee();
coffee.prepare();
// Output:
// Boiling water
// Brewing coffee
// Pouring into cup
// Adding sugar and milk
var tea = new Tea();
tea.prepare();
// Output:
// Boiling water
// Steeping tea
// Pouring into cup
// Adding lemon
一般的なデザインパターンの例をいくつか示します。