红宝书第十二讲:详解JavaScript中的工厂模式与原型模式等各种设计模式
红宝书第十二讲:详解JavaScript中的工厂模式与原型模式等各种设计模式
资料取自《JavaScript高级程序设计(第5版)》。
查看总目录:红宝书学习大纲
工厂模式和原型模式解析
一、工厂模式:像订外卖一样创建对象
工厂模式就像一个**“对象生成器”**,只需要告诉它“我要什么”,它就会自动生成并返回对应的对象。适合需要批量创建复杂对象的场景 1。
案例:点餐系统
假设你经营奶茶店,需要根据订单类型生成不同配置的饮品:
// 工厂函数决定饮品类型和配料
function createDrink(type, name) {const drink = { name };if (type === '奶茶') {drink.base = '红茶';drink.toppings = ['珍珠', '奶盖'];} else if (type === '果茶') {drink.base = '四季春茶';drink.toppings = ['椰果', '寒天'];}return drink;
}// 下单操作
const order1 = createDrink('奶茶', '招牌奶茶');
const order2 = createDrink('果茶', '满杯鲜橙');
1: 参考资料6明确将工厂模式列为JavaScript对象创建的核心设计模式
二、原型模式:共享技能的忍者军团
原型模式的精髓是**“多人共用同一套技能”**。将公共方法存放在原型(prototype
)中,所有实例无需重复存储这些方法 2。
示例:游戏中的敌人构造
// 定义敌人原型
function Enemy(type) {this.type = type;
}// 共享攻击方法(所有敌人都会)
Enemy.prototype.attack = function() {console.log(`${this.type}发起攻击!`);
};// 创建实例
const dragon = new Enemy('火龙');
const goblin = new Enemy('哥布林');dragon.attack(); // 火龙发起攻击!
goblin.attack(); // 哥布林发起攻击!// 验证方法共享
console.log(dragon.attack === goblin.attack); // true ✅
2: 参考资料4通过Person.prototype案例证明原型共享方法的有效性
为什么用原型?
- 内存节省:1000个实例共享1个方法,而非存储1000份 3
- 动态更新:在原型添加新方法,现有实例即时生效
例如新增
Enemy.prototype.run = function() { ... }
,所有敌人都能调用.run()
三、对比总结
模式 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
工厂模式 | 需要灵活创建多类型对象 | 隐藏创建细节,代码简洁 | 类型识别困难(如无法用instanceof ) |
原型模式 | 大量对象需共享方法或属性 | 节省内存,动态扩展性强 | 复杂属性需独立初始化(如对象引用) |
3: 参考资料3强调代码维护性需通过共享结构实现,这正是原型模式的核心优势
好的!以下是根据你的要求修改后的版本,增加了对比表格、详细说明文字以及代码注释。
JavaScript 常用设计模式解析
一、设计模式特点对比表
设计模式 | 主要用途 | 优点 | 缺点 |
---|---|---|---|
工厂模式 | 创建多种类型的对象,隐藏创建逻辑,提供统一的创建接口 | 简化对象创建逻辑,易于扩展和维护 | 增加了系统的复杂性,可能需要维护多个工厂类 |
单例模式 | 确保一个类只有一个实例,并提供全局访问点 | 避免重复实例化,节省资源,便于管理全局状态 | 可能导致代码难以测试,过度使用会限制系统的灵活性 |
原型模式 | 通过复制现有对象创建新对象,避免复杂的初始化过程 | 性能优化,减少重复代码,易于创建多个类似对象 | 可能导致对象之间的关系复杂,难以管理 |
代理模式 | 提供一个代理对象来控制对实际对象的访问,添加额外逻辑 | 可以在不修改原始对象的情况下添加功能,如权限控制、缓存等 | 增加了系统的复杂性,可能影响性能 |
观察者模式 | 定义对象间的一对多依赖关系,当一个对象改变时,所有依赖它的对象都会得到通知 | 实现了对象间的松耦合,便于扩展和维护 | 可能导致通知风暴,影响性能,且依赖关系复杂 |
策略模式 | 定义一系列算法,封装起来并使它们可互换 | 提供了算法的可切换性,易于扩展和维护 | 可能导致策略类过多,增加系统的复杂性 |
装饰者模式 | 动态地给对象添加额外的功能,而不改变其结构 | 提供了比继承更灵活的扩展方式,易于添加新功能 | 可能导致装饰器过多,影响性能和可读性 |
适配器模式 | 将一个类的接口转换成客户希望的另一个接口 | 提高了类的兼容性,避免了修改现有代码 | 增加了系统的复杂性,可能隐藏了接口的不一致性 |
模板方法模式 | 定义一个操作中的算法框架,将某些步骤延迟到子类中 | 提供了代码复用性,便于维护和扩展 | 子类可能过度依赖父类,限制了灵活性 |
建造者模式 | 逐步构建一个复杂的对象,隐藏构建过程 | 提供了构建过程的灵活性,易于扩展和维护 | 可能增加系统的复杂性,需要维护多个建造者类 |
二、设计模式详细解析
1. 工厂模式(Factory Pattern)
详细说明
工厂模式就像一个“制造机器”,它的主要任务是根据不同的需求创建不同类型的对象,而不需要直接使用 new
关键字去实例化对象。想象一下,你去玩具店买玩具,你只需要告诉店员你想要什么类型的玩具(比如小汽车、洋娃娃),店员就会给你制造出对应类型的玩具,而你不需要自己去组装玩具。工厂模式的核心在于隐藏了对象的创建细节,让使用者只需要关心对象的使用,而不需要关心对象是如何被创建的。
代码示例
// 定义一个基类 Animal
class Animal {speak() {return this.makeSound();}
}// 定义具体的 Dog 类
class Dog extends Animal {makeSound() {return "Woof"; // 狗的叫声}
}// 定义具体的 Cat 类
class Cat extends Animal {makeSound() {return "Meow"; // 猫的叫声}
}// 工厂类,负责创建不同类型的动物对象
class AnimalFactory {createAnimal(type) {switch (type) {case "dog":return new Dog(); // 创建 Dog 实例case "cat":return new Cat(); // 创建 Cat 实例default:throw new Error("Unknown animal type"); // 如果类型未知,抛出错误}}
}// 使用工厂模式创建对象
const factory = new AnimalFactory();
const dog = factory.createAnimal("dog"); // 创建 Dog 实例
console.log(dog.speak()); // 输出:Woof
const cat = factory.createAnimal("cat"); // 创建 Cat 实例
console.log(cat.speak()); // 输出:Meow
代码注释
Animal
是一个基类,定义了一个通用的speak
方法。Dog
和Cat
是具体的动物类,分别实现了makeSound
方法。AnimalFactory
是工厂类,通过createAnimal
方法根据传入的类型参数创建对应的动物对象。- 使用工厂模式时,我们只需要通过工厂类的
createAnimal
方法来获取对象,而不需要直接使用new
关键字。
流程图
2. 单例模式(Singleton Pattern)
详细说明
单例模式确保一个类只有一个实例,并提供一个全局访问点。想象一下,一个国家的总统,全国只能有一个总统,不管你从哪里获取总统的信息,都是同一个总统对象。如果已经有一个总统实例了,再获取总统对象时,就直接返回这个已经存在的实例,而不是再创建一个新的。单例模式的核心在于保证全局唯一性,避免重复实例化,节省资源。
代码示例
// 定义一个 Logger 类
class Logger {constructor() {if (Logger.instance) {return Logger.instance; // 如果已经存在实例,直接返回}this.messages = []; // 用于存储日志消息Logger.instance = this; // 保存当前实例}log(message) {this.messages.push(message); // 添加日志消息}getLogs() {return this.messages; // 获取所有日志消息}
}// 使用单例模式
const logger1 = new Logger();
logger1.log("Hello"); // 添加日志const logger2 = new Logger();
logger2.log("World"); // 添加日志console.log(logger1.getLogs()); // 输出:["Hello", "World"]
console.log(logger1 === logger2); // 输出:true(logger1 和 logger2 是同一个实例)
代码注释
Logger
类中有一个静态属性instance
,用于保存唯一的实例。- 在构造函数中,如果
Logger.instance
已经存在,则直接返回已存在的实例,否则创建一个新的实例并保存到Logger.instance
中。 - 通过
log
方法添加日志消息,通过getLogs
方法获取所有日志消息。 - 无论创建多少次
Logger
实例,始终返回同一个实例,确保全局唯一性。
流程图
3. 原型模式(Prototype Pattern)
详细说明
原型模式允许通过复制现有的对象来创建新的对象,而不需要通过构造函数来创建。想象一下,你有一个已经配置好的电脑模板,你可以直接复制这个模板来创建新的电脑,而不需要重新配置。原型模式的核心在于通过对象的复制来创建新对象,避免了复杂的初始化过程,提高了性能。
代码示例
// 定义一个原型对象
const animalPrototype = {speak() {return this.makeSound(); // 调用 makeSound 方法},
};// 通过 Object.create 复制原型对象创建 Dog 实例
const dog = Object.create(animalPrototype);
dog.makeSound = function () {return "Woof"; // 狗的叫声
};// 通过 Object.create 复制原型对象创建 Cat 实例
const cat = Object.create(animalPrototype);
cat.makeSound = function () {return "Meow"; // 猫的叫声
};console.log(dog.speak()); // 输出:Woof
console.log(cat.speak()); // 输出:Meow
代码注释
animalPrototype
是一个原型对象,定义了一个通用的speak
方法。- 使用
Object.create
方法,通过复制animalPrototype
来创建dog
和cat
实例。 - 每个实例都可以通过覆盖
makeSound
方法来定义自己的行为。 - 这种方式避免了通过构造函数创建对象的复杂性,同时保持了对象的继承关系。
流程图
4. 代理模式(Proxy Pattern)
详细说明
代理模式通过创建一个代理对象来控制对实际对象的访问,代理对象可以添加额外的逻辑,比如权限检查、缓存、远程调用等。想象一下,你去银行取钱,银行柜员就是一个代理,他可以检查你的身份,确认你有权限取钱后,才会让你取钱。代理模式的核心在于在不修改原始对象的情况下,添加额外的控制逻辑。
代码示例
// 定义一个真实对象
class RealSubject {request() {return "RealSubject: Handling request."; // 真实对象的请求方法}
}// 定义一个代理对象
class Proxy {constructor(realSubject) {this.realSubject = realSubject; // 保存真实对象的引用}request() {if (this.checkAccess()) { // 检查权限this.realSubject.request(); // 调用真实对象的请求方法this.logAccess(); // 记录日志}}checkAccess() {console.log("Proxy: Logging the time of request."); // 记录请求时间return true; // 假设权限检查通过}logAccess() {console.log("Proxy: Logging the request."); // 记录请求}
}// 使用代理模式
const realSubject = new RealSubject(); // 创建真实对象
const proxy = new Proxy(realSubject); // 创建代理对象
proxy.request(); // 调用代理的请求方法
代码注释
RealSubject
是真实对象,定义了一个request
方法。Proxy
是代理对象,通过request
方法控制对真实对象的访问。- 在调用真实对象的
request
方法之前,代理对象会执行额外的逻辑,如权限检查和日志记录。 - 通过代理模式,可以在不修改真实对象的情况下,添加额外的功能。
流程图
5. 观察者模式(Observer Pattern)
详细说明
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象发生变化时,所有依赖它的观察者都会自动收到通知并进行更新。想象一下,你关注了一个博主,当博主发布新的文章时,你会收到通知。观察者模式的核心在于实现对象间的松耦合,当一个对象改变时,其他对象可以自动响应。
代码示例
// 定义一个主题对象
class Subject {constructor() {this.observers = []; // 保存观察者列表}addObserver(observer) {this.observers.push(observer); // 添加观察者}removeObserver(observer) {this.observers = this.observers.filter((obs) => obs !== observer); // 移除观察者}notify() {this.observers.forEach((observer) => observer.update()); // 通知所有观察者}
}// 定义一个观察者对象
class Observer {update() {console.log("Observer: I've been notified!"); // 观察者收到通知后的响应}
}// 使用观察者模式
const subject = new Subject(); // 创建主题对象
const observer1 = new Observer(); // 创建观察者1
const observer2 = new Observer(); // 创建观察者2subject.addObserver(observer1); // 添加观察者1
subject.addObserver(observer2); // 添加观察者2subject.notify(); // 通知所有观察者
代码注释
Subject
是主题对象,维护一个观察者列表,并提供添加、移除和通知观察者的方法。Observer
是观察者对象,定义了一个update
方法,用于响应通知。- 当主题对象的状态发生变化时,通过调用
notify
方法通知所有观察者。 - 观察者模式实现了对象间的松耦合,便于扩展和维护。
流程图
6. 策略模式(Strategy Pattern)
详细说明
策略模式定义了一系列的算法,把它们封装起来,并使它们可以互换。策略模式让算法的变化独立于使用算法的客户。想象一下,你有一个计算器,可以选择不同的运算方式(如加法、减法)。策略模式的核心在于通过封装不同的算法,让客户端可以在运行时动态选择算法,而不需要修改代码。
代码示例
// 定义一个上下文对象
class Context {constructor(strategy) {this.strategy = strategy; // 保存策略对象}executeStrategy(a, b) {return this.strategy(a, b); // 执行策略}
}// 定义加法策略
function addStrategy(a, b) {return a + b; // 加法运算
}// 定义减法策略
function subtractStrategy(a, b) {return a - b; // 减法运算
}// 使用策略模式
const context = new Context(addStrategy); // 使用加法策略
console.log(context.executeStrategy(10, 5)); // 输出:15context.strategy = subtractStrategy; // 切换到减法策略
console.log(context.executeStrategy(10, 5)); // 输出:5
代码注释
Context
是上下文对象,保存一个策略对象,并通过executeStrategy
方法执行策略。addStrategy
和subtractStrategy
是具体的策略,分别实现加法和减法运算。- 客户端可以通过动态更换策略对象来改变行为,而不需要修改上下文对象的代码。
流程图
7. 装饰者模式(Decorator Pattern)
详细说明
装饰者模式允许动态地给一个对象添加额外的功能,而不需要修改其结构。想象一下,你有一件衣服,你可以通过添加装饰(如徽章、刺绣)来改变它的外观,而不需要改变衣服本身的结构。装饰者模式的核心在于通过组合的方式,动态地扩展对象的功能。
代码示例
// 定义一个组件基类
class Component {operation() {return "Component"; // 基类的通用方法}
}// 定义具体的组件类
class ConcreteComponent extends Component {operation() {return "ConcreteComponent"; // 具体组件的实现}
}// 定义装饰者类
class Decorator extends Component {constructor(component) {super();this.component = component; // 保存组件对象}operation() {return `Decorator(${this.component.operation()})`; // 调用组件对象的方法,并添加额外逻辑}
}// 使用装饰者模式
const component = new ConcreteComponent(); // 创建具体组件
console.log(component.operation()); // 输出:ConcreteComponentconst decoratedComponent = new Decorator(component); // 创建装饰者
console.log(decoratedComponent.operation()); // 输出:Decorator(ConcreteComponent)
代码注释
Component
是组件基类,定义了一个通用的operation
方法。ConcreteComponent
是具体的组件类,实现了operation
方法。Decorator
是装饰者类,通过组合的方式保存一个组件对象,并在调用组件对象的方法时添加额外的逻辑。- 通过装饰者模式,可以在不修改组件类的情况下,动态地扩展功能。
流程图
8. 适配器模式(Adapter Pattern)
详细说明
适配器模式将一个类的接口转换成客户希望的另一个接口,使原本不兼容的接口能够兼容。想象一下,你有一个充电器,但它只能给手机充电,而你有一个平板电脑需要充电。适配器模式的核心在于通过一个适配器对象,将充电器的接口转换为平板电脑可以使用的接口,从而实现兼容。
代码示例
// 定义一个媒体播放器接口
class MediaPlayer {play(audioType, fileName) {console.log(`Playing ${audioType} file. Name: ${fileName}`); // 播放音频文件}
}// 定义一个高级媒体播放器接口
class AdvancedMediaPlayer {playVlc(fileName) {console.log(`Playing VLC file. Name: ${fileName}`); // 播放 VLC 文件}playMp4(fileName) {console.log(`Playing MP4 file. Name: ${fileName}`); // 播放 MP4 文件}
}// 定义一个适配器类
class MediaAdapter extends MediaPlayer {constructor(audioType, advancedMediaPlayer) {super();this.audioType = audioType; // 保存音频类型this.advancedMediaPlayer = advancedMediaPlayer; // 保存高级媒体播放器对象}play(audioType, fileName) {if (audioType === "vlc") {this.advancedMediaPlayer.playVlc(fileName); // 调用高级媒体播放器的 playVlc 方法} else if (audioType === "mp4") {this.advancedMediaPlayer.playMp4(fileName); // 调用高级媒体播放器的 playMp4 方法}}
}// 使用适配器模式
const mediaPlayer = new MediaPlayer(); // 创建媒体播放器对象
const advancedMediaPlayer = new AdvancedMediaPlayer(); // 创建高级媒体播放器对象
const mediaAdapter = new MediaAdapter("vlc", advancedMediaPlayer); // 创建适配器对象mediaAdapter.play("vlc", "song.vlc"); // 通过适配器播放 VLC 文件
代码注释
MediaPlayer
是媒体播放器接口,定义了一个play
方法。AdvancedMediaPlayer
是高级媒体播放器接口,定义了playVlc
和playMp4
方法。MediaAdapter
是适配器类,通过继承MediaPlayer
并保存一个AdvancedMediaPlayer
对象,将高级媒体播放器的接口适配到媒体播放器的接口。- 通过适配器模式,可以让客户端代码(
MediaPlayer
)调用高级媒体播放器的方法,实现接口的兼容。
流程图
9. 模板方法模式(Template Method Pattern)
详细说明
模板方法模式定义了一个操作中的算法的框架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变算法的结构即可重定义算法的某些特定步骤。想象一下,你有一个烤面包机,它有一个固定的烤面包流程(预热、烤面包、冷却),但你可以通过更换不同的面包片来改变烤面包的结果。模板方法模式的核心在于通过定义一个算法框架,让子类可以在不改变框架的情况下,实现具体的步骤。
代码示例
// 定义一个抽象类
class AbstractClass {templateMethod() {this.baseOperation1(); // 执行基础操作1this.requiredOperations1(); // 执行需要子类实现的操作1this.baseOperation2(); // 执行基础操作2this.hook1(); // 执行钩子方法1this.requiredOperations2(); // 执行需要子类实现的操作2this.baseOperation3(); // 执行基础操作3this.hook2(); // 执行钩子方法2}baseOperation1() {console.log("AbstractClass says: I am doing the bulk of the work"); // 基础操作1}baseOperation2() {console.log("AbstractClass says: But I let subclasses alter some specific steps"); // 基础操作2}baseOperation3() {console.log("AbstractClass says: But I am doing the bulk of the work anyway"); // 基础操作3}hook1() {} // 钩子方法1hook2() {} // 钩子方法2
}// 定义一个具体的子类
class ConcreteClass1 extends AbstractClass {requiredOperations1() {console.log("ConcreteClass1 says: Implemented Operation1"); // 子类实现的操作1}requiredOperations2() {console.log("ConcreteClass1 says: Implemented Operation2"); // 子类实现的操作2}
}// 定义另一个具体的子类
class ConcreteClass2 extends AbstractClass {requiredOperations1() {console.log("ConcreteClass2 says: Implemented Operation1"); // 子类实现的操作1}requiredOperations2() {console.log("ConcreteClass2 says: Implemented Operation2"); // 子类实现的操作2}hook1() {console.log("ConcreteClass2 says: Overridden Hook1"); // 子类重写的钩子方法1}
}// 使用模板方法模式
const template1 = new ConcreteClass1(); // 创建具体子类1
template1.templateMethod(); // 调用模板方法const template2 = new ConcreteClass2(); // 创建具体子类2
template2.templateMethod(); // 调用模板方法
代码注释
AbstractClass
是抽象类,定义了一个模板方法templateMethod
,它按顺序调用一系列操作,包括基础操作和需要子类实现的操作。ConcreteClass1
和ConcreteClass2
是具体的子类,分别实现了requiredOperations1
和requiredOperations2
方法。- 子类可以通过重写钩子方法(如
hook1
和hook2
)来扩展模板方法的行为。 - 模板方法模式允许子类在不改变算法结构的情况下,实现具体的步骤。
流程图
10. 建造者模式(Builder Pattern)
详细说明
建造者模式将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。想象一下,你正在建造一座房子,你需要逐步完成不同的步骤(如打地基、建墙壁、安装门窗)。建造者模式的核心在于通过一个建造者对象逐步构建复杂对象,隐藏具体的构建过程,让客户端只需要关心最终的结果。
代码示例
// 定义一个复杂对象
class Car {constructor() {this.type = null; // 车型this.seats = null; // 座位数this.engine = null; // 发动机类型this.tripComputer = null; // 是否有行程计算机this.gps = null; // 是否有 GPS}
}// 定义一个建造者类
class CarBuilder {constructor() {this.car = new Car(); // 创建一个 Car 实例}setType(type) {this.car.type = type; // 设置车型return this; // 返回当前对象,支持链式调用}setSeats(seats) {this.car.seats = seats; // 设置座位数return this; // 返回当前对象,支持链式调用}setEngine(engine) {this.car.engine = engine; // 设置发动机类型return this; // 返回当前对象,支持链式调用}setTripComputer(tripComputer) {this.car.tripComputer = tripComputer; // 设置行程计算机return this; // 返回当前对象,支持链式调用}setGPS(gps) {this.car.gps = gps; // 设置 GPSreturn this; // 返回当前对象,支持链式调用}build() {return this.car; // 返回构建好的 Car 实例}
}// 使用建造者模式
const carBuilder = new CarBuilder(); // 创建建造者对象
const car = carBuilder.setType("SUV") // 设置车型为 SUV.setSeats(4) // 设置座位数为 4.setEngine("V6") // 设置发动机类型为 V6.setTripComputer(true) // 设置行程计算机为 true.setGPS(true) // 设置 GPS 为 true.build(); // 构建汽车console.log(car); // 输出构建好的汽车对象
代码注释
Car
是一个复杂对象,包含多个属性(如车型、座位数、发动机类型等)。CarBuilder
是建造者类,通过一系列的设置方法(如setType
、setSeats
等)逐步构建Car
对象。- 每个设置方法都返回当前建造者对象,支持链式调用,最后通过
build
方法返回构建好的Car
对象。 - 建造者模式隐藏了构建过程的细节,让客户端只需要关心最终的结果。
流程图
希望这些详细的说明和代码注释能帮助你更好地理解这些设计模式!
目录:总目录
上篇文章:红宝书第十一讲:超易懂版「ES6类与继承」零基础教程:用现实例子+图解实现
下篇文章:红宝书第十三讲:详解JavaScript核心对象:Array、Object、Date、RegExp
脚注
《JavaScript高级程序设计(第5版)》索引的"factory pattern"验证工厂模式的代码组织形式。 ↩︎ ↩︎
《JavaScript高级程序设计(第5版)》通过Person类与.prototype的关联说明原型共享机制。 ↩︎ ↩︎
《JavaScript高级程序设计(第5版)》指出代码可维护性的核心在于避免冗余,与原型设计理念一致。 ↩︎ ↩︎
相关文章:
红宝书第十二讲:详解JavaScript中的工厂模式与原型模式等各种设计模式
红宝书第十二讲:详解JavaScript中的工厂模式与原型模式等各种设计模式 资料取自《JavaScript高级程序设计(第5版)》。 查看总目录:红宝书学习大纲 工厂模式和原型模式解析 一、工厂模式:像订外卖一样创建对象 工厂模…...
Flutter完整开发实战详解(一、Dart语言和Flutter基础)
前言 在如今的 Flutter 大潮下,本系列是让你看完会安心的文章。本系列将完整讲述:如何快速从0开发一个完整的 Flutter APP,配套高完成度 Flutter 开源项目 GSYGithubAppFlutter。同时也会提供一些 Flutter 的开发细节技巧,并针对…...
Kafka 偏移量
在 Apache Kafka 中,偏移量(Offset)是一个非常重要的概念。它不仅用于标识消息的位置,还在多种场景中发挥关键作用。本文将详细介绍 Kafka 偏移量的核心概念及其使用场景。 一、偏移量的核心概念 1. 定义 偏移量是一个非负整数…...
手撕LRU缓存Java版(带输入输出)
由于面试手撕lru没撕出来,导致心态炸裂,今天特地练习了lru输入输出 手撕版,在每个函数里手动加上输出 public class LC146 {static class LRUCache{class Node{int key, value;Node prev, next;Node(int key, int value){this.key key;thi…...
Android 12系统源码_系统启动(二)Zygote进程
前言 Zygote(意为“受精卵”)是 Android 系统中的一个核心进程,负责 孵化(fork)应用进程,以优化应用启动速度和内存占用。它是 Android 系统启动后第一个由 init 进程启动的 Java 进程,后续所有…...
python之并发编程
并发编程介绍 串行、并行与并发的区别 进程、线程、协程的区别 1. 进程 (Process) 定义:进程是操作系统为运行中的程序分配的基本单位。每个进程都有独立的地址空间和资源(如内存、文件句柄等)。特点: 进程是资源分配的基本单位…...
极速全场景 MPP数据库starrocks介绍
目录 一、引子 二、起源 (一)前身 (二)定位 三、特点 (一)高性能架构 (二)实时分析 (三)高并发与扩展性 (四)兼容性与生态 …...
MySQL 表连接(内连接与外连接)
🏝️专栏:Mysql_猫咪-9527的博客-CSDN博客 🌅主页:猫咪-9527-CSDN博客 “欲穷千里目,更上一层楼。会当凌绝顶,一览众山小。” 目录 1、表连接的核心概念 1.1 为什么需要表连接? 2、内连接&a…...
重学Java基础篇—什么是快速失败(fail-fast)和安全失败(fail-safe)?
快速失败(fail-fast) 和 安全失败(fail-safe) 是两种不同的迭代器设计策略,主要用于处理集合(如 List、Map)在遍历过程中被修改的场景。 它们的核心区别在于对并发修改的容忍度和实现机制。 1…...
Redis 集群配置
在币圈交易所,Redis 集群的节点数量和内存大小通常根据交易所的规模、访问量、并发需求等因素来决定。一般来说,可以按照以下标准配置: Redis 集群节点数量 小型交易所(日活 < 10万,QPS < 10k)&…...
容器C++
string容器 string构造函数 #include<iostream> using namespace std; #include<string.h> void test01() {string s1;//默认构造const char* str "hello world";string s2(str);//传入char*cout << "s2" << s2 << endl;s…...
Git 基础入门:从概念到实践的版本控制指南
一、Git 核心概念解析 1. 仓库(Repository) Git 的核心存储单元,包含项目所有文件及其完整历史记录。分为本地仓库(开发者本地副本)和远程仓库(如 GitHub、GitLab 等云端存储),支持…...
蓝桥杯真题_小蓝和小桥的讨论
小蓝和小桥的讨论 问题描述 小蓝和小桥是一所高中的好朋友,他们正在讨论下一次的课程。这节课需要讨论 nn 个主题,第 ii 个主题对老师来说有 aia**i 的趣味度,对学生来说有 bib**i 的趣味度。 小蓝认为,如果一个主题对老师来说…...
【C++游戏引擎开发】《线性代数》(2):矩阵加减法与SIMD集成
一、矩阵加减法数学原理 1.1 定义 逐元素操作:运算仅针对相同位置的元素,不涉及矩阵乘法或行列变换。交换律与结合律: 加法满足交换律(A + B = B + A)和结合律( ( A + B ) + C = A + ( B + C ) )。 减法不满足交换律(A − B ≠ B − A)。1.2 公式 C i j = …...
HTML应用指南:利用POST请求获取全国小鹏汽车的充电桩位置信息
在新能源汽车快速发展的背景下,充电桩的分布和可用性成为影响用户体验的关键因素之一。随着全球对环境保护意识的增强以及政府对新能源政策的支持,越来越多的消费者倾向于选择电动汽车作为日常出行工具。然而,充电设施是否完备、便捷直接影响…...
工具介绍《WireShark》
Wireshark 过滤命令中符号含义详解 一、比较运算符 Wireshark 支持两种比较运算符语法:英文缩写(如 eq)和 C语言风格符号(如 ),两者功能等价。 符号(英文缩写)C语言风格符号含义示…...
深入理解 Linux 中磁盘空间驱动的编写:从原理到实践
在编写 Linux 内核中的磁盘空间驱动时,理解不同类型的存储设备及其在内核中的工作模式至关重要。常见的存储设备主要分为两类:采用 MTD(Memory Technology Device)模式的原始闪存设备(如 NAND、NOR Flash)&…...
flutter android端抓包工具
flutter做的android app,使用fiddler抓不了包,现介绍一款能支持flutter的抓包工具Reqable,使用方法如下: 1、下载电脑端安装包 下载地址为【https://reqable.com/zh-CN/download/】 2、还是在上述地址下载 android 端apk…...
知识周汇 | 用 matplotlib 轻松绘制折线图、散点图、柱状图、直方图
目录 前言 折线图 散点图 柱状图 直方图 组合图:柱状图和折线图 1. 导入库 2. 定义组合图函数 3. 设置中文字体和样式 4. 创建画布和子图 5. 绘制柱状图 6. 绘制折线图 7. 美化图表 8. 保存和显示图表 9. 调用函数 总结 前言 matplotlib 是 Python…...
Ribbon负载均衡的深度解析与应用
在微服务架构中,服务之间的调用频繁且复杂,因此负载均衡显得尤为重要。Spring Cloud生态系统中,Ribbon作为一个客户端负载均衡器,扮演着关键的角色。它不仅能提高系统的响应速度,还能确保系统的稳定性和可用性。接下来…...
Neo4j GDS-06-neo4j GDS 库中社区检测算法介绍
neo4j apoc 系列 Neo4j APOC-01-图数据库 apoc 插件介绍 Neo4j APOC-01-图数据库 apoc 插件安装 neo4j on windows10 Neo4j APOC-03-图数据库 apoc 实战使用使用 Neo4j APOC-04-图数据库 apoc 实战使用使用 apoc.path.spanningTree 最小生成树 Neo4j APOC-05-图数据库 apo…...
Android 删除aar中的一个类 aar包冲突 aar类冲突 删除aar中的一个包
Duplicate class com.xxxa.naviauto.sdk.listener.OnChangeListener found in modules jetified-xxxa-sdk-v1.1.2-release-runtime (:xxx-sdk-v1.1.2-release:) and jetified-xxxb-sdk-1.1.3-runtime (:xxxb-sdk-1.1.3:) A.aar B.aar 有类冲突; 使用 exclude 排除本…...
【老电脑翻新】华硕A456U(换电池+换固态+光驱换机械+重装系统+重装系统后开始菜单失灵问题解决)
前言 电脑华硕A456U买来快10年了,倒是还能用,就是比较卡,cpu占比总是100%,之前已经加过内存条了。想要不换个固态看看。 省流:没太大效果。 记录一下拆机&换固态的过程 准备 西部数据固态硬盘480G WD Green S…...
Unity 简单使用Addressables加载SpriteAtlas图集资源
思路很简单,传入图集名和资源名,利用Addressables提供的异步加载方式从ab包中加载。加载完成后存储进缓存字典里,以供后续使用。 添加引用计数,防止多个地方使用同一图集时,不会提前释放 using UnityEngine; using U…...
stable diffusion本地安装
1. 基本环境准备 安装conda 环境 pytorch基础学习-CSDN博客 创建虚拟环境: conda create -n sd python3.10 一定要指定用3.10,过高的版本会提示错误: 激活启用环境: conda activate sd 设置pip国内镜像源: pip conf…...
MQ 如何保证数据一致性?
大家好,我是苏三,又跟大家见面了。 前言 上个月,我们有个电商系统出了个灵异事件:用户支付成功了,但订单状态死活不改成“已发货”。 折腾了半天才定位到问题:订单服务的MQ消息,像人间蒸发一…...
spring @Autowired对属性、set方法,构造器的分别使用,以及配合 @Autowired 和 @Qualifier避免歧义性的综合使用案例
代码结构 依赖注入 在Spring IoC容器的概念中,主要是使用依赖注入来实现Bean之间的依赖关系的 举例 例如,人类(Person)有时候会利用动物(Animal)来完成一些事情,狗(Dog࿰…...
Ubuntu 系统上完全卸载 Docker
以下是在 Ubuntu 系统上完全卸载 Docker 的分步指南 一.卸载验证 二.卸载步骤 1.停止 Docker 服务 sudo systemctl stop docker.socket sudo systemctl stop docker.service2.卸载 Docker 软件包 # 移除 Docker 核心组件 sudo apt-get purge -y \docker-ce \docker-ce-cli …...
国际机构Gartner发布2025年网络安全趋势
转自:中国新闻网 中新网北京3月14日电 国际机构高德纳(Gartner)14日发布的消息称,网络安全和风险管理在2025年“面临挑战与机遇并存的局面”,“实现转型和提高弹性”对确保企业在快速变化的数字世界中,实现安全且可持续的创新至关…...
设计秒杀系统(高并发的分布式系统)
学海无涯,志当存远。燃心砺志,奋进不辍。 愿诸君得此鸡汤,如沐春风,事业有成。 若觉此言甚善,烦请赐赞一枚,共励学途,同铸辉煌! 思路 处理高并发 流量削峰:限流…...
C# 打印模板设计-ACTIVEX打印控件-多模板加载
一、启动软件 using System; using System.Collections.Generic; using System.Windows.Forms; using System.Data;namespace Print {static class Program{/// <summary>/// 应用程序的主入口点。/// </summary>[STAThread]static void Main(){//使用模板前必须…...
华为HCIE方向那么多应该如何选择?
在华为认证体系里,HCIE作为最高等级的认证,是ICT领域专业实力的有力象征。HCIE设置了多个细分方向,这些方向宛如不同的专业赛道,为期望在ICT行业深入发展的人提供了丰富的选择。今天,咱们就来好好聊聊华为HCIE方向的相…...
五子棋游戏
五子棋 - deveco <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>五子棋 - deveco</title>…...
Vue3.5 企业级管理系统实战(十):面包屑导航组件
1 breadcrumb 组件 1.1 安装插件 path-to-regexp 首先,我们需要安装插件 path-to-regexp,以便在下面的面包屑组件中对路由地址进行解析。 path-to-regexp是一个 JavaScript 库,可将路径字符串转化为正则表达式,广泛用于 Web 开发…...
【python】OpenCV—Hand Detection
文章目录 1、功能描述2、代码实现3、效果展示4、完整代码5、参考6、其它手部检测和手势识别的方案 更多有趣的代码示例,可参考【Programming】 1、功能描述 基于 opencv-python 和 mediapipe 进行手部检测 2、代码实现 导入必要的库函数 import cv2 import media…...
[ComfyUI] SDXL Prompt Styler 自定义节点的作用解析
1. SDXL Prompt Styler 的位置与基本功能 在 ComfyUI 的 “新建节点” → “实用工具” 下,可以找到 Style 节点(SDXL Prompt Styler)。该节点的主要作用是对输入的描述进行结构化处理,并在转换为 Stable Diffusion XL (SDXL) 提示词时,自动补充风格相关的内容,使提示词…...
Oracle-rman restore遭遇RMAN-03002与ORA-19563
文章目录 在原DB上检查是否有重复的文件名:查看rman恢复的日志修正重名部分重新执行rman恢复结论: 在 RMAN 恢复过程中,遇到RMAN-03002连同ORA-19563:错误。 操作是将 Oracle 10.0.5的数据库备份从 RMAN備份恢复到另一台测试主机的同一个目录…...
FPGA中串行执行方式之使用时钟分频或延迟的方式
FPGA中串行执行方式之使用时钟分频或延迟的方式 在FPGA设计中,时钟分频和延迟是两种常用的技术,用于控制信号的时序或调整信号的频率。它们可以用来实现简单的串行逻辑、状态转移或其他需要时间控制的场景。 时钟分频(Clock Division) 基本原理:时钟分频是通过将输入…...
Dubbo 全面解析:从 RPC 核心到服务治理实践
一、分布式系统与 RPC 框架概述 在当今互联网时代,随着业务规模的不断扩大,单体架构已经无法满足高并发、高可用的需求,分布式系统架构成为主流选择。而在分布式系统中,远程服务调用(Remote Procedure Call࿰…...
JavaScript 调试入门指南
JavaScript 调试入门指南 一、调试准备阶段 1. 必备工具配置 浏览器套件:安装最新Chrome102+,开启实验性功能(地址栏输入chrome://flags/#enable-devtools-experiments)编辑器集成:VS Code安装以下扩展: JavaScript Debugger:支持浏览器与Node.js双端调试Error Lens:实…...
不能将下载行为传输到IDM
目录预览 一、问题描述二、原因分析三、解决方案四、参考链接 一、问题描述 安装IDM后,调用IDM下载软件显示:不能将下载行为传输到IDM,Error 0x80029C4A 二、原因分析 可能是识别浏览器插件不到,或者本地的插件版本不对导致的 三…...
spring security 认证流程分析
Spring Security 认证流程分析 Spring Security 的认证流程是一个模块化且可扩展的过程,核心围绕 过滤器链 和 认证组件 协作实现。以下是详细流程分析: 1. 请求拦截与过滤器链 • 入口:所有 HTTP 请求经过 Spring Security 的过滤器链。 •…...
Docker Compose 部署 Loki
官方文档:https://grafana.com/docs/loki/latest/setup/install/docker/ 环境准备 安装 Docker和Docker Compose 参考:https://qiangsh.blog.csdn.net/article/details/125375187 创建loki目录 mkdir -p /opt/loki/config mkdir -p /data/monitoring…...
nuxt3 seo优化
在 Nuxt3 中,通过 nuxtjs/seo、nuxtjs/sitemap 和 nuxtjs/robots 模块可以生成包含动态链接的站点地图(sitemap.xml),但具体是“实时生成”还是“部署时生成”,取决于你的配置方式和数据更新频率。以下是具体分析&…...
CentOS 8 Stream 配置在线yum源参考 —— 筑梦之路
CentOS 8 Stream ISO 文件下载地址:http://mirrors.aliyun.com/centos-vault/8-stream/isos/x86_64/CentOS-Stream-8-20240603.0-x86_64-dvd1.isoCentOS 8 Stream 网络引导ISO 文件下载地址:http://mirrors.aliyun.com/centos-vault/8-stream/isos/x86_6…...
uniapp 在app上 字体如何不跟着系统字体大小变
在UniApp开发中,默认情况下App的字体可能会跟随系统字体设置而变化。如果你希望保持固定的字体样式,不随系统字体设置改变,可以采用以下几种方法: 方法一:全局CSS设置 在App.vue的样式中添加以下CSS: /*…...
leetcode141.环形链表
直接快慢指针,如果有环,那么快指针一定会在成环的起始点与慢指针相遇 /*** Definition for singly-linked list.* class ListNode {* int val;* ListNode next;* ListNode(int x) {* val x;* next null;* }* }*/ pu…...
【HTML5游戏开发教程】零基础入门合成大西瓜游戏实战 | JS物理引擎+Canvas动画+完整源码详解
《从咖啡杯到财务自由:一个程序员的合成之旅——当代码遇上物理引擎的匠心之作》 🌟 这是小游戏开发系列的第四篇送福利文章,感谢一路以来支持和关注这个项目的每一位朋友! 💡 文章力求严谨,但难免有疏漏之…...
【C#语言】深入理解C#多线程编程:从基础到高性能实践
文章目录 ⭐前言⭐一、多线程的本质价值🌟1、现代计算需求🌟2、C#线程演进史 ⭐二、线程实现方案对比🌟1、传统线程模型🌟2、现代任务模型(推荐)🌟3、异步编程范式 ⭐三、线程安全深度解析&…...
短信验证码安全需求设计
背景: 近期发现部分系统再短信充值频繁,发现存在恶意消耗短信额度现象,数据库表排查,发现大量非合法用户非法调用短信接口API导致额度耗尽。由于系统当初设计存在安全缺陷,故被不法分子进行利用,造成损失。…...