これを想像してください: あなたはキッチンに立って、おいしい食事を作る準備をしています。材料はすべて揃っていますが、従うべきレシピがありません。実験を始めますが、すぐに圧倒されてしまいます。ある料理に塩を入れすぎて、別の料理が焦げてしまいます。明確な計画がないと、料理は当てずっぽうになってしまいます。
ソフトウェアの作成はまさにこのような感じです。すべてのツールとノウハウはありますが、十分に体系化されたアプローチがなければ、新しい機能を追加するのはイライラするパズルになる可能性があります。コードで何を行う必要があるかは理解していますが、すべてを連携させるための最善の方法を考え出していますか?そこが事態が複雑になるところです。ほんの小さなエラーが 1 つあるだけで、バグと複雑なコードで満たされた穴に落ちていくことになります。
デザイン パターンを入力します。これは、プログラマーによって長年にわたって受け継がれてきた、実績のあるレシピです。これらの再利用可能な修正は、ソフトウェア作成の難しい部分を汗をかくことなく処理するのに役立ちます。デザイン パターンとは正確には何なのか、デザイン パターンによってコーディング作業がどのように容易になるか、堅牢で保守しやすいアプリを構築するための鍵となる理由について説明します。物事をより面白くするために、説明全体を通して料理用語を使用します。なぜなら、正直に言うと、優れた料理番組が嫌いな人がいるでしょうか?
それでは、デザインパターンとは何でしょうか?より良いアプリの構築にどのように役立つのでしょうか?
デザイン パターンは、ソフトウェア設計で繰り返し発生する問題やテーマに適用できる、再利用可能なソリューション テンプレートです。これは、ソフトウェア設計の一般的な問題に取り組んでいる経験豊富な開発者による、試行錯誤されたソリューションをまとめた優れたクックブックになります。ガイドラインでは、デザイン パターンを使用すると、アプリ内で保守可能で再利用可能なコードを実現できると述べています。
デザイン パターンは、本質的に、解決する問題に応じて、創造的デザイン パターン、構造的デザイン パターン、動作的デザイン パターンの 3 つの大きなカテゴリに分類されます。
デザイン パターンは、解決する問題に基づいて 3 つのカテゴリに分類されます。それらは、創造的なデザイン パターン、構造的なデザイン パターン、および動作的なデザイン パターンです。
創造的なデザイン パターンは、オブジェクトを作成するためのメカニズムを提供します。料理番組の文脈で言えば、これらのパターンは、調理する前に材料を集めて準備することに似ています。このカテゴリに分類されるパターンには、コンストラクター、ファクトリー、抽象、プロトタイプ、シングルトン、ビルダーなどがあります。より深く理解するには、以下の 3 つの例を見てください。
1.シングルトン
何世代にもわたって特別な鍋でのみ作ることができる、ある家族の秘密のソースがあると想像してください。もちろん、鍋が違えばタレの味も同じにはなりません。これは、Singleton が行うこととほとんど同じです。つまり、クラスが 1 つのインスタンスを持つように制限される設計パターンです。
class SecretSauce { constructor() { if (SecretSauce.instance) { return SecretSauce.instance; } SecretSauce.instance = this; this.flavor = 'Umami'; } getFlavor() { return this.flavor; } } const sauce1 = new SecretSauce(); const sauce2 = new SecretSauce(); console.log(sauce1.getFlavor() === sauce2.getFlavor()); // true
2.ファクトリーメソッド
ファクトリ メソッドは、オブジェクトを作成するための汎用インターフェイスを提供し、必要なオブジェクトの種類を指定できます。私たちの料理番組では、レシピ本が工場です。作りたい料理の種類に応じて、必要なレシピ (オブジェクト) が表示されます。
// Product Classes class Pizza { constructor(size, toppings) { this.size = size; this.toppings = toppings; } prepare() { console.log(`Preparing a ${this.size} pizza with ${this.toppings.join(', ')} toppings.`); } } class Pasta { constructor(sauce, noodles) { this.sauce = sauce; this.noodles = noodles; } prepare() { console.log(`Preparing pasta with ${this.noodles} noodles and ${this.sauce} sauce.`); } } // Creator Class class RecipeBook { createDish(type, options) { let dish; if (type === 'Pizza') { dish = new Pizza(options.size, options.toppings); } else if (type === 'Pasta') { dish = new Pasta(options.sauce, options.noodles); } return dish; } } // Usage const recipeBook = new RecipeBook(); const pizzaOptions = { size: 'large', toppings: ['cheese', 'pepperoni', 'olives'] }; const pastaOptions = { sauce: 'alfredo', noodles: 'fettuccine' }; const pizza = recipeBook.createDish('Pizza', pizzaOptions); const pasta = recipeBook.createDish('Pasta', pastaOptions); pizza.prepare(); // Preparing a large pizza with cheese, pepperoni, olives toppings. pasta.prepare(); // Preparing pasta with fettuccine noodles and alfredo sauce.
ファクトリ メソッドは、環境に応じて異なるインスタンスを生成したり、多数の同様のオブジェクトを管理したりするなど、複雑なオブジェクトを作成するシナリオで役立ちます。
*3.抽象ファクトリー *
オブジェクトの一般的な使用法から実装の詳細をカプセル化します。これを説明する最も適切な方法は、ミールキットの配達サービスを検討している場合です。イタリア料理、中華料理、メキシコ料理のいずれを料理する場合でも、このサービスは、手元の料理に合わせて調整された材料とレシピをすべて配達してくれるため、すべてが完璧に適合します。
// Abstract Factory Interfaces class ItalianKitchen { createPizza(options) { return new Pizza(options.size, options.toppings); } createPasta(options) { return new Pasta(options.sauce, options.noodles); } } class MexicanKitchen { createTaco(options) { return new Taco(options.shellType, options.fillings); } createBurrito(options) { return new Burrito(options.size, options.fillings); } } // Concrete Product Classes class Pizza { constructor(size, toppings) { this.size = size; this.toppings = toppings; } prepare() { console.log(`Preparing a ${this.size} pizza with ${this.toppings.join(', ')} toppings.`); } } class Pasta { constructor(sauce, noodles) { this.sauce = sauce; this.noodles = noodles; } prepare() { console.log(`Preparing pasta with ${this.noodles} noodles and ${this.sauce} sauce.`); } } class Taco { constructor(shellType, fillings) { this.shellType = shellType; this.fillings = fillings; } prepare() { console.log(`Preparing a taco with a ${this.shellType} shell and ${this.fillings.join(', ')} fillings.`); } } class Burrito { constructor(size, fillings) { this.size = size; this.fillings = fillings; } prepare() { console.log(`Preparing a ${this.size} burrito with ${this.fillings.join(', ')} fillings.`); } } // Client Code const italianKitchen = new ItalianKitchen(); const mexicanKitchen = new MexicanKitchen(); const italianPizza = italianKitchen.createPizza({ size: 'medium', toppings: ['mozzarella', 'tomato', 'basil'] }); const mexicanTaco = mexicanKitchen.createTaco({ shellType: 'hard', fillings: ['beef', 'lettuce', 'cheese'] }); italianPizza.prepare(); // Preparing a medium pizza with mozzarella, tomato, basil toppings. mexicanTaco.prepare(); // Preparing a taco with a hard shell and beef, lettuce, cheese fillings.
構造設計パターンはオブジェクトの構成に焦点を当て、異なるオブジェクト間の関係を確立する簡単な方法を特定します。これらは、システムの一部が変更された場合でも、全体の構造が安定した状態を保つのに役立ちます。料理において、これらのパターンは、食材を組み合わせて調和のとれたおいしい料理を作るために使用する技術とツールを表しています。
このカテゴリに分類されるパターンには、デコレーター、ファサード、フライウェイト、アダプター、プロキシが含まれます。
1.ファサードパターン
Facade パターンは、より複雑なコード本体への便利な高レベルのインターフェイスを提供し、根底にある複雑さを効果的に隠します。副料理長が料理長の複雑なタスクを簡素化しているところを想像してください。副料理長は食材を集め、準備し、料理長が料理の最終仕上げに集中できるようにすべてを整理します
// Complex Subsystem class IngredientPrep { chop(ingredient) { console.log(`Chopping ${ingredient}.`); } measure(amount, ingredient) { console.log(`Measuring ${amount} of ${ingredient}.`); } } class CookingProcess { boil(waterAmount) { console.log(`Boiling ${waterAmount} of water.`); } bake(temp, duration) { console.log(`Baking at ${temp} degrees for ${duration} minutes.`); } } class Plating { arrangeDish(dish) { console.log(`Arranging the ${dish} on the plate.`); } garnish(garnish) { console.log(`Adding ${garnish} as garnish.`); } } // Facade Class class SousChef { constructor() { this.ingredientPrep = new IngredientPrep(); this.cookingProcess = new CookingProcess(); this.plating = new Plating(); } prepareDish(dishName) { console.log(`Starting to prepare ${dishName}...`); this.ingredientPrep.chop('vegetables'); this.ingredientPrep.measure('2 cups', 'flour'); this.cookingProcess.boil('1 liter'); this.cookingProcess.bake(180, 30); this.plating.arrangeDish(dishName); this.plating.garnish('parsley'); console.log(`${dishName} is ready!`); } } // Client Code const sousChef = new SousChef(); sousChef.prepareDish('Lasagna'); // Output: // Starting to prepare Lasagna... // Chopping vegetables. // Measuring 2 cups of flour. // Boiling 1 liter of water. // Baking at 180 degrees for 30 minutes. // Arranging the Lasagna on the plate. // Adding parsley as garnish. // Lasagna is ready!
2. Decorator
The Decorator pattern is used to modify existing systems by adding features to objects without significantly altering the underlying code. If our applications require many distinct types of objects, this pattern is ideal. For instance, when making coffee, we start with a basic cup and then dynamically add ingredients like milk, sugar, or whipped cream. The Decorator pattern lets us add the base coffee without changing the core recipe.
// Base Component class Coffee { constructor() { this.description = 'Basic Coffee'; } getDescription() { return this.description; } cost() { return 2; // Base cost for a simple coffee } } // Decorator Class class CoffeeDecorator { constructor(coffee) { this.coffee = coffee; } getDescription() { return this.coffee.getDescription(); } cost() { return this.coffee.cost(); } } // Concrete Decorators class Milk extends CoffeeDecorator { constructor(coffee) { super(coffee); } getDescription() { return `${this.coffee.getDescription()}, Milk`; } cost() { return this.coffee.cost() + 0.5; } } class Sugar extends CoffeeDecorator { constructor(coffee) { super(coffee); } getDescription() { return `${this.coffee.getDescription()}, Sugar`; } cost() { return this.coffee.cost() + 0.2; } } class WhippedCream extends CoffeeDecorator { constructor(coffee) { super(coffee); } getDescription() { return `${this.coffee.getDescription()}, Whipped Cream`; } cost() { return this.coffee.cost() + 0.7; } } // Client Code let myCoffee = new Coffee(); console.log(`${myCoffee.getDescription()} costs $${myCoffee.cost()}`); // Basic Coffee costs $2 myCoffee = new Milk(myCoffee); console.log(`${myCoffee.getDescription()} costs $${myCoffee.cost()}`); // Basic Coffee, Milk costs $2.5 myCoffee = new Sugar(myCoffee); console.log(`${myCoffee.getDescription()} costs $${myCoffee.cost()}`); // Basic Coffee, Milk, Sugar costs $2.7 myCoffee = new WhippedCream(myCoffee); console.log(`${myCoffee.getDescription()} costs $${myCoffee.cost()}`); // Basic Coffee, Milk, Sugar, Whipped Cream costs $3.4
3. Flyweight
The Flyweight pattern is a classical structural solution for optimizing code that is repetitive, slow, and inefficiently shares data. It aims to minimize memory in use in an application by sharing as much data as possible with related objects. Think of common ingredients like salt, pepper, and olive oil that are used in many dishes. Instead of having separate instances of these ingredients for each dish, they are shared across dishes to save resources. For example, you put salt on fried chicken and beef stew from the same jar.
// Flyweight Class class Ingredient { constructor(name) { this.name = name; } use() { console.log(`Using ${this.name}.`); } } // Flyweight Factory class IngredientFactory { constructor() { this.ingredients = {}; } getIngredient(name) { if (!this.ingredients[name]) { this.ingredients[name] = new Ingredient(name); } return this.ingredients[name]; } getTotalIngredientsMade() { return Object.keys(this.ingredients).length; } } // Client Code const ingredientFactory = new IngredientFactory(); const salt1 = ingredientFactory.getIngredient('Salt'); const salt2 = ingredientFactory.getIngredient('Salt'); const pepper = ingredientFactory.getIngredient('Pepper'); salt1.use(); // Using Salt. salt2.use(); // Using Salt. pepper.use(); // Using Pepper. console.log(ingredientFactory.getTotalIngredientsMade()); // 2, Salt and Pepper were created only once console.log(salt1 === salt2); // true, Salt is reused
Behavioral patterns focus on improving or streamlining the communication between disparate objects in a system. They identify common communication patterns among objects and provide solutions that distribute the communication responsibility among different objects, thereby increasing communication flexibility. In a cooking show, behavioral design patterns are the way we cook the dish, the process of cooking, and how various parts of the kitchen interact with each other to create the final dish. Some of the behavioral patterns are Iterator, Mediator, Observer, and Visitor.
1.Observer
The Observer pattern is used to notify components of state changes. When a subject needs to inform observers about a change, it broadcasts a notification. If an observer no longer wishes to receive updates, they can be removed from the list of observers. For example, once the head chef finishes preparing a dish, all the assistant chefs need to be notified to begin their tasks, such as plating or garnishing. The Observer pattern allows multiple chefs (observers) to be notified when the head chef (subject) completes a dish.
// Subject Class class HeadChef { constructor() { this.chefs = []; this.dishReady = false; } addObserver(chef) { this.chefs.push(chef); } removeObserver(chef) { this.chefs = this.chefs.filter(c => c !== chef); } notifyObservers() { if (this.dishReady) { this.chefs.forEach(chef => chef.update(this.dishName)); } } prepareDish(dishName) { this.dishName = dishName; console.log(`HeadChef: Preparing ${dishName}...`); this.dishReady = true; this.notifyObservers(); } } // Observer Class class Chef { constructor(name) { this.name = name; } update(dishName) { console.log(`${this.name}: Received notification - ${dishName} is ready!`); } } // Client Code const headChef = new HeadChef(); const chef1 = new Chef('Chef A'); const chef2 = new Chef('Chef B'); headChef.addObserver(chef1); headChef.addObserver(chef2); headChef.prepareDish('Beef Wellington'); // Output: // HeadChef: Preparing Beef Wellington... // Chef A: Received notification - Beef Wellington is ready! // Chef B: Received notification - Beef Wellington is ready!
2. Mediator
The Mediator pattern allows one object to be in charge of the communication between several other objects when an event occurs. While it does sound similar to the Observer pattern, the key difference is that the Mediator handles communication between objects rather than just broadcasting changes. For example, let's think of our kitchen with its grill, bakery, and garnish station sections. A kitchen coordinator (mediator) handles the communication so that all the preparations are done on time.
// Mediator Class class KitchenCoordinator { notify(sender, event) { if (event === 'dishPrepared') { console.log(`Coordinator: Notifying all stations that ${sender.dishName} is ready.`); } else if (event === 'orderReceived') { console.log(`Coordinator: Received order for ${sender.dishName}, notifying preparation stations.`); } } } // Colleague Classes class GrillStation { constructor(coordinator) { this.coordinator = coordinator; } prepareDish(dishName) { this.dishName = dishName; console.log(`GrillStation: Grilling ${dishName}.`); this.coordinator.notify(this, 'dishPrepared'); } } class BakeryStation { constructor(coordinator) { this.coordinator = coordinator; } bakeDish(dishName) { this.dishName = dishName; console.log(`BakeryStation: Baking ${dishName}.`); this.coordinator.notify(this, 'dishPrepared'); } } // Client Code const coordinator = new KitchenCoordinator(); const grillStation = new GrillStation(coordinator); const bakeryStation = new BakeryStation(coordinator); grillStation.prepareDish('Steak'); // Output: // GrillStation: Grilling Steak. // Coordinator: Notifying all stations that Steak is ready. bakeryStation.bakeDish('Bread'); // Output: // BakeryStation: Baking Bread. // Coordinator: Notifying all stations that Bread is ready.
3. Command
The Command design pattern is an Object Behavioral Pattern that encapsulates the invocation of methods, requests, or operations into a single object and allows both parameterization and pass method calls that can be executed at our discretion. For example, look at how the head chef gives the command below.
// Command Interface class Command { execute() {} } // Concrete Commands class GrillCommand extends Command { constructor(grillStation, dishName) { super(); this.grillStation = grillStation; this.dishName = dishName; } execute() { this.grillStation.grill(this.dishName); } } class BakeCommand extends Command { constructor(bakeryStation, dishName) { super(); this.bakeryStation = bakeryStation; this.dishName = dishName; } execute() { this.bakeryStation.bake(this.dishName); } } // Receiver Classes class GrillStation { grill(dishName) { console.log(`GrillStation: Grilling ${dishName}.`); } } class BakeryStation { bake(dishName) { console.log(`BakeryStation: Baking ${dishName}.`); } } // Invoker Class class HeadChef { setCommand(command) { this.command = command; } executeCommand() { this.command.execute(); } } // Client Code const grillStation = new GrillStation(); const bakeryStation = new BakeryStation(); const grillCommand = new GrillCommand(grillStation, 'Steak'); const bakeCommand = new BakeCommand(bakeryStation, 'Bread'); const headChef = new HeadChef(); headChef.setCommand(grillCommand); headChef.executeCommand(); // GrillStation: Grilling Steak. headChef.setCommand(bakeCommand); headChef.executeCommand(); // BakeryStation: Baking Bread.
Behavioral patterns can feel similar, so let's highlight their differences:
Observer: When a head chef prepares a dish, several other chefs are informed about it.
Mediator: A coordinator works in the kitchen, facilitating communication between various stations in the kitchen.
Command: The head chef issues commands to grill or bake dishes, encapsulating these actions as objects.
Design patterns give a clear way to fix common issues in software development much like a tidy kitchen and smart cooking methods lead to a good meal. When you get these patterns and put them to use, you make your coding easier and help your apps work better and grow more. It doesn't matter if you're new to coding or have done it for a long time - think of design patterns as trusted recipes passed down by many coders over the years. Try them out, play around with them, and soon you'll find that making strong apps becomes as natural as following a recipe you love. Happy coding!
以上がコードを作成する: JavaScript デザイン パターンの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。