Gambar ini: anda berdiri di dapur anda bersedia untuk memasak hidangan yang lazat. Anda telah menyediakan semua bahan, tetapi anda kehilangan resipi untuk diikuti. Anda mula mencuba, tetapi tidak lama kemudian anda berasa terharu. Anda menambah terlalu banyak garam pada satu hidangan, bakar yang lain. Tanpa perancangan yang jelas, memasak menjadi kucar kacir.
Mencipta perisian boleh berasa seperti ini. Anda mempunyai semua alatan dan pengetahuan, tetapi menambah ciri baharu boleh menjadi teka-teki yang mengecewakan tanpa pendekatan yang teratur. Adakah anda memahami perkara yang perlu dilakukan oleh kod anda, tetapi adakah anda menggunakan cara terbaik untuk menjadikan semuanya berfungsi bersama? Di situlah keadaan menjadi rumit. Satu ralat kecil, dan anda mendapati diri anda jatuh ke dalam lubang yang dipenuhi pepijat dan kod kusut.
Masukkan corak reka bentuk— resipi yang telah diuji masa yang diturunkan oleh pengekod selama bertahun-tahun. Pembaikan boleh guna semula ini membantu anda mengendalikan bahagian rumit membuat perisian tanpa mengeluarkan peluh. Kami akan membincangkan apa sebenarnya corak reka bentuk, cara ia boleh menjadikan kehidupan pengekodan anda lebih mudah dan sebab ia adalah kunci untuk membina apl yang teguh dan mudah diselenggara. Untuk menjadikan perkara lebih menarik, kami akan menggunakan terminologi masakan sepanjang penerangan kami—kerana, jujurlah, siapa yang tidak suka rancangan memasak yang sedap?
Jadi, apakah corak reka bentuk? Bagaimanakah mereka akan membantu kami membina apl yang lebih baik?
Corak reka bentuk ialah templat penyelesaian boleh guna semula yang boleh digunakan untuk masalah dan tema berulang dalam reka bentuk perisian. Itu akan menjadi buku masakan yang bagus untuk penyelesaian yang telah dicuba dan diuji daripada pembangun berpengalaman yang menangani masalah biasa reka bentuk perisian. Garis panduan mengatakan bahawa dengan corak reka bentuk, kami boleh mencapai kod yang boleh diselenggara dan boleh digunakan semula dalam apl kami.
Corak reka bentuk, pada dasarnya, dikelaskan mengikut masalah yang mereka selesaikan kepada tiga kategori luas: corak reka bentuk ciptaan, corak reka bentuk struktur dan corak reka bentuk tingkah laku.
Corak reka bentuk dibahagikan kepada tiga kategori berdasarkan masalah yang mereka selesaikan. Ia adalah corak reka bentuk ciptaan, corak reka bentuk struktur dan corak reka bentuk tingkah laku.
Corak reka bentuk ciptaan menyediakan mekanisme untuk mencipta objek. Dalam konteks persembahan masakan, corak-corak ini seperti mengumpulkan dan menyediakan bahan-bahan sebelum dimasak. Beberapa corak yang termasuk dalam kategori ini ialah Pembina, Kilang, Abstrak, Prototaip, Singleton dan Pembina. Untuk mendapatkan pemahaman yang lebih baik, lihat tiga contoh di bawah.
1. Singleton
Bayangkan ada beberapa sos rahsia keluarga yang hanya boleh dibuat dalam periuk khas, diwarisi turun temurun. Sudah tentu, rasa sos tidak boleh sama jika periuk berbeza. Inilah yang dilakukan oleh Singleton: corak reka bentuk yang mana kelas dihadkan untuk mempunyai satu kejadian.
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. Kaedah Kilang
Kaedah Kilang menyediakan antara muka generik untuk mencipta objek, membolehkan kami menentukan jenis objek yang kami mahukan. Dalam rancangan masakan kami, Buku Resipi adalah kilang. Bergantung pada jenis hidangan yang anda ingin buat, ia akan memberikan anda resipi—objek—yang anda perlukan.
// 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.
Kaedah Kilang berguna dalam senario penciptaan objek kompleks—contohnya, menjana kejadian berbeza bergantung pada persekitaran atau mengurus banyak objek serupa.
*3. Kilang Abstrak *
Ia merangkumi butiran pelaksanaan daripada penggunaan umum Objek. Cara terbaik untuk menjelaskan perkara ini ialah jika anda mempertimbangkan perkhidmatan penghantaran kit makan: sama ada anda memasak Itali, Cina atau Mexico, perkhidmatan ini akan menyampaikan segala-galanya dengan bahan-bahan dan resipi, hanya disesuaikan dengan masakan yang ada, jadi semuanya sesuai dengan sempurna.
// 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.
Corak reka bentuk struktur memberi tumpuan kepada komposisi objek, mengenal pasti cara mudah untuk mewujudkan hubungan antara objek yang berbeza. Mereka membantu memastikan bahawa apabila satu bahagian sistem berubah, struktur keseluruhan kekal stabil. Dalam masakan, corak ini mewakili teknik dan alatan yang kami gunakan untuk menggabungkan bahan-bahan menjadi hidangan yang harmoni dan lazat.
Corak yang termasuk dalam kategori ini termasuk Penghias, Fasad, Flyweight, Penyesuai dan Proksi.
1. Corak Fasad
Corak Fasad menawarkan antara muka tahap tinggi yang mudah kepada badan kod yang lebih kompleks, dengan berkesan menyembunyikan kerumitan asas. Bayangkan seorang chef sous memudahkan tugas yang rumit untuk ketua chef. Sous-chef mengumpulkan bahan-bahan, menyediakannya dan mengatur segala-galanya supaya ketua chef boleh memberi tumpuan kepada sentuhan akhir hidangan
// 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!
Atas ialah kandungan terperinci Masak Kod Anda: Corak Reka Bentuk JavaScript. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!