设计模式-创建型模式详解
这里写目录标题
- 一、基本概念
- 二、单例模式
- 1. 模式特点
- 2. 适用场景
- 3. 实现方法
- 4. 经典示例
- 三、简单工厂模式
- 1. 模式特点
- 2. 经典示例
- 四、工厂方法模式
- 五、抽象工厂模式
- 1. 适用场景
- 2. 经典示例
- 六、建造者模式
- 1. 模式特点
- 2. 一般流程
- 3. 适用场景
- 4. 经典示例
- 七、原型模式
一、基本概念
创建型模式(Creational)是一种设计模式类别,用于解决与对象创建相关的常见问题。
单例模式、简单工厂模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式。
抽象类概念:
在面向对象编程中有以下几个主要作用:
-
提供一种约束和规范: 抽象类可以定义一些抽象方法,子类必须实现这些方法。这样可以约束子类在继承抽象类时必须按照一定的规范来实现这些方法,确保代码的一致性和可维护性。
-
封装通用的属性和方法: 抽象类可以包含一些通用的属性和方法,这样子类就可以继承这些属性和方法,避免了重复编写代码。通过继承抽象类,子类可以共享抽象类中已经实现的功能。
-
实现多态性: 抽象类可以作为多个具体子类的类型,通过抽象类可以实现多态性的特性。在编程中,可以通过抽象类来声明变量或参数的类型,然后在运行时根据实际的具体子类对象赋值给这些变量或参数实现不同子类对象的统一对待。
-
降低耦合度: 抽象类可以作为中间层,将具体实现和调用方进行解耦。调用方可以通过抽象类进行交互,而不必依赖具体的子类。这样可以提高代码的灵活性和可维护性,方便进行扩展和修改。
总结: 抽象类在面向对象编程中起到了规范、封装、多态和解耦的作用,使得代码更加灵活、可扩展和可维护。
二、单例模式
单例模式(Singleton Pattern)是一种创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点。
1. 模式特点
-
节约资源: 单例模式确保一个类只有一个实例,减少内存开销,节约系统资源。
-
全局访问: 提供全局访问点,方便在不同的组件中共享实例。
-
控制实例数量: 避免了多个实例带来的数据不一致性问题。
2. 适用场景
全局变量、状态管理、共享资源、缓存、日志记录、数据库连接、全局Loading等。
3. 实现方法
构造函数、类的静态属性、闭包。
// 类的静态属性
class Singleton {// 在类中添加一个私有静态成员变量用于保存单例实例private static instance: Singleton// 将类的构造函数设为私有。 类的静态方法仍能调用构造函数,但是其他对象不能调用private constructor() {}// 声明一个公有静态构建方法用于获取单例实例public static getInstance(): Singleton {return this.instance || (this.instance = new Singleton())}
}
// 构造函数
function Singleton() {}Singleton.getInstance = function () {if (!Singleton.instance) {Singleton.instance = new Singleton()}return Singleton.instance
}const s1 = Singleton.getInstance()
const s2 = Singleton.getInstance()
console.log(s1 === s2);
class Singleton {static getInstance() {if(!Singleton.instance) {Singleton.instance = new Singleton()}return Singleton.instance}
}const s1 = Singleton.getInstance()
const s2 = Singleton.getInstance()
console.log(s1 === s2); // true// 也可以基于 constructor(虽然不标准化,但本质都是一样的)
class Singleton {constructor() {if (!Singleton.instance) {Singleton.instance = this}return Singleton.instance}
}
const s1 = new Singleton()
const s2 = new Singleton()
console.log(s1 === s2); // true
// 闭包实现
Singleton.getInstance = (function () {let instancereturn function () {if (!instance) {instance = new Singleton()}return instance}
})()
4. 经典示例
localStorage
function StorageBase() {}
StorageBase.prototype.getItem = function(key) {return this[key]
}
StorageBase.prototype.setItem = function (key, value) {this[key] = value
}var Storage = (function () {var instance = nullreturn function () {return instance || (instance = new StorageBase())}
})()// 这里用不用new无所谓
var s1 = new Storage()
var s2 = new Storage()console.log(s1 === s2); // true
s1.setItem('a', 1)
console.log(s1.getItem('a')); // 1
console.log(s2.getItem('a')); // 1
class Storage {static instance = nullstatic getInstance = function () {return this.instance || (this.instance = new Storage())}getItem(key) {return this[key]}setItem(key, value) {this[key] = value}
}const s1 = Storage.getInstance()
const s2 = Storage.getInstance()console.log(s1 === s2); // true
s1.setItem('a', 1)
console.log(s1.getItem('a')); // 1
console.log(s2.getItem('a')); // 1
message 弹框
模态框/弹窗管理器:确保在同一时间只有一个模态框或弹窗处于打开状态。
// html
<div><button class="add">add</button><button class="close">close</button>
</div>// js
class Model {static instancestatic getInstance () {if (!this.instance) {const model = document.createElement('div')model.id = 'model'model.style.display = 'none'model.innerHTML = 'here! i am here!'document.body.appendChild(model)this.instance = new Model(model)}return this.instance}constructor(model) {this.model = model}open() {this.model.style.display = 'block'}close() {this.model.style.display = 'none'}
}const addBtn = document.querySelector('.add')
addBtn.addEventListener('click', () => {const model = Model.getInstance()model.open()
})const closeBtn = document.querySelector('.close')
closeBtn.addEventListener('click', () => {const model = Model.getInstance()model.close()
})
import Vue from 'vue'
import { Message } from 'element-ui'
import type { ElMessageOptions, ElMessageComponent } from 'element-ui/types/message'class SingletonMessage {static instance: ElMessageComponent | null = nullprivate constructor() {throw new Error('class SingletonMessage can not be called by new')}static show(options: string | ElMessageOptions) {if (this.instance) returnlet config: ElMessageOptionsif (typeof options === 'string') {config = {message: options,onClose: () => {this.instance = null}}} else {const { onClose, ...otherOptions } = optionsconfig = {...otherOptions,onClose: (...args) => {typeof onClose === 'function' && onClose.apply(this, args)this.instance = null}}}this.instance = Message(config)}static close() {this.instance && this.instance.close()}
}Vue.prototype.$singletonMessage = SingletonMessage
我们在axios的拦截器使用
const SingletonMessage = Vue.prototype.$singletonMessageaxios.interceptors.response.use(res => res,err => {SingletonMessage.show({type: 'error',....})return Promise.reject(err)}
)
ESM 模块化
在 ES6 新增语法的 Module 模块特性,通过 import/export 导出模块中的变量是单例的。
// index.html
<script type="module">
import './single.js'
import './single.js'console.log(obj === obj2) // true
</script>// single.js
console.log('modeule test exec :>> ');
在上面代码中,import 引入的模块是会自动执行的,重复引入一个模块并不会执行多次。
如果我们在当前模块修改了某个导出变量。别的模块再引入会是改变后(实时)的值。如果多个模块都引入该变量,则容易出现莫名问题且不好排查。因此我们尽量不要去改变导出变量的值。
**总结:**单例模式本质核心点在于把之前生成的实例缓存起来。缓存的方式有很多种(类静态属性、闭包等),再对外提供一个访问该缓存实例的接口。
三、简单工厂模式
简单工厂模式 又叫 静态工厂模式 ,由一个工厂对象决定创建某一种产品对象类的实例,主要用来创建同一类对象。
1. 模式特点
优点:工厂类中包含了需要创建哪个品种的产品的判断,使用的时候可以直接根据参数创建对应的产品。不需要知道具体产品的类名,只需要知道创建所用的参数即可。
缺点:
-
扩展困难。一旦增加了新的产品,就需要去修改工厂的逻辑,在产品类型较多的时候,会造成工厂过于复杂,增加维护和扩展的难度。
-
简单工厂模式使用了 static 方法,这样就没有办法通过继承来形成一个阶级结构。
2. 经典示例
工厂上需要有一个静态方法,通过参数去创建这个参数对应的工厂实例。
function createCar(make, model) {return {make: make,model: model,start: function() {console.log(`${make} ${model} is starting.`);},stop: function() {console.log(`${make} ${model} is stopping.`);}};
}const car1 = createCar('Toyota', 'Camry');
const car2 = createCar('Honda', 'Accord');car1.start(); // 输出: Toyota Camry is starting.
car2.stop(); // 输出: Honda Accord is stopping.
class Product {constructor(name) {this.name = name;}
}class Factory {createProduct(name) {return new Product(name);}
}// 使用示例
const factory = new Factory();
const product = factory.createProduct('test');
console.log(product.name); // 输出:test
四、工厂方法模式
工厂方法模式引入了一个抽象工厂,通过继承或实现接口来创建对象。
**示例:**假设我们有一个生产汽车的公司,可以生产轿车、货车和客车。为了生产这些不同类型的汽车,我们需要设计三个工厂:轿车工厂、货车工厂和客车工厂。这三个工厂都实现了一个名为 CarFactory
的接口,该接口定义了创建汽车的方法 createCar()
。
// 定义产品接口
class Car {constructor(name) {this.name = name;}getName() {return this.name;}
}// 定义轿车产品
class SedanCar extends Car {constructor() {super('Sedan Car');}
}// 定义货车产品
class TruckCar extends Car {constructor() {super('Truck Car');}
}// 定义客车产品
class BusCar extends Car {constructor() {super('Bus Car');}
}// 定义工厂接口
class CarFactory {createCar() {throw new Error('createCar method must be implemented.');}
}// 定义轿车工厂
class SedanCarFactory extends CarFactory {createCar() {return new SedanCar();}
}// 定义货车工厂
class TruckCarFactory extends CarFactory {createCar() {return new TruckCar();}
}// 定义客车工厂
class BusCarFactory extends CarFactory {createCar() {return new BusCar();}
}// 使用工厂方法创建汽车
let sedanFactory = new SedanCarFactory();
let sedanCar = sedanFactory.createCar();
console.log(sedanCar.getName()); // Sedan Carlet truckFactory = new TruckCarFactory();
let truckCar = truckFactory.createCar();
console.log(truckCar.getName()); // Truck Carlet busFactory = new BusCarFactory();
let busCar = busFactory.createCar();
console.log(busCar.getName()); // Bus Car
五、抽象工厂模式
抽象工厂模式是一种使用工厂对象来创建一系列相关对象的设计模式。它通过提供一个共同的接口来创建一组相关或相互依赖的对象,而不需要指定具体的类。
抽象工厂模式的核心思想是将对象的创建与使用分离开来。通过抽象工厂函数定义一个接口,具体工厂函数实现这个接口,并负责创建具体的产品对象。客户端代码只与抽象工厂函数和抽象产品对象进行交互,而不需要了解具体的产品对象是如何创建的。
抽象工厂模式的优点之一是它能够提供灵活的对象创建机制。通过定义不同的具体工厂函数,我们可以根据需求创建不同类型或变种的产品对象,而无需修改原有代码。抽象工厂模式还可以隐藏具体产品对象的实现细节,只提供一个统一的接口给客户端使用。
抽象工厂本身不直接创建具体的对象,而是通过具体的工厂子类来创建一组相关的对象。
在JavaScript中,抽象工厂模式通常由以下组件组成:
-
抽象工厂函数: 由一个函数定义,并负责定义一组抽象产品对象的接口。它可以是一个普通的函数,也可以是一个构造函数。
-
具体工厂函数: 由一个函数定义,并负责创建具体产品对象,实现了抽象工厂的接口。具体工厂函数通常使用原型继承或对象字面量来实现。
-
抽象产品对象: 由一个普通对象或原型对象定义,并负责定义产品对象的接口。抽象产品对象通常定义了一组共同的方法或属性。
-
具体产品对象: 由一个普通对象或原型对象定义,并实现了抽象产品对象的接口。具体产品对象通常是根据抽象产品对象定义的模板创建的具体实例。
抽象工厂模式的主要思路是通过抽象工厂函数定义的接口来创建具体的产品对象,这样可以实现对象的解耦和灵活性。客户端代码只需关注抽象工厂函数和抽象产品对象,从而实现了高度的可扩展性和可维护性。
1. 适用场景
-
当有多个相关的对象需要创建,并且这些对象之间有一定的约束关系时,可以使用抽象工厂模式统一创建这些对象。
-
当希望通过一个统一的接来创建一组相关对象时,可以使用抽象工厂模式。
不过,抽象工厂模式也有一些限制和注意事项:
-
抽象工厂模式增加了系统的复杂性,因为它引入了多个抽象类和多个具体类。
-
当需要新增一种产品时,需要同时修改抽象工厂接口和具体工厂实现,这可能会影响到系统的稳定性。
-
抽象工厂模式难以支持新类型的产品的变化,因为它的设计侧重于一类产品。
在实际使用抽象工厂模式的过程中,我们需要根据具体的业务需求和系统架构来判断是否适合采用。它可以帮助我们实现代码的解耦和灵活性,但也需要权衡其可能引入的复杂性和代码的维护成本。
// 定义抽象产品类
class Animal {constructor(name, age) {if (new.target === Animal) {throw new Error("抽象类不能直接实例化");}this.name = name;this.age = age;}eat() {throw new Error("抽象方法不能直接调用");}
}// 定义具体产品类
class Dog extends Animal {constructor(name, age, breed) {super(name, age);this.breed = breed;}eat() {console.log(`${this.name} is eating bones.`);}bark() {console.log(`${this.name} is barking.`);}
}class Cat extends Animal {constructor(name, age, color) {super(name, age);this.color = color;}eat() {console.log(`${this.name} is eating fish.`);}meow() {console.log(`${this.name} is meowing.`);}
}// 定义抽象工厂类
class AnimalFactory {createAnimal() {throw new Error("抽象方法不能直接调用");}
}// 定义具体工厂类
class DogFactory extends AnimalFactory {createAnimal(name, age, breed) {return new Dog(name, age, breed);}
}class CatFactory extends AnimalFactory {createAnimal(name, age, color) {return new Cat(name, age, color);}
}// 使用工厂方法模式创建对象
const dogFactory = new DogFactory();
const dog = dogFactory.createAnimal("Max", 3, "Bulldog");
dog.eat();
dog.bark();const catFactory = new CatFactory();
const cat = catFactory.createAnimal("Luna", 2, "Black");
cat.eat();
cat.meow();
在上述示例中,我们定义了一个抽象产品类 Animal,其中包含一个抽象方法 eat。然后我们定义了具体的产品类 Dog 和 Cat,它们都继承自 Animal,并实现了 eat 方法。接着,我们定义了一个抽象工厂类 AnimalFactory,其中包含一个抽象方法 createAnimal。最后,我们定义了具体的工厂类 DogFactory 和 CatFactory,它们都继承自 AnimalFactory,并实现了 createAnimal 方法,用于创建具体的产品。
2. 经典示例
UI组件库
在开发大型的 UI 组件库时,工厂模式可以用于创建各种类型的 UI 组件,通过工厂来统一管理组件的创建和初始化。
class Button {constructor(text) {this.text = text;}render() {console.log(`<button>${this.text}</button>`);}
}class Input {constructor(type) {this.type = type;}render() {console.log(`<input type="${this.type}"/>`);}
}class UIComponentFactory {createComponent(type, options) {switch (type) {case 'button':return new Button(options.text);case 'input':return new Input(options.type);default:throw new Error('Invalid component type.');}}
}const uiFactory = new UIComponentFactory();const button = uiFactory.createComponent('button', { text: 'Click me' });
const input = uiFactory.createComponent('input', { type: 'text' });button.render(); // 输出: <button>Click me</button>
input.render(); // 输出: <input type="text"/>
插件系统
在构建插件化的应用程序时,工厂模式可以用于动态创建和加载插件。
class Plugin {constructor(name) {this.name = name;}execute() {console.log(`${this.name} plugin is executing.`);}
}class PluginFactory {createPlugin(name) {return new Plugin(name);}
}class App {constructor() {this.plugins = [];this.pluginFactory = new PluginFactory();}loadPlugin(name) {const plugin = this.pluginFactory.createPlugin(name);this.plugins.push(plugin);}runPlugins() {this.plugins.forEach(plugin => plugin.execute());}
}const app = new App();
app.loadPlugin('Analytics');
app.loadPlugin('Logger');
app.runPlugins();
模块化加载
在模块化加载的应用中,工厂模式可以用于创建和管理模块实例。
class Module {constructor(name) {this.name = name;}execute() {console.log(`${this.name} module is executing.`);}
}class ModuleFactory {createModule(name) {return new Module(name);}
}class ModuleManager {constructor() {this.modules = [];this.moduleFactory = new ModuleFactory();}loadModule(name) {const module = this.moduleFactory.createModule(name);this.modules.push(module);}runModules() {this.modules.forEach(module => module.execute());}
}const moduleManager = new ModuleManager();
moduleManager.loadModule('Authentication');
moduleManager.loadModule('Storage');
moduleManager.runModules();
三者的特点:
设计模式 | 异同点 | 适用场景 |
---|---|---|
简单工厂模式 | (Simple Factory Pattern | 通过一个一个的工厂类(即简单工厂)来创建所有的产品对象。只需要调用工厂类的静态方法,传入相应的参数即可获取特定类型的对象。 |
工厂方法模式 | (Factory Pattern) | 将对象的创建延迟到具体的子类工厂中,具体的子工厂实现这个方法来创建不同类型的对象。每个具体的子类工厂负责创建一种具体的产品对象。 |
抽象工厂模式 | (Abstract Factory Pattern) | 抽象工厂定义一个接口,该接口声明了一组可以创建不同类型对象的抽象方法。具体的工厂则实现这个接口,并负责创建一组相关的具体产品对象,通过抽象工厂的方法来创建不同类型的产品对象。 |
六、建造者模式
建造者模式(Builder Pattern)是一种创建型设计模式,旨在将对象的创建过程与其表示相互分离。它允许我们连续地构建对象,逐步设置其属性,然后获取最终的构建结果。使用建造者模式,我们可以按照自己的需求构建对象,而不必关心对象的创建过程和内部细节。
可以将一个复杂的对象分解成多个简单的对象来进行构建,将复杂的构建层与表现层分离,使相同的构建过程可以创建不同的表示模式。
建造者模式通常包含以下几个角色:
-
Director(指挥者): 负责定义构建过程的顺序和逻辑,控制对象的创建过程。它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。
-
Builder(抽象建造者): 负责实际构建复杂对象的接口,定义了创建对象的每个步骤。它是一个包含创建产品各个子部件的抽象方法的接口,通常还包含一个返回复杂产品的方法 getResult()。
-
ConcreteBuilder(具体建造者): 实现 Builder 接口,实际进行对象的构建。完成复杂产品的各个部件的具体创建方法。
-
Product(产品角色): 表示最终构建完成的对象。它是包含多个组成部件的复杂对象,由具体建造者来创建其各个零部件。
1. 模式特点
建造者模式的优点:
-
**灵活性高:**建造者模式允许逐步构建复杂对象,并且可以根据需求自由选择和配置对象的属性,而不需要关心对象的创建过程和内部细节。
-
**代码可读性好:**通过将对象的构建过程封装在建造者类中,代码更加清晰直观。建造者模式提供了一种结构化的方式来创建对象,使得代码易于理解和维护。
-
**创建不可变对象:**通过将对象的属性设置为私有,并提供相应的 getter 方法,可以确保对象在创建后不会被修改,增加对象的安全性和封装性。
-
**代码复用性高:**可以将复杂对象构建的逻辑封装在建造者类中,以便在同一场景下复用。
当然,任何一件事物并不是只有优点没有缺点,当然建造者模式也是有一定的缺点的。
-
增加代码量: 引入建造者模式会增加一些额外的类和方法,这可能导致代码量的增加。有时候,如果只需要简单的对象,使用建造者模式可能会过于繁琐。
-
对象构建过程不够灵活: 建造者模式在创建对象时要按照一定的顺序和一系列步骤。这可能会限制构建过程的灵活性,不够适应某些特殊情况。
-
可能存在建造者类过多: 当对象有很多属性时,可能需要创建多个建造者类来构建对象。这可能会导致建造者类的增加,给代码的维护和理解带来一定的复杂性。
2. 一般流程
定义产品类: 首先,需要定义一个产品类,它是由多个部分组成的复杂对象。产品类通常具有多个属性和方法。
创建建造者类: 建造者类负责实际构建产品对象。它提供了一系列方法来设置产品的不同属性。
设置产品属性方法: 建造者类中的方法用于设置产品的属性。每个方法通常设置一个特定的属性,并返回建造者对象本身,以便支持方法链式调用。
构建产品方法: 当所有属性设置完毕后,建造者类提供一个构建产品的方法。该方法执行实际的构建过程,将属性组装成最终的产品对象。
创建指导者类(可选): 在某些情况下,可以创建一个指导者类来协调建造者的构建过程。指导者类知道构建的顺序和方式,并且负责按照正确的顺序调用建造者的方法。
客户端使用: 客户端代码通过实例化建造者对象,并使用建造者对象的方法来设置产品的属性。最后,通过调用建造者对象的构建产品方法,获取构建完成的产品对象。
3. 适用场景
1、需要生成的对象具有复杂的内部结构。
2、需要生成的对象内部属性本身相互依赖。
4. 经典示例
案例场景一
假设我们正在开发一个游戏,需要构建一种特殊类型的角色:骑士。骑士有许多属性,包括名字、等级、武器、盔甲等。
首先,我们定义一个角色类,用于表示骑士对象:
class Knight {constructor(name, level, weapon, armor) {this.name = name;this.level = level;this.weapon = weapon;this.armor = armor;}toString() {return `${this.name}, ${this.level}, weapon: ${this.weapon}, armor: ${this.armor}`;}
}
然后,我们定义一个骑士建造者类,用于逐步构建骑士对象:
class KnightBuilder {constructor() {this.name = null;this.level = null;this.weapon = null;this.armor = null;}// 名称setName(name) {this.name = name;return this;}// 等级setLevel(level) {this.level = level;return this;}// 武器setWeapon(weapon) {this.weapon = weapon;return this;}// 盔甲setArmor(armor) {this.armor = armor;return this;}// 构造骑士build() {return new Knight(this.name, this.level, this.weapon, this.armor);}
}
在这个例子,我们定义了一个KnightBuilder类它包含了与骑士对象相关的各种属性我们可以使用该建造者类逐步设置这些属性,最终通过调用 build 方法来获取建完成的骑士对象。
接下来,让我们使用建造者模式来构建一个骑士对象:
const knight = new KnightBuilder().setName("Arthur").setLevel(10).setWeapon("Excalibur").setArmor("Plate").build();console.log(knight.toString());
在这个例子中,我们首先创建了一个 KnightBuilder 实例,并逐步设置了骑士对象的各个属性。
最后,通过调用 build 方法,我们获取到了构建完成的骑士对象并打印出来。输出结果为:
Arthur, Level 10, weapon: Excalibur, armor: Plate
**总结:**正如我们所看到的,通过建造者模式,我们可以在不污染对象类的情况下,方便地构建出具有各种属性的骑士对象。
案例场景二
假设我们正在构建一个汽车对象,并且汽车具有一些复杂的属性。
class Car {constructor() {this.brand = null;this.color = null;this.engine = null;}
}class CarBuilder {constructor() {this.car = new Car();}// 品牌setBrand(brand) {this.car.brand = brand;return this;}// 颜色setColor(color) {this.car.color = color;return this;}// 引擎setEngine(engine) {this.car.engine = engine;return this;}build() {return this.car;}
}// 创建汽车对象示例
const car = new CarBuilder().setBrand("Tesla").setColor("Red").setEngine("Electric").build();console.log(car);
案例场景三
构建一个在线订餐平台时,我们需要处理各种不同的订单项,包括不同类型的菜品、饮料以及可能的特别要求。订单构建过程需要灵活地适应不同的定制选项。
// 订单项接口
interface OrderItem {void prepare();}// 具体订单项实现
class Meal implements OrderItem {private String dishType;private int quantity;private List<String> toppings;// Constructor, getters and setterspublic void prepare() {// 准备餐点逻辑}
}class Beverage implements OrderItem {private String drinkType;private String size;private boolean ice;// Constructor, getters and setterspublic void prepare() {// 准备饮料逻辑}
}// 订单建造者
class OrderBuilder {private List<OrderItem> orderItems = new ArrayList<>();private String deliveryTime;public OrderBuilder addOrderItem(OrderItem item) {orderItems.add(item);return this;}public OrderBuilder setDeliveryTime(String time) {this.deliveryTime = time;return this;}public Order build() {Order order = new Order();order.setOrderItems(orderItems);order.setDeliveryTime(deliveryTime);return order;}
}// 订单类
class Order {private List<OrderItem> orderItems;private String deliveryTime;// Constructor, getters and setters// 准备订单中的所有项public void prepareOrder() {for (OrderItem item : orderItems) {item.prepare();}}
}// 客户端使用建造者模式构建订单
public class OrderSystem {public static void main(String[] args) {Order order = new OrderBuilder().addOrderItem(new MealBuilder().setDishType("牛肉面").setQuantity(2).addTopping("香菜").build()).addOrderItem(new BeverageBuilder().setDrinkType("可乐").setSize("大杯").addIce(true).build()).setDeliveryTime("12:00").build();order.prepareOrder();}
}
案例场景四
假设我们需要创建一个包含多个输入字段和验证规则的表单。适用建造者模式可以将表单的构建过程分解为多个步骤,每个步骤负责添加一个字段和相应的验证规则。这样一来,我们可以根据需要自由组合字段和验证规则,而不需要关心具体的构建细节。
// 表单建造者
class FormBuilder {constructor() {this.fields = [];}addField(label, type, required) {this.fields.push({ label, type, required });return this;}addValidation(validationFn) {const field = this.fields[this.fields.length - 1];field.validation = validationFn;return this;}build() {return new Form(this.fields);}
}class Form {constructor(fields) {this.fields = fields;}validate() {for (const field of this.fields) {if (field.required && !field.value) {return false;}if (field.validation && !field.validation(field.value)) {return false;}}return true;}
}// 使用建造者模式创建表单
const form = new FormBuilder().addField("Username", "text", true).addValidation(value => value.length >= 6).addField("Password", "password", true).addValidation(value => value.length >= 8).build();// 验证表单
if (form.validate()) {console.log("Form is valid");
} else {console.log("Form is invalid");
}
案例场景五
在前端开发中,我们经常要构建复杂的UI组件,其中包含多个子组件和配置选项。使用建造者模式可以将组件的构建过程分解为多个步骤,每个步骤负责添加一个子组件或者配置选项。这样一来,我们可以根据需要自由组合子组件和配置选项,而不需要关心具体的构建细节。
class ComponentBuilder {constructor() {this.children = [];this.props = {};}addChild(child) {this.children.push(child);return this;}setProps(props) {this.props = props;return this;}build() {return new Component(this.children, this.props);}
}class Component {constructor(children, props) {this.children = children;this.props = props;}render() {// 渲染组件}
}// 使用建造者模式构建复杂的UI组件
const component = new ComponentBuilder().addChild(new ChildComponent1()).addChild(new ChildComponent2()).setProps({ color: "red", size: "large" }).build();// 渲染组件
component.render();
七、原型模式
相关文章:

设计模式-创建型模式详解
这里写目录标题 一、基本概念二、单例模式1. 模式特点2. 适用场景3. 实现方法4. 经典示例 三、简单工厂模式1. 模式特点2. 经典示例 四、工厂方法模式五、抽象工厂模式1. 适用场景2. 经典示例 六、建造者模式1. 模式特点2. 一般流程3. 适用场景4. 经典示例 七、原型模式 一、基…...

【蓝桥杯】每天一题,理解逻辑(2/90)【LeetCode 复写零】
闲话系列:每日一题,秃头有我,Hello!!!!!,我是IF‘Maxue,欢迎大佬们来参观我写的蓝桥杯系列,我好久没有更新博客了,因为up猪我寒假用自己的劳动换了…...

米尔基于STM32MP25x核心板Debian系统发布,赋能工业设备
一、系统概述 MYD-LD25X搭载的Debian系统包含以太网、WIFI/BT、USB、RS485、RS232、CAN、AUDIO、HDMI显示和摄像头等功能,同时也集成了XFCE轻量化桌面、VNC远程操控、SWITCH网络交换和TSN时间敏感网络功能,为工业设备赋予“超强算力实时响应极简运维”的…...

ES02 - ES语句
ES语句 文章目录 ES语句一:连接和基本的使用1:显示详细信息2:输出可显示列3:查看分片 二:Http接口 - 索引(数据库)的增删改2.1:插入数据2.2:删除数据2.3:更新数据2.3.1:P…...

C++ 学生成绩管理系统
一、项目背景与核心需求 成绩管理系统是高校教学管理的重要工具,本系统采用C++面向对象编程实现,主要功能模块包括: 学生信息管理(学号/姓名/3门课程成绩) 成绩增删改查(CRUD)操作 数据持久化存储 统计分析与报表生成 用户友好交互界面 二、系统架构设计 1. 类结构设计 …...

项目管理工具 Maven
目录 1.Maven的概念 1.1什么是Maven 1.2什么是依赖管理 1.3什么是项目构建 1.4Maven的应用场景 1.5为什么使用Maven 1.6Maven模型 2.初识Maven 2.1Maven安装 2.1.1安装准备 2.1.2Maven安装目录分析 2.1.3Maven的环境变量 2.2Maven的第一个项目 2.2.1按照约…...

设计心得——分层和划分模块
一、分层 在实际的设计开发过程中,对于稍微大一些的项目,基本都涉及到分层。什么是分层呢?其实非常简单,就是利用某种逻辑或域的范围等把整个项目划分成多个层次。它们之间通过接口(可能是简单的函数接口也可以是服务…...

uniapp使用蓝牙,usb,局域网,打印机打印
使用流程(支持安卓和iOS) 引入SDK 引入原生插件包地址如下 https://github.com/oldfive20250214/UniPrinterDemo 连接设备 安卓支持经典蓝牙、ble蓝牙、usb、局域网(参考API) iOS支持ble蓝牙、局域网(参考API&…...

PQL查询和监控各类中间件
1 prometheus的PQL查询 1.1 Metrics数据介绍 prometheus监控中采集过来的数据统一称为Metrics数据,其并不是代表具体的数据格式,而是一种统计度量计算单位当需要为某个系统或者某个服务做监控时,就需要使用到 metrics prometheus支持的met…...

day1 redis登入指令
[rootlocalhost data]# redis-cli -h ip -p 6379 -a q123q123 Warning: Using a password with -a or -u option on the command line interface may not be safe. ip:6379> 以上, Bigder...

2025 年 AI 网络安全预测
🍅 点击文末小卡片 ,免费获取网络安全全套资料,资料在手,涨薪更快 微软和 OpenAI 宣布延长战略合作伙伴关系,加强对推进人工智能技术的承诺,这表明强大的 AI 将在未来占据主导地位。 随着人工智能 &#x…...

[Windows] 多系统键鼠共享工具 轻松跨系统控制多台电脑
参考原文:[Windows] 多系统键鼠共享工具 轻松跨系统控制多台电脑 还在为多台电脑需要多套键盘鼠标而烦恼吗?是不是在操控 Windows、macOS、Linux 不同系统电脑时手忙脚乱?现在,这些问题通通能解决!Deskflow 软件闪亮登…...

单片机的发展
一、引言 单片机自诞生以来,经历了四十多年的风风雨雨,从最初的工业控制逐步扩展到家电、通信、智能家居等各个领域。其发展过程就像是一场精彩的冒险,每一次技术的革新都像是在未知的海域中开辟新的航线。 二、单片机的发展历程 ÿ…...

Spring 构造器注入和setter注入的比较
一、比较说明 在 Spring 框架中,构造器注入(Constructor Injection)和 Setter 注入(Setter Injection)是实现依赖注入(DI)的两种主要方式。它们的核心区别在于依赖注入的时机、代码设计理念以及…...

uploadlabs通关思路
目录 靶场准备 复现 pass-01 代码审计 执行逻辑 文件上传 方法一:直接修改或删除js脚本 方法二:修改文件后缀 pass-02 代码审计 文件上传 1. 思路 2. 实操 pass-03 代码审计 过程: 文件上传 pass-04 代码审计 文件上传 p…...

迷你世界脚本自定义UI接口:Customui
自定义UI接口:Customui 彼得兔 更新时间: 2024-11-07 15:12:42 具体函数名及描述如下:(除前两个,其余的目前只能在UI编辑器内部的脚本使用) 序号 函数名 函数描述 1 openUIView(...) 打开一个UI界面(注意…...

【情境领导者】评估情境——准备度水平
本系列是看了《情境领导者》一书,结合自己工作的实践经验所做的学习笔记。 在文章【情境领导者】评估情境——什么是准备度-CSDN博客我们提到准备度是由能力和意愿两部分组成的。 准备度水平 而我们要怎么去评估准备度呢?准备度水平是指人们在每项工作中…...

2025 ubuntu24.04系统安装docker
1.查看ubuntu版本(Ubuntu 24.04 LTS) rootmaster:~# cat /etc/os-release PRETTY_NAME"Ubuntu 24.04 LTS" NAME"Ubuntu" VERSION_ID"24.04" VERSION"24.04 LTS (Noble Numbat)" VERSION_CODENAMEnoble IDubun…...

Android中AIDL和HIDL的区别
在Android中,AIDL(Android Interface Definition Language) 和 HIDL(HAL Interface Definition Language) 是两种用于定义跨进程通信接口的语言。AIDL 是 Android 系统最早支持的 IPC(进程间通信࿰…...

通过数据库网格架构构建现代分布式数据系统
在当今微服务驱动的世界中,企业在跨分布式系统管理数据方面面临着越来越多的挑战。数据库网格架构已成为应对这些挑战的强大解决方案,它提供了一种与现代应用架构相匹配的分散式数据管理方法。本文将探讨数据库网格架构的工作原理,以及如何使…...

Python基于Django的医用耗材网上申领系统【附源码、文档说明】
博主介绍:✌Java老徐、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇&…...

Python爬虫实战:一键采集电商数据,掌握市场动态!
电商数据分析是个香饽饽,可市面上的数据采集工具要不贵得吓人,要不就是各种广告弹窗。干脆自己动手写个爬虫,想抓啥抓啥,还能学点技术。今天咱聊聊怎么用Python写个简单的电商数据爬虫。 打好基础:搞定请求头 别看爬虫…...

STM32之I2C硬件外设
注意:硬件I2C的引脚是固定的 SDA和SCL都是复用到外部引脚。 SDA发送时数据寄存器的数据在数据移位寄存器空闲的状态下进入数据移位寄存器,此时会置状态寄存器的TXE为1,表示发送寄存器为空,然后往数据控制寄存器中一位一位的移送数…...

【C++】中的赋值初始化和直接初始化的区别
在C中,赋值初始化(也称为拷贝初始化)和直接初始化(也称为构造初始化)虽然常常产生相同的结果,但在某些情况下它们有不同的含义和行为。 赋值初始化(Copy Initialization) 使用等号…...

Python ❀ Unix时间戳转日期或日期转时间戳工具分享
设计一款Unix时间戳和日期转换工具,其代码如下: from datetime import datetimeclass Change_Date_Time(object):def __init__(self, date_strNone, date_numNone):self.date_str date_strself.date_num date_num# 转时间戳def datetime2timestamp(s…...

本地部署Dify及避坑指南
Dify作为开源的大模型应用开发平台,支持本地私有化部署,既能保障数据安全,又能实现灵活定制。但对于新手而言,从环境配置到服务启动可能面临诸多挑战。本文结合实战经验,手把手教你从零部署Dify,并总结高频…...

大白话CSS 优先级计算规则的详细推导与示例
大白话CSS 优先级计算规则的详细推导与示例 答题思路 引入概念:先通俗地解释什么是 CSS 优先级,让读者明白为什么要有优先级规则,即当多个 CSS 样式规则作用于同一个元素时,需要确定哪个规则起作用。介绍优先级的分类࿱…...

OpenCV计算摄影学(19)非真实感渲染(Non-Photorealistic Rendering, NPR)
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 非真实感渲染(Non-Photorealistic Rendering, NPR)是一种计算机图形学技术,旨在生成具有艺术风格或其他非现实…...

深度学习(斋藤)学习笔记(五)-反向传播2
上一篇关于反向传播的代码仅支持单变量的梯度计算,下面我们将扩展代码使其支持多个输入/输出。增加了对多输入函数(如 Add),以实现的计算。 1.关于前向传播可变长参数的改进-修改Function类 修改方法: Function用于对…...

数据库基础练习1
目录 1.创建数据库和表 2.插入数据 创建一个数据库,在数据库种创建一张叫heros的表,在表中插入几个四大名著的角色: 1.创建数据库和表 #创建表 CREATE DATABASE db_test;#查看创建的数据库 show databases; #使用db_test数据库 USE db_te…...