이것을 상상해보세요: 당신은 맛있는 식사를 준비하기 위해 부엌에 서 있습니다. 모든 재료가 준비되어 있지만 따라야 할 레시피가 없습니다. 실험을 시작하지만 곧 압도감을 느끼게 됩니다. 한 접시에 소금을 너무 많이 넣으면 다른 접시가 태워집니다. 명확한 계획이 없으면 요리는 추측에 불과합니다.
소프트웨어를 만드는 것은 이런 느낌일 수 있습니다. 모든 도구와 노하우를 갖추고 있지만 잘 조직된 접근 방식이 없으면 새로운 기능을 추가하는 것이 답답한 퍼즐이 될 수 있습니다. 코드에서 수행해야 하는 작업이 무엇인지 이해하고 있지만 모든 것이 함께 작동하도록 하는 최선의 방법을 찾고 있습니까? 여기서 상황이 복잡해집니다. 작은 오류 하나가 버그와 얽힌 코드로 가득 찬 구멍 속으로 굴러떨어지는 자신을 발견하게 됩니다.
디자인 패턴을 입력하세요. 수년에 걸쳐 코더가 전수해 온 오랜 테스트를 거친 레시피입니다. 이러한 재사용 가능한 수정 사항은 땀을 흘리지 않고도 소프트웨어 제작의 까다로운 부분을 처리하는 데 도움이 됩니다. 디자인 패턴이 정확히 무엇인지, 디자인 패턴이 코딩 생활을 어떻게 더 쉽게 만들어 주는지, 그리고 디자인 패턴이 강력하고 유지 관리하기 쉬운 앱을 구축하는 데 중요한 이유에 대해 알아봅니다. 더 흥미롭게 만들기 위해 설명 전반에 걸쳐 요리 용어를 사용할 것입니다. 솔직히 말해서 좋은 요리 쇼를 좋아하지 않는 사람이 있을까요?
그럼 디자인 패턴이란 무엇일까요? 더 나은 앱을 구축하는 데 어떻게 도움이 될까요?
디자인 패턴은 소프트웨어 디자인에서 반복되는 문제와 테마에 적용할 수 있는 재사용 가능한 솔루션 템플릿입니다. 이는 소프트웨어 설계의 일반적인 문제를 해결하기 위해 노력하는 숙련된 개발자가 시도하고 테스트한 솔루션을 담은 훌륭한 요리책이 될 것입니다. 가이드라인에 따르면 디자인 패턴을 사용하면 앱에서 유지 관리 및 재사용이 가능한 코드를 얻을 수 있습니다.
디자인 패턴은 본질적으로 해결하는 문제에 따라 창의적 디자인 패턴, 구조적 디자인 패턴, 행동 디자인 패턴의 세 가지 범주로 분류됩니다.
디자인 패턴은 해결하는 문제에 따라 세 가지 범주로 나뉩니다. 창조적인 디자인 패턴, 구조적인 디자인 패턴, 행동적인 디자인 패턴이 있습니다.
창의적 디자인 패턴은 객체를 생성하기 위한 메커니즘을 제공합니다. 쿠킹쇼의 맥락에서 이러한 패턴은 요리하기 전에 재료를 모으고 준비하는 것과 같습니다. 이 범주에 속하는 일부 패턴에는 생성자, 팩토리, 추상, 프로토타입, 싱글톤 및 빌더가 있습니다. 더 잘 이해하려면 아래 세 가지 예를 살펴보세요.
1. 싱글톤
몇 세대에 걸쳐 특별한 냄비에서만 만들 수 있는 가족의 비법 소스가 있다고 상상해 보세요. 물론 냄비가 다르면 소스의 맛이 같을 수는 없습니다. 이것이 바로 Singleton이 수행하는 작업입니다. 즉, 클래스가 단일 인스턴스만 갖도록 제한하는 디자인 패턴입니다.
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.
구조적 디자인 패턴은 객체 구성에 중점을 두고 다양한 객체 간의 관계를 설정하는 간단한 방법을 식별합니다. 이는 시스템의 한 부분이 변경될 때 전체 구조가 안정적으로 유지되도록 도와줍니다. 요리에서 이러한 패턴은 재료를 조화롭고 맛있는 요리로 결합하는 데 사용하는 기술과 도구를 나타냅니다.
이 카테고리에 속하는 패턴에는 Decorator, Facade, Flyweight, Adapter 및 Proxy가 포함됩니다.
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 중국어 웹사이트의 기타 관련 기사를 참조하세요!