当前位置: 首页 > news >正文

面向对象与过程介绍

一、面向对象具体介绍

(1)基本概念

1.对象

在面向对象编程中,对象是对现实世界中事物的抽象,它具有状态(属性)和行为(方法)。在 TypeScript 中,我们可以通过类、接口等方式来创建和描述对象。以下从不同角度来深入理解面向对象中的对象概念,并结合 TypeScript 代码示例:

1. 对象是类的实例

类是创建对象的模板,对象是类的具体实例,每个对象都拥有类中定义的属性和方法。

class Car {// 属性,描述对象的状态brand: string;color: string;// 构造函数,用于初始化对象的属性constructor(brand: string, color: string) {this.brand = brand;this.color = color;}// 方法,描述对象的行为start() {console.log(`${this.brand} ${this.color} car is starting.`);}
}// 创建Car类的实例(对象)
const myCar = new Car('Toyota', 'Blue');
// 访问对象的属性
console.log(myCar.brand);
// 调用对象的方法
myCar.start();

在上述代码中,Car类定义了brandcolor属性来描述汽车的特征,start方法表示汽车启动的行为。myCarCar类的一个实例对象,通过new关键字创建,它拥有brandcolor属性的值,并且可以调用start方法。

2. 对象符合接口定义的形状

接口用于定义对象应该具有的结构,对象需要满足接口中规定的属性和方法。

interface Person {name: string;age: number;speak(message: string): void;
}// 创建符合Person接口的对象
const student: Person = {name: 'Alice',age: 20,speak(message: string) {console.log(`${this.name} says: ${message}`);}
};student.speak('Hello, world!');

这里Person接口定义了name(字符串类型)、age(数字类型)属性以及speak方法。student对象满足Person接口的定义,因此可以被赋值给Person类型的变量,并且能够调用speak方法。

3. 对象的多态性体现

多态性是指同一个方法在不同的对象上可以有不同的表现形式。在 TypeScript 中,可以通过继承和重写方法来实现多态。

class Animal {speak() {console.log('An animal makes a sound.');}
}class Dog extends Animal {speak() {console.log('The dog barks.');}
}class Cat extends Animal {speak() {console.log('The cat meows.');}
}// 定义一个函数,接受Animal类型的对象
function makeAnimalSpeak(animal: Animal) {animal.speak();
}const dog = new Dog();
const cat = new Cat();makeAnimalSpeak(dog); // 输出: The dog barks.
makeAnimalSpeak(cat); // 输出: The cat meows.

在这个示例中,Dog类和Cat类都继承自Animal类,并且重写了speak方法。makeAnimalSpeak函数接受Animal类型的对象作为参数,当传入不同的子类对象(dogcat)时,调用speak方法会表现出不同的行为,这就是多态性的体现。

4. 对象的封装性

封装是将对象的属性和实现细节隐藏起来,只对外提供公共的访问接口。在 TypeScript 中,可以通过访问修饰符(publicprivateprotected)来实现封装。

class BankAccount {// 私有属性,只能在类内部访问private balance: number;constructor(initialBalance: number) {this.balance = initialBalance;}// 公共方法,用于访问和修改私有属性deposit(amount: number) {this.balance += amount;console.log(`Deposited ${amount}. New balance: ${this.balance}`);}withdraw(amount: number) {if (amount <= this.balance) {this.balance -= amount;console.log(`Withdrew ${amount}. New balance: ${this.balance}`);} else {console.log('Insufficient funds.');}}
}const account = new BankAccount(1000);
account.deposit(500);
account.withdraw(200);
// console.log(account.balance); // 错误:balance是私有属性,无法直接访问

BankAccount类中的balance属性被声明为private,外部无法直接访问。通过depositwithdraw公共方法来对balance进行操作,实现了数据的封装,保护了对象的内部状态。

2.类

在面向对象编程中,类(class)是一种对具有相同属性和行为的对象的抽象描述,它是创建对象的模板。在 TypeScript 中,类的概念得到了很好的支持,下面详细介绍并通过示例来加深理解:

1. 类的基本定义与实例化

类包含属性和方法,属性用于描述对象的状态,方法用于定义对象的行为。通过class关键字来定义类,使用new关键字来创建类的实例。

class Person {// 属性name: string;age: number;// 构造函数,用于初始化对象的属性constructor(name: string, age: number) {this.name = name;this.age = age;}// 方法introduce() {console.log(`My name is ${this.name}, and I'm ${this.age} years old.`);}
}// 创建Person类的实例
const person1 = new Person('Alice', 30);
person1.introduce();

在上述代码中,Person类定义了nameage属性以及introduce方法。constructor是构造函数,在创建对象时会自动调用,用于初始化对象的属性。person1Person类的一个实例,调用introduce方法可以输出相应的信息。

2. 类的访问修饰符

TypeScript 提供了publicprivateprotected三种访问修饰符,用于控制类成员的访问权限:

  • public:默认修饰符,成员在类内外都可以访问。
  • private:成员只能在类内部访问,外部无法直接访问。
  • protected:成员在类内部以及子类中可以访问,外部不能访问。
class Animal {public name: string;private sound: string;protected legs: number;constructor(name: string, sound: string, legs: number) {this.name = name;this.sound = sound;this.legs = legs;}makeSound() {console.log(`${this.name} makes a ${this.sound} sound.`);}
}class Dog extends Animal {constructor(name: string) {super(name, 'woof', 4);}showLegs() {console.log(`${this.name} has ${this.legs} legs.`); // 可以访问protected属性}
}const dog = new Dog('Buddy');
dog.makeSound();
dog.showLegs();
// console.log(dog.sound); // 错误:sound是私有属性,无法访问

在这个例子中,Animal类的namepublic属性,soundprivate属性,legsprotected属性。Dog类继承自Animal类,在Dog类中可以访问protectedlegs属性,但不能访问privatesound属性。

3. 类的继承

继承允许一个类(子类)从另一个类(父类)获取属性和方法,子类可以重写父类的方法或添加新的属性和方法。通过extends关键字实现继承。

class Shape {calculateArea(): number {return 0;}
}class Rectangle extends Shape {width: number;height: number;constructor(width: number, height: number) {super();this.width = width;this.height = height;}calculateArea(): number {return this.width * this.height;}
}const rectangle = new Rectangle(5, 3);
console.log(rectangle.calculateArea());

这里Rectangle类继承自Shape类,并重写了calculateArea方法,以实现计算矩形面积的功能。

4. 静态成员

类还可以有静态属性和静态方法,它们属于类本身,而不是类的实例。通过static关键字来定义静态成员。

class MathUtils {static PI: number = 3.14159;static calculateCircleArea(radius: number): number {return this.PI * radius * radius;}
}console.log(MathUtils.PI);
console.log(MathUtils.calculateCircleArea(5));

MathUtils类定义了静态属性PI和静态方法calculateCircleArea,可以直接通过类名来访问,而不需要创建类的实例。

通过以上这些概念和示例,可以较为全面地理解 TypeScript 中类的概念及其在面向对象编程中的应用。

3.封装

在面向对象编程中,封装(Encapsulation) 是将数据(属性)和操作数据的方法(行为)绑定在一起,并隐藏对象的内部实现细节,仅对外提供必要的访问接口。封装的核心目标是保护数据不被外部随意修改,提高代码的安全性和可维护性。

1. 封装的核心思想
  • 数据隐藏:通过访问修饰符(如 privateprotected)限制属性和方法的可见性,防止外部直接访问或修改内部数据。
  • 接口暴露:提供公共方法(如 gettersetter)来控制对内部数据的访问,确保数据的合法性。
2. TypeScript 中的封装实现
示例 1:使用 private 修饰符隐藏属性
class BankAccount {// 私有属性,外部无法直接访问private balance: number = 0;// 公共方法:存款public deposit(amount: number) {if (amount > 0) {this.balance += amount;console.log(`存款成功,当前余额:${this.balance}`);} else {console.log("存款金额必须大于0");}}// 公共方法:取款public withdraw(amount: number) {if (amount > 0 && amount <= this.balance) {this.balance -= amount;console.log(`取款成功,当前余额:${this.balance}`);} else {console.log("余额不足或取款金额无效");}}// 公共方法:查询余额(只读)public getBalance() {return this.balance;}
}// 使用示例
const account = new BankAccount();
account.deposit(1000);    // ✅ 存款成功
account.withdraw(500);    // ✅ 取款成功
// account.balance = 10000; // ❌ 错误:无法直接访问私有属性
console.log(account.getBalance()); // ✅ 只能通过公共方法获取余额

说明

  • balance 被声明为 private,外部无法直接修改,只能通过 depositwithdraw 方法间接操作。
  • getBalance() 方法提供只读访问,确保余额数据的安全性。
示例 2:使用 gettersetter 控制属性访问
class User {private _age: number; // 私有属性constructor(age: number) {this._age = age;}// Getter:获取年龄get age() {return this._age;}// Setter:设置年龄(添加验证逻辑)set age(value: number) {if (value >= 0 && value <= 120) {this._age = value;} else {throw new Error("年龄必须在0到120之间");}}
}// 使用示例
const user = new User(25);
console.log(user.age); // ✅ 通过 getter 获取年龄
user.age = 30;         // ✅ 通过 setter 设置年龄
// user.age = 150;      // ❌ 抛出错误:年龄超出范围

说明

  • gettersetter 允许对属性访问进行更精细的控制,例如添加验证逻辑。
  • 外部通过 user.age 访问属性,而不需要显式调用 getset 方法。
示例 3:封装复杂业务逻辑
class ShoppingCart {private items: { name: string; price: number }[] = [];// 添加商品public addItem(name: string, price: number) {if (price <= 0) {throw new Error("商品价格必须大于0");}this.items.push({ name, price });console.log(`添加商品:${name},价格:${price}`);}// 计算总价(内部逻辑隐藏)public calculateTotal() {return this.items.reduce((total, item) => total + item.price, 0);}// 清空购物车public clear() {this.items = [];console.log("购物车已清空");}
}// 使用示例
const cart = new ShoppingCart();
cart.addItem("手机", 3000);
cart.addItem("耳机", 500);
console.log("总价:", cart.calculateTotal()); // 3500
cart.clear();

说明

  • items 数组被封装在类内部,外部无法直接修改,只能通过 addItemclear 等方法操作。
  • calculateTotal 方法隐藏了计算逻辑,外部只需调用方法获取结果。
3. 封装的优势
  1. 安全性:防止外部直接修改内部数据,避免非法操作。
  2. 可维护性:内部实现细节的修改不会影响外部代码,降低耦合度。
  3. 灵活性:可以在不改变外部接口的情况下,调整内部实现(例如优化计算逻辑)。
  4. 数据完整性:通过验证逻辑确保数据的有效性(如年龄范围、金额合法性)。
4. 总结

封装是面向对象编程的核心原则之一,通过 TypeScript 的访问修饰符(privateprotected)和属性访问器(gettersetter),可以实现对数据的严格控制和隐藏。合理的封装能够提高代码的安全性、可维护性和可扩展性,是构建高质量软件的关键。

4.继承

在面向对象编程中,继承(Inheritance) 是一种机制,允许一个类(子类 / 派生类)继承另一个类(父类 / 基类)的属性和方法,从而实现代码复用和层次化设计。继承的核心思想是 “is-a” 关系,即子类是父类的一种特殊类型。

1. 继承的核心概念
  • 父类(基类):定义通用属性和方法的类。
  • 子类(派生类):继承父类并可以扩展或修改其行为的类。
  • 方法重写(Override):子类重新实现父类的方法,以改变其行为。
  • 访问修饰符:控制子类对父类成员的访问权限(如 publicprotectedprivate)。
2. TypeScript 中的继承实现
示例 1:基本继承
// 父类:Animal
class Animal {constructor(public name: string) {}move(distance: number = 0) {console.log(`${this.name} moved ${distance}m.`);}
}// 子类:Dog(继承自 Animal)
class Dog extends Animal {bark() {console.log(`${this.name} says Woof!`);}
}// 使用示例
const dog = new Dog("Buddy");
dog.move(10); // 继承自 Animal 的方法
dog.bark();   // Dog 自己的方法

说明

  • Dog 类通过 extends 关键字继承了 Animal 类的所有 publicprotected 成员。
  • Dog 类可以添加自己的方法(如 bark()),扩展父类的功能。
示例 2:方法重写
class Bird extends Animal {constructor(name: string) {super(name); // 调用父类的构造函数}// 重写父类的 move 方法move(distance: number = 50) {console.log(`${this.name} flies ${distance}m.`);}
}// 使用示例
const bird = new Bird("Eagle");
bird.move(); // 输出: "Eagle flies 50m."

说明

  • 子类可以通过定义与父类同名的方法来重写父类的行为。
  • super 关键字用于调用父类的构造函数或方法。
示例 3:多级继承
class Mammal extends Animal {nurse() {console.log(`${this.name} nurses its young.`);}
}class Cat extends Mammal {purr() {console.log(`${this.name} purrs.`);}
}// 使用示例
const cat = new Cat("Whiskers");
cat.move(5);   // 继承自 Animal
cat.nurse();   // 继承自 Mammal
cat.purr();    // Cat 自己的方法

说明

  • 继承可以形成层级结构(如 Animal → Mammal → Cat),子类可以继承多级父类的特性。
示例 4:访问修饰符与继承
class Vehicle {protected brand: string; // 受保护的属性,子类可访问private wheels: number;  // 私有属性,子类不可访问constructor(brand: string, wheels: number) {this.brand = brand;this.wheels = wheels;}protected getWheels() {return this.wheels;}
}class Car extends Vehicle {constructor(brand: string) {super(brand, 4); // 调用父类构造函数}showDetails() {console.log(`Brand: ${this.brand}, Wheels: ${this.getWheels()}`);// this.wheels ❌ 错误:私有属性无法在子类中访问}
}// 使用示例
const car = new Car("Toyota");
car.showDetails(); // ✅ 可以访问 protected 成员
// car.brand ❌ 错误:protected 属性无法在类外部访问

说明

  • protected 成员可以被子类访问,但不能在类外部直接访问。
  • private 成员只能在定义它们的类内部访问,子类无法继承。
3. 继承的优势
  1. 代码复用:避免重复编写相同的代码,提高开发效率。
  2. 可维护性:修改父类的代码会影响所有子类,减少代码冗余。
  3. 多态性:通过继承实现方法重写,支持不同子类表现出不同行为。
  4. 层次化设计:通过类的继承关系构建清晰的类层次结构,便于理解和管理。
4. 继承 vs 组合

继承并非解决代码复用的唯一方式,组合(Composition) 也是一种常用的设计模式:

  • 继承:通过 extends 实现 “is-a” 关系,子类直接继承父类的属性和方法。
  • 组合:通过持有其他对象的引用实现 “has-a” 关系,更灵活但可能增加代码复杂度。

何时选择继承?

  • 当子类与父类确实存在 “is-a” 关系时(如 DogAnimal 的一种)。
  • 需要通过方法重写实现多态行为时。

何时选择组合?

  • 当需要在运行时动态改变对象的行为时。
  • 当对象间的关系更适合描述为 “has-a” 而非 “is-a” 时(如 Car 拥有 Engine)。
5. 总结

继承是面向对象编程的重要特性之一,通过 TypeScript 的 extends 关键字,子类可以继承父类的属性和方法,并通过方法重写实现差异化。合理使用继承可以提高代码复用性和可维护性,但需注意避免过度继承导致的代码耦合。在实际开发中,应根据对象间的关系谨慎选择继承或组合。

5.多态

在面向对象编程中,多态(Polymorphism) 是指不同对象对同一消息(方法调用)做出不同响应的能力。多态允许通过统一的接口操作不同类型的对象,而这些对象会根据自身的实现逻辑表现出不同的行为。多态的核心思想是 “同一接口,多种实现”。

1. 多态的核心概念
  • 方法重写(Override):子类重新实现父类的方法,改变其行为。
  • 接口 / 抽象类:定义统一的接口或抽象方法,由不同的子类具体实现。
  • 父类引用指向子类对象:通过父类类型的变量引用子类实例,调用方法时会动态绑定到子类的实现。
2. TypeScript 中的多态实现
示例 1:基于继承的多态
// 父类:Shape
abstract class Shape {abstract calculateArea(): number; // 抽象方法,子类必须实现// 通用方法,调用子类的具体实现displayArea() {console.log(`Area: ${this.calculateArea()}`);}
}// 子类:Circle
class Circle extends Shape {constructor(private radius: number) {super();}calculateArea() {return Math.PI * this.radius ** 2;}
}// 子类:Rectangle
class Rectangle extends Shape {constructor(private width: number, private height: number) {super();}calculateArea() {return this.width * this.height;}
}// 使用示例
function printArea(shape: Shape) {shape.displayArea(); // 调用子类的实现
}const circle = new Circle(5);
const rectangle = new Rectangle(4, 6);printArea(circle);    // 输出: Area: 78.53981633974483
printArea(rectangle); // 输出: Area: 24

说明

  • Shape 是抽象类,定义了抽象方法 calculateArea(),子类必须实现该方法。
  • printArea 函数接受 Shape 类型的参数,调用 displayArea() 时会根据实际对象类型动态调用子类的实现。
示例 2:基于接口的多态
// 接口:Animal
interface Animal {speak(): string;
}// 实现类:Dog
class Dog implements Animal {speak() {return "Woof!";}
}// 实现类:Cat
class Cat implements Animal {speak() {return "Meow!";}
}// 使用示例
function makeSound(animal: Animal) {console.log(animal.speak());
}const dog = new Dog();
const cat = new Cat();makeSound(dog); // 输出: Woof!
makeSound(cat); // 输出: Meow!

说明

  • Animal 接口定义了 speak() 方法,DogCat 类实现该接口并提供不同的实现。
  • makeSound 函数接受 Animal 类型的参数,根据传入的实际对象调用对应的方法。
示例 3:参数多态(泛型)
// 泛型函数:比较两个对象的大小
function compare<T extends { length: number }>(a: T, b: T): number {return a.length - b.length;
}// 使用示例
const str1 = "apple";
const str2 = "banana";
console.log(compare(str1, str2)); // -1 (5 - 6)const arr1 = [1, 2, 3];
const arr2 = [4, 5];
console.log(compare(arr1, arr2)); // 1 (3 - 2)

说明

  • 泛型函数 compare 接受任何具有 length 属性的类型,实现了对不同类型对象的统一操作。
3. 多态的优势
  1. 代码灵活性:通过统一接口处理不同类型的对象,降低代码耦合度。
  2. 可扩展性:新增子类时无需修改现有代码,符合开闭原则。
  3. 可维护性:将公共逻辑放在父类 / 接口中,子类专注于具体实现。
  4. 可读性:代码更符合人类思维,例如 “所有动物都会说话,但方式不同”。
4. 多态的实现条件
  1. 继承或接口实现:子类继承父类或实现接口。
  2. 方法重写:子类重写父类的方法。
  3. 父类引用指向子类对象:通过父类类型的变量引用子类实例。
5. 多态 vs 重载
  • 多态(运行时):同一个方法在不同对象上表现出不同行为,通过方法重写实现。
  • 重载(编译时):同一个类中多个同名方法,参数列表不同,编译器根据参数类型选择调用。

TypeScript 中的方法重载示例

class Calculator {// 方法重载签名add(a: number, b: number): number;add(a: string, b: string): string;// 实际实现add(a: any, b: any): any {if (typeof a === "number" && typeof b === "number") {return a + b;}if (typeof a === "string" && typeof b === "string") {return a + b;}throw new Error("Unsupported types");}
}const calc = new Calculator();
console.log(calc.add(1, 2));    // 3(编译时根据参数类型选择实现)
console.log(calc.add("a", "b")); // "ab"
6. 总结

多态是面向对象编程的核心特性之一,通过 TypeScript 的继承、接口和泛型机制,可以实现灵活且可扩展的多态设计。合理使用多态能够使代码更具适应性,支持未来的功能扩展,同时保持代码的清晰和可维护性。在实际开发中,多态常用于实现插件系统、策略模式、依赖注入等场景。

(2)编程语言支持

不同的面向对象编程语言具有各自的特性和语法:

  • Java:是一种广泛使用的面向对象编程语言,具有强类型、跨平台、自动内存管理等特性。它强调代码的可读性和可维护性,通过接口、抽象类等机制实现多态性和代码复用。例如,在 Java 的图形界面编程中,通过继承JFrame类来创建窗口,通过实现ActionListener接口来处理按钮点击事件。
  • Python:是一种简洁、灵活的面向对象编程语言,支持多种编程范式。Python 的面向对象特性相对较为动态,类和对象的定义较为简洁,同时支持多重继承。在 Python 的 Web 开发中,使用 Flask 或 Django 框架时,会大量用到面向对象的编程方式来定义路由、处理请求等。
  • C++:是一种高效的面向对象编程语言,结合了面向对象、面向过程和泛型编程的特性。它提供了对底层硬件的直接访问,同时具有强大的类和对象机制,支持模板、运算符重载等高级特性。在游戏开发中,C++ 常用于开发游戏引擎,通过面向对象的设计来实现游戏中的角色、场景、道具等各种元素。
  • TypeScript:是 JavaScript 的强类型超集,通过静态类型检查增强了代码的可靠性与可维护性。它保留了 JavaScript 的动态特性,同时引入了类、接口、泛型等面向对象概念,使代码更具结构化和可扩展性。例如,在前端框架(如 Angular、React)中,TypeScript 可通过类型定义规范组件接口,通过装饰器简化依赖注入,提升团队协作效率。

(3)设计模式

  • 创建型模式:主要用于对象的创建过程,比如单例模式,确保一个类只有一个实例,并提供一个全局访问点。在系统中,像配置文件管理类,可能只需要一个实例来读取和存储配置信息,就可以使用单例模式。
  • 结构型模式:关注如何将类或对象组合成更大的结构,例如代理模式。当需要控制对某个对象的访问,或者需要在访问对象前后添加一些额外的处理时,可以使用代理模式。如在网络访问中,通过代理对象来处理网络请求,在请求前后可以进行日志记录、权限验证等操作。
  • 行为型模式:主要用于处理对象之间的交互和职责分配,比如观察者模式。在图形界面应用中,当一个组件的状态发生变化时,需要通知其他相关组件进行更新,就可以使用观察者模式,让相关组件作为观察者,观察目标组件的状态变化并做出相应反应。

(4)应用场景

  • 图形界面设计:在图形界面设计中,每个界面元素都可以看作是一个对象,如窗口、按钮、文本框等。以 Java 语言为例,通过使用 Swing 或 JavaFX 库,创建JFrame(窗口)、JButton(按钮)等对象,利用面向对象的继承和多态特性,可以方便地实现界面的布局和交互功能。
  • 游戏开发:游戏中的角色、道具、场景等都可以用对象来表示。以 C# 语言为例,在 Unity 游戏开发引擎中,每个游戏对象都有自己的属性(如位置、速度、生命值)和行为(如移动、攻击、死亡)。通过面向对象的编程方式,可以轻松地管理游戏中的各种元素,实现复杂的游戏逻辑。
  • 企业级应用开发:在企业级应用中,经常需要处理各种业务对象,如用户、订单、商品等。以 Java 语言为例,使用 Spring 框架可以方便地进行面向对象的开发,将业务逻辑封装在各个 Java 类中,通过依赖注入等机制实现对象之间的协作,提高代码的可维护性和可扩展性。
  • 移动应用开发:在移动应用开发中,无论是 iOS 还是 Android 平台,都广泛采用面向对象的编程语言。以 Swift 语言为例,在 iOS 应用开发中,通过创建UIViewController(视图控制器)、UITableView(表格视图)等对象,利用面向对象的特性来构建用户界面和实现应用的功能。
  • 系统模拟与仿真:在科学研究和工程领域,经常需要对复杂的系统进行模拟和仿真。以 Python 语言为例,使用面向对象的方式可以将系统中的各个组件抽象为对象,通过模拟对象之间的交互来研究系统的行为。例如,模拟交通流量系统,将车辆、道路、信号灯等都作为对象进行建模。

(5)开发流程

  • 需求分析:对现实世界中的问题域进行分析,识别出相关的对象、对象的属性和行为,以及对象之间的关系。例如,在开发一个图书馆管理系统时,需要分析出图书、读者、管理员等对象,以及它们各自的操作和相互之间的关联。
  • 设计阶段:包括类的设计、类层次结构的设计以及对象之间交互的设计。根据需求分析的结果,设计出各个类的属性和方法,确定类之间的继承、聚合等关系,并绘制类图等设计文档来描述系统的结构。例如,设计 “图书” 类,包含书名、作者、ISBN 等属性,以及借阅、归还等方法。
  • 编码实现:使用具体的面向对象编程语言,将设计阶段的成果转化为实际的代码。按照类的设计来定义类和创建对象,实现各个方法的具体逻辑。例如,用 Java 语言实现图书馆管理系统中 “读者” 类的注册、登录等功能。
  • 测试调试:对面向对象的程序进行测试,检查对象的行为是否符合预期,类之间的交互是否正确。通过调试来解决发现的问题,确保系统的稳定性和正确性。例如,测试 “借阅” 功能是否能正确更新图书的状态和读者的借阅记录。
  • 维护扩展:在系统运行过程中,根据新的需求对系统进行维护和扩展。由于面向对象的代码具有较好的封装性和可扩展性,可以相对容易地添加新的类、修改现有类的方法来满足变化的需求。例如,当图书馆需要增加一种新的图书类型时,可在原有系统中添加相应的类来处理。

(6)优势与局限性

  • 优势:能够更自然地模拟现实世界,使代码结构清晰,易于理解和维护;通过继承和多态实现代码复用,提高了开发效率;具有良好的封装性,增强了代码的安全性和可靠性;便于团队协作开发,不同的开发人员可以负责不同的类和模块。
  • 局限性:对于一些简单的问题,使用面向对象可能会使代码变得复杂,增加不必要的开销;在某些性能关键的场景下,可能需要进行额外的优化;对开发人员的面向对象设计能力要求较高,如果设计不合理,可能导致系统结构混乱。

(7)与其他编程范式比较

  • 与面向过程编程比较:面向过程编程强调的是过程和步骤,将程序看作是一系列函数的调用,数据和操作数据的函数是分离的。而面向对象编程将数据和操作数据的方法封装在对象中,更注重数据的完整性和对象之间的交互。例如,在处理学生成绩管理时,面向过程可能是通过一系列函数来分别处理成绩的录入、计算平均分等操作,而面向对象则是将学生成绩作为学生对象的一个属性,通过学生对象的方法来进行成绩相关的操作。
  • 与函数式编程比较:函数式编程强调函数的纯性和不可变性,将计算看作是函数的组合,避免副作用。面向对象编程则更侧重于对象的状态和行为,对象的状态可以改变。例如,在函数式编程中,计算一个列表中元素的总和会使用纯函数来处理,不会改变列表本身。而在面向对象编程中,可能会有一个列表对象,通过向列表中添加元素等操作来改变其状态。

(8)编程原则

  • 单一职责原则(SRP):一个类应该只有一个引起它变化的原因,即一个类只负责一项职责。例如,一个 “用户管理类” 应该只负责与用户相关的操作,如用户注册、登录、信息修改等,而不应该涉及订单处理等其他职责。
  • 开闭原则(OCP):软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。这意味着当需要增加新功能时,应该通过扩展现有代码来实现,而不是修改已有的代码。例如,在一个图形绘制系统中,当需要增加一种新的图形时,应该通过增加新的图形类来实现,而不是修改现有的图形绘制代码。
  • 里氏替换原则(LSP):子类对象能够替换其父类对象,并且程序的行为不会发生改变。这要求子类必须遵循父类的接口契约,并且在实现父类方法时,不能改变方法的前置条件和后置条件。例如,如果有一个 “矩形” 类和它的子类 “正方形” 类,那么在使用 “矩形” 类的地方,应该能够用 “正方形” 类来替换,而不会出现错误。
  • 接口隔离原则(ISP):客户端不应该依赖它不需要的接口。一个类对另一个类的依赖应该建立在最小的接口上。例如,一个 “打印机” 类可能有打印、扫描、复印等功能,但如果一个客户端只需要使用打印功能,那么它不应该依赖包含所有功能的接口,而应该使用只包含打印功能的接口。
  • 依赖倒置原则(DIP):高层模块不应该依赖低层模块,两者都应该依赖抽象;抽象不应该依赖细节,细节应该依赖抽象。例如,在一个电商系统中,“订单处理模块”(高层模块)不应该直接依赖 “数据库存储模块”(低层模块),而是应该依赖一个抽象的 “数据存储接口”,这样可以方便地更换不同的存储实现,如从数据库存储改为文件存储。

(9)开发工具与框架

  • 集成开发环境(IDE):如 Eclipse、IntelliJ IDEA 等,为面向对象编程提供了强大的开发支持,包括代码自动完成、语法检查、调试工具、版本控制集成等功能,提高了开发效率。例如,在 IntelliJ IDEA 中,可以方便地创建、编辑和管理 Java 类,通过调试工具可以快速定位和解决代码中的问题。
  • 框架:许多面向对象的框架为特定领域的开发提供了基础架构和工具。如 Spring 框架,用于 Java 企业级应用开发,提供了依赖注入、面向切面编程等功能,帮助开发者更方便地构建复杂的应用系统。在 Web 开发中,Django(Python)和 Spring Web MVC(Java)等框架基于面向对象的设计,提供了处理请求、视图渲染等功能,使 Web 应用的开发更加高效。

(10)在非编程领域的应用

  • 系统分析与设计:在软件工程中,面向对象的分析与设计方法(OOAD)被广泛应用于软件系统的需求分析和设计阶段。通过识别系统中的对象、类、关系和行为,绘制用例图、类图、序列图等模型,帮助开发团队更好地理解系统需求,设计出合理的系统架构。例如,在设计一个医院管理系统时,使用 OOAD 方法可以分析出病人、医生、科室等对象,以及它们之间的关系和交互。

  • 数据库设计:面向对象的数据库设计方法将数据库中的数据看作是对象的集合,每个对象具有属性和行为。通过将现实世界中的实体映射为数据库中的表和对象,使用面向对象的数据库管理系统(OODBMS)或对象关系映射(ORM)框架,可以更方便地进行数据的存储、查询和管理。例如,Hibernate 是一个流行的 Java ORM 框架,它允许开发者使用面向对象的方式来操作关系型数据库。

  • 项目管理:面向对象的思维方式也可以应用于项目管理中。将项目中的各个任务、资源、人员等看作是对象,每个对象具有自己的属性和行为。通过分析这些对象之间的关系和交互,制定合理的项目计划和管理策略,提高项目的成功率。例如,在一个软件开发项目中,将开发人员、测试人员、项目经理等看作是不同的对象,分析他们之间的协作关系,以更好地协调项目进度。

  • 游戏开发:游戏中的角色、道具、场景等都可以看作是对象。通过面向对象的设计,可以方便地管理这些对象的属性和行为,实现游戏的逻辑和交互。例如,一个角色扮演游戏中,每个角色都有自己的生命值、攻击力、技能等属性和行为,通过类来定义角色的共性,通过对象来表示具体的角色实例。

  • 人工智能与机器学习:在一些机器学习算法中,如神经网络,会使用面向对象的方式来表示神经元、神经网络层等概念。每个神经元可以看作是一个对象,具有输入、输出、权重等属性和计算激活值等行为。通过面向对象的设计,可以方便地构建和管理复杂的神经网络模型。

  • 移动应用开发:无论是 iOS 应用开发(使用 Swift 或 Objective - C 语言)还是 Android 应用开发(使用 Java 或 Kotlin 语言),都广泛采用面向对象的编程方式。例如,在 Android 应用中,通过 Activity 类来表示应用的界面,通过 Intent 对象来实现界面之间的跳转和数据传递。

二、面向对象与面向过程和区别和联系

(1)区别

1.具体介绍

  • 编程思维方式

    • 面向对象:将现实世界中的事物抽象为对象,每个对象都具有属性和行为。例如,在一个游戏中,把玩家、怪物、武器等都看作是具有各自属性(如生命值、攻击力)和行为(如移动、攻击)的对象,通过对象之间的交互来实现游戏的各种功能。
    • 面向过程:以过程为中心,将问题分解为一系列的步骤和函数,按照顺序依次执行这些步骤来完成任务。例如,在一个简单的计算器程序中,按照输入数字、选择运算符号、再输入数字、计算结果并输出的过程来编写代码。
  • 代码结构

    • 面向对象:代码结构更加模块化和层次化,由多个类和对象组成。类是对象的模板,定义了对象的属性和方法,对象是类的实例。不同的类之间可以通过继承、实现接口等关系进行关联,形成复杂的层次结构。例如,在一个图形绘制程序中,可能有Shape类作为所有图形的基类,Circle类、Rectangle类等继承自Shape类,每个类都有自己的属性(如半径、边长)和方法(如绘制图形、计算面积)。
    • 面向过程:代码结构通常是由一系列函数和数据结构组成,函数之间通过参数传递和全局变量进行数据交互。例如,在一个文件处理程序中,可能有打开文件、读取文件内容、处理文件内容、关闭文件等函数,这些函数共同协作来完成文件处理的任务。
  • 数据和方法的关系

    • 面向对象:数据(属性)和对数据的操作(方法)被封装在对象内部,对象对外提供一些公共的方法来访问和修改其内部数据,实现了数据的隐藏和保护。例如,一个Person类有nameage等属性,以及setName()getName()setAge()getAge()等方法来操作这些属性,外部代码只能通过这些方法来访问和修改Person对象的属性,而不能直接访问对象内部的数据。
    • 面向过程:数据和方法是分离的,数据通常以全局变量或数据结构的形式存在,方法(函数)通过参数来操作这些数据。例如,在一个排序程序中,数据是一个数组,排序函数接受这个数组作为参数,对数组进行排序操作,但数组本身和排序函数是相互独立的,函数可以独立于数据存在。
  • 可维护性和可扩展性

    • 面向对象:具有更好的可维护性和可扩展性。当需要添加新功能或修改现有功能时,可以通过在现有类的基础上进行扩展或修改,而不会对其他不相关的部分产生太大影响。例如,在一个电商系统中,如果要添加一种新的商品类型,只需要创建一个新的商品类,继承自Product类,并实现相应的方法即可,不会影响到其他商品类型和系统的其他部分。
    • 面向过程:在维护和扩展方面相对较为困难,因为函数之间的调用关系可能比较复杂,当需要修改某个功能时,可能会涉及到多个函数的修改,容易导致牵一发而动全身的情况。例如,在一个已经编写好的文件处理程序中,如果要添加一种新的文件格式处理功能,可能需要在多个函数中添加相应的代码来判断和处理新的文件格式,容易引入错误。
    • 类与对象:代码围绕 “类” 和 “对象” 组织,类定义了对象的属性和方法,对象是类的实例。
    • 模块化:通过类的封装实现高内聚、低耦合,不同类负责不同职责(如 MVC 架构中的 Model、View、Controller)。
    • 层次结构:通过继承形成类的层次体系(如基类→子类),便于代码复用和扩展。
  • 面向过程

    • 函数与数据:代码由一组函数和全局数据组成,函数直接操作数据。
    • 线性流程:程序按照 “主函数→子函数” 的顺序执行,逻辑流程清晰但扩展性较差。
    • 扁平化结构:函数之间通过参数传递数据,缺乏明确的层次关系,可能导致代码冗余。

示例对比

  • OOP:设计一个电商系统时,将UserProductOrder等抽象为类,每个类封装自身行为(如User.login()Order.calculateTotal())。
  • POP:用函数实现相同功能时,可能需要定义loginUser()calculateOrderTotal()等独立函数,数据与操作分离。

2. 数据访问控制

  • 面向对象
    • 访问修饰符:通过privateprotectedpublic限制数据的访问范围,实现数据隐藏。
    • 封装性:对象内部状态只能通过其公共方法(如 Getter/Setter)访问,避免外部直接修改。
  • 面向过程
    • 全局可见性:数据通常是全局变量或结构体,函数可以直接访问和修改,缺乏封装。
    • 安全性风险:全局数据可能被任意函数修改,导致数据不一致或难以调试。

示例对比

  • OOP

    class BankAccount {private balance: number; // 私有属性,外部无法直接访问public deposit(amount: number) {if (amount > 0) this.balance += amount;}
    }
    
  • POP

    float balance; // 全局变量,所有函数可直接修改
    void deposit(float amount) {if (amount > 0) balance += amount;
    }
    

3. 可维护性与可扩展性

  • 面向对象
    • 高内聚:类的职责单一,修改一个类很少影响其他部分。
    • 开闭原则:通过继承和接口实现 “对扩展开放,对修改关闭”。
    • 多态性:通过父类引用指向子类对象,实现运行时动态绑定,简化代码逻辑。
  • 面向过程
    • 低内聚:函数可能依赖多个全局变量,修改一处可能影响整个系统。
    • 扩展性差:新增功能需修改现有函数或添加新函数,可能破坏原有逻辑。
    • 代码重复:相似功能可能在多个函数中重复实现,维护成本高。

示例对比

  • OOP:若要为电商系统新增 “折扣计算” 功能,可通过继承Product类创建DiscountedProduct,无需修改原有代码。
  • POP:需修改所有涉及价格计算的函数,可能引入错误。

4. 适用场景

  • 面向对象
    • 大型复杂系统:如企业级应用、游戏引擎、框架开发。
    • 需要高度复用和扩展:如 UI 组件库、插件系统。
    • 多人协作项目:类的封装和接口定义便于分工。
  • 面向过程
    • 简单任务:如脚本工具、小型工具类程序。
    • 性能敏感场景:如嵌入式系统、算法实现(直接操作内存)。
    • 流程明确的系统:如批处理程序、数据处理管道。

典型案例

  • OOP:Java 的 Spring 框架、Python 的 Django 框架。
  • POP:Unix 命令行工具、C 语言编写的操作系统内核。

5. 编程范式的纯度

  • 面向对象
    • 支持继承、多态、封装等核心特性,强调 “一切皆对象”。
    • 语言层面提供类、接口、抽象类等语法支持(如 Java、C#)。
  • 面向过程
    • 以函数为中心,不强制要求对象概念。
    • 语言层面主要提供函数、结构体等基础元素(如 C、Fortran)。

混合范式
许多现代语言支持两种范式(如 Python、TypeScript),允许开发者根据场景灵活选择。例如:

  • TypeScript 中可使用类和接口实现 OOP,也可编写纯函数式代码。
  • Python 通过class支持 OOP,但也广泛使用函数式编程思想。

6.总结对比表

维度面向对象编程(OOP)面向过程编程(POP)
核心思想对象、类、继承、多态函数、流程、数据操作
代码组织类和对象的层次结构函数和全局数据的线性结构
数据控制封装、访问修饰符全局可见性,直接操作数据
复用方式继承、组合函数调用、代码复制
扩展性高(开闭原则)低(修改现有代码)
适用场景大型复杂系统、需要复用和扩展简单任务、性能敏感场景
典型语言Java、C++、Python、TypeScriptC、Fortran、Bash

(2)联系

1.概括与介绍

  • 相互补充:在实际的软件开发中,面向对象和面向过程并不是完全对立的,而是可以相互补充的。面向对象编程侧重于对问题域中的概念和实体进行建模,而面向过程编程侧重于对具体的算法和流程进行实现。例如,在一个面向对象的系统中,某个对象的方法内部可能会使用到面向过程的编程方式来实现具体的功能。
  • 基础与发展:面向过程编程是面向对象编程的基础。在学习面向对象编程之前,通常需要先掌握基本的编程概念和面向过程的编程方法,如变量、数据类型、函数、控制结构等。这些基础知识对于理解面向对象编程中的对象、方法、属性等概念是非常有帮助的。面向对象编程可以看作是在面向过程编程的基础上,进一步对代码进行组织和抽象,以更好地应对复杂的软件系统开发。
  • 目标相同:两者的最终目标都是为了开发出能够满足用户需求的软件系统。无论是面向对象编程还是面向过程编程,都需要分析问题、设计解决方案、编写代码、测试和维护软件,只是在实现方式和代码组织上有所不同。在实际应用中,需要根据具体的问题和项目需求来选择合适的编程范式或结合使用两种范式。

2.具体

  • 代码复用
    • 面向过程:主要通过函数来实现代码复用。将常用的功能封装成函数,在不同的地方可以通过调用函数来重复使用这些功能。例如,在多个不同的程序模块中,如果都需要进行数据排序,就可以编写一个通用的排序函数,在各个模块中调用该函数来实现排序功能。
    • 面向对象:通过类和继承来实现更高级的代码复用。类可以作为模板创建多个对象,这些对象具有相同的属性和方法。同时,子类可以继承父类的属性和方法,在子类中还可以对父类的方法进行重写或扩展,从而实现代码的复用和扩展。例如,在一个图形绘制系统中,Shape类中定义了绘制图形的基本方法,Circle类和Rectangle类继承自Shape类,它们可以复用Shape类的部分代码,同时又有自己独特的绘制方法。
  • 系统架构
    • 在面向过程的系统架构:通常是按照功能模块来划分的,每个模块由一组相关的函数组成,模块之间通过函数调用和数据传递来进行通信。例如,在一个简单的学生信息管理系统中,可能会有学生信息录入模块、查询模块、修改模块等,每个模块都包含若干个函数来完成相应的功能,模块之间通过传递学生信息数据来协同工作。
    • 在面向对象的系统架构:更强调对象之间的交互和协作。系统由多个不同类型的对象组成,这些对象通过发送消息来调用彼此的方法,从而实现系统的功能。例如,在一个电商系统中,有Customer对象、Order对象、Product对象等,Customer对象可以向Order对象发送创建订单的消息,Order对象又会与Product对象交互来获取商品信息等,通过这些对象之间的复杂交互来实现电商系统的各种功能。但在面向对象的系统中,也会存在一些底层的面向过程的代码,用于实现对象内部的一些具体操作,比如对象的属性赋值、简单的计算等。
  • 程序执行流程
    • 面向过程:程序的执行流程通常是按照函数的调用顺序依次执行的,从程序的入口开始,顺序执行各个函数,直到程序结束。例如,在一个计算两个数之和的程序中,先输入两个数,然后调用加法函数进行计算,最后输出结果,整个过程是按照顺序依次进行的。
    • 面向对象:程序的执行流程是由对象之间的消息传递驱动的。当一个对象接收到消息时,会调用相应的方法来处理该消息,方法的执行可能会导致其他对象接收到消息并进行相应的处理,从而形成复杂的执行流程。例如,在一个图形界面应用中,当用户点击一个按钮时,按钮对象会接收到一个点击事件消息,然后调用相应的事件处理方法,这个方法可能会修改界面上其他对象的属性或触发其他对象的方法调用,从而实现界面的动态更新和交互效果。然而,在对象方法的内部实现中,仍然可能会使用到面向过程的编程方式来完成一些具体的任务,如进行数据计算、逻辑判断等。

3.问题解决思路

  • 面向过程:将问题分解为一系列的步骤和操作,强调对问题求解过程的描述。它通常按照顺序执行这些步骤,通过函数之间的调用来完成整个任务。例如,在开发一个文件处理程序时,会先打开文件,然后按行读取文件内容,对每一行进行处理,最后关闭文件,每个步骤都由一个或多个函数来实现。
  • 面向对象:把问题域中的概念直接映射到程序中的对象,将问题分解为各个对象之间的交互和协作。它更关注对象的行为和职责,以及对象如何通过消息传递来完成任务。例如,在开发一个文件处理程序时,可能会有File对象、FileReader对象和FileProcessor对象等。File对象负责表示文件,FileReader对象负责读取文件内容,FileProcessor对象负责对读取到的内容进行处理,通过这些对象之间的协作来完成文件处理的任务。在分析和设计阶段,面向对象方法也会借鉴面向过程的一些思路,对对象内部的方法实现进行步骤化的分析和设计。

4.数据处理

  • 面向过程:数据通常作为函数的参数进行传递,函数对数据进行操作和处理。数据和操作数据的函数是分离的,数据的结构和处理方式在不同的函数中可能会有所不同。例如,在一个统计学生成绩的程序中,可能会有一个函数用于读取学生成绩数据,另一个函数用于计算平均分,再一个函数用于找出最高分,这些函数都以成绩数据作为参数,但各自对数据的处理方式不同。
  • 面向对象:数据被封装在对象内部,对象通过方法来操作自己的数据。对象将数据和对数据的操作封装在一起,形成一个相对独立的单元。例如,在一个学生成绩管理系统中,Student对象包含了学生的成绩数据以及计算成绩相关的方法,如计算平均分、获取最高分等方法,这些方法只能操作该Student对象内部的数据,体现了数据和操作的紧密结合。不过,面向对象编程中在某些情况下也会使用面向过程的方式来处理一些全局数据或临时数据,比如在对多个对象进行批量操作时,可能会使用循环等面向过程的结构来遍历对象集合并对每个对象进行相同的操作。

5.语言实现

  • 面向过程:许多编程语言都支持面向过程的编程方式,如 C 语言。在 C 语言中,通过函数、结构体等机制来实现面向过程的编程。函数是基本的代码单元,用于完成特定的功能,结构体用于组织相关的数据。
  • 面向对象:像 Java、C++、Python 等编程语言都支持面向对象编程。这些语言提供了类、对象、继承、多态等面向对象的特性。然而,即使是面向对象的编程语言,在底层实现上也会用到面向过程的思想和技术。例如,在 Java 虚拟机(JVM)中,字节码的执行过程就是按照一定的顺序和步骤进行的,这类似于面向过程的执行方式。而且在编写面向对象的代码时,也会用到一些面向过程的编程技巧,如使用循环、条件判断等来实现对象方法内部的逻辑。

6.软件工程实践

  • 面向过程:在软件开发的早期阶段,面向过程的方法被广泛应用。它对于一些功能相对简单、流程明确的系统开发非常有效,能够快速地将问题转化为可执行的代码。在软件维护阶段,对于一些局部功能的修改和扩展,面向过程的代码也比较容易理解和修改,只要找到对应的函数进行修改即可。
  • 面向对象:在大型软件系统的开发中,面向对象的方法具有明显的优势。它能够更好地应对复杂的业务逻辑和不断变化的需求,通过封装、继承和多态等特性,提高软件的可维护性、可扩展性和可复用性。在软件项目的团队开发中,面向对象的设计使得不同的开发人员可以专注于不同的对象或对象层次,便于分工协作。然而,面向对象的软件系统在底层实现和一些基础功能的开发中,仍然可能会借助面向过程的方法来提高效率和实现一些特定的功能。同时,在进行面向对象设计时,也需要考虑到面向过程的一些因素,如算法的效率、数据的存储和访问方式等,以确保整个系统的性能和质量。

相关文章:

面向对象与过程介绍

一、面向对象具体介绍 &#xff08;1&#xff09;基本概念 1.对象 在面向对象编程中&#xff0c;对象是对现实世界中事物的抽象&#xff0c;它具有状态&#xff08;属性&#xff09;和行为&#xff08;方法&#xff09;。在 TypeScript 中&#xff0c;我们可以通过类、接口等…...

UniGetUI 使用指南:轻松管理 Windows 软件(包括CUDA)

UniGetUI&#xff08;前身为 WingetUI&#xff09;是一款专门为 Windows 10&#xff08;x64&#xff09;和 Windows 11 系统打造的图形化包管理器界面工具。它集成了 Winget、Scoop、Chocolatey、Npm、Pip、Cargo、vcpkg、.NET Tool 和 PowerShell 等多种常用包管理器的功能&am…...

使用 NGINX 实现 HTTP Basic 认证ngx_http_auth_basic_module 模块

一、前言 在 Web 应用中&#xff0c;对部分资源进行访问控制是十分常见的需求。除了基于 IP 限制、JWT 验证、子请求校验等方式外&#xff0c;最经典也最简单的一种方式便是 HTTP Basic Authentication。NGINX 提供的 ngx_http_auth_basic_module 模块支持基于用户名和密码的基…...

014枚举之指针尺取——算法备赛

枚举是数据结构与算法中基本的操作&#xff0c;常用于解决序列的区间问题。算法界将"双指针"视为其重要分支&#xff0c;类似地当然还有"三指针"&#xff0c;“四指针”&#xff0c;最常见的还是“双指针”&#xff0c;我认为它们应统称为“指针尺取”。 双…...

Python小酷库系列:bidict,可以双向查询的dict

bidict&#xff0c;可以双向查询的dict 基本使用创建双向字典正向查找&#xff08;key → value&#xff09;反向查找&#xff08;value → key&#xff09;添加新项删除项 进阶功能使用 ~ &#xff08;.inverse&#xff09;获取逆映射使用 namedbidict 创建具名双向字典批量更…...

Android Compose 中 CompositionLocal 的全面解析与最佳实践

CompositionLocal 在 Android Compose 中的详细总结 核心概念 CompositionLocal 是 Jetpack Compose 提供的一种隐式数据传递机制&#xff0c;允许数据在组件树中向下传递&#xff0c;而无需显式地通过每个 Composable 函数的参数传递。 两种创建方式 1. staticComposition…...

Qt开发:容器组控件的介绍和使用

文章目录 一、Group Box&#xff08;分组框&#xff09;1.1 QGroupBox 简介1.2 基本用法1.3 设置为可勾选&#xff08;可启用/禁用子控件&#xff09;1.4 信号与槽连接&#xff08;监控勾选状态&#xff09;1.5 布局示例&#xff08;完整&#xff09; 二、Scroll Area&#xff…...

JS逆向入门案例1——集思录登录

JS逆向入门案例1——集思录登录 前言声明网站流程分析总结 前言 由于这段时间本职工作比较繁忙&#xff0c;没有很多空余的时间去研究各大厂的加密风控了&#xff0c;想起来自己刚接触js逆向走过坎坷&#xff0c;所以决定出一期js入门案例分析&#xff0c;为刚接触js逆向的小伙…...

ARM子程序和栈

微处理器中的栈由栈指针指向存储器中的栈顶来实现&#xff0c;当数据项入栈时&#xff0c;栈 指针向上移动&#xff0c;当数据项出栈时&#xff0c;栈指针向下移动。 实现栈时需要做出两个决定&#xff1a;一是当数据项进栈时是向低位地址方向向上生 长&#xff08;图a和图b&a…...

笔试专题(十五)

文章目录 排序子序列题解代码 消减整数题解代码 最长公共子序列(二)题解代码 排序子序列 题目链接 题解 1. 贪心 模拟 2. 1 2 3 2 2 应该是有两个排列子序列的&#xff0c;所以i n-1时ret 3. 把水平的位置和上升部分&#xff0c;水平位置和下降部分分为一个排列子序列 代…...

使用OpenCV 和 Dlib 进行卷积神经网络人脸检测

文章目录 引言1.准备工作2.代码解析2.1 导入必要的库2.2 加载CNN人脸检测模型2.3 加载并预处理图像2.4 进行人脸检测2.5 绘制检测结果2.6 显示结果 3.完整代码4.性能考虑5.总结 引言 人脸检测是计算机视觉中最基础也最重要的任务之一。今天我将分享如何使用dlib库中的CNN人脸检…...

某信服EDR3.5.30.ISO安装测试(一)

一、前言 1.某信服EDR3.5.30 以下简称“EDR3.5”&#xff0c;即统一端点安全管理系统aES(终端检测响应EDR)&#xff0c; 官网最新版&#xff1a;aES6.0.1R2&#xff0c;可下载的最低版本&#xff1a;EDR3.7.11R3&#xff0c; 下载地址&#xff1a;统一端点安全管理系统aES-…...

Dify 快速构建和部署基于LLM的应用程序

本文先对Dify做一个初步的认识&#xff0c;然后以一个实际的简单金融问答案例&#xff0c;配置chatflow 工作流。 一、Dify简介 如果你是第一次接触Dify&#xff0c;可以先创建一个简单的聊天助手&#xff0c;初步感觉一下&#xff0c;Dify在构建聊天问答类应用的过程。 比如…...

精益数据分析(40/126):移动应用商业模式的关键指标与盈利策略

精益数据分析&#xff08;40/126&#xff09;&#xff1a;移动应用商业模式的关键指标与盈利策略 在创业和数据分析的探索之路上&#xff0c;我们持续挖掘不同商业模式的内在规律&#xff0c;以寻求更好的发展机遇。今天&#xff0c;我们依旧秉持共同进步的理念&#xff0c;深…...

JavaScript 实现输入框的撤销功能

在 Web 开发中&#xff0c;为输入框添加撤销功能可以极大地提升用户体验&#xff0c;方便用户快速回滚到之前的输入状态。本文将通过一段简单的 HTML、CSS 和 JavaScript 代码&#xff0c;详细介绍如何实现输入框的撤销功能。 整体实现思路 利用 JavaScript 监听输入框的inpu…...

【C++】类和对象(一)

前言 类和对象第一部分知识包括定义访问限定符类域实例化this指针 本人其他文章&#xff1a;恋风诗 文章中的源码[gitte]&#xff1a;mozhengy 类和对象&#xff08;一&#xff09; 前言1. 类的定义引例1.1 类定义格式1.2 类的访问限定符1.3 类域 2. 实例化2.1 实例化概念2.2 …...

【Vue】Vue3源码解析与实现原理

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Vue 文章目录 1. Vue 3 架构概览1.1 模块化设计1.2 整体流程 2. 响应式系统2.1 响应式原理2.2 ref 和 reactive2.3 依赖收集与触发更新 3. 渲染系统3.1 虚拟DOM设计3.2 渲染管线3.3 Patch算法与Diff优化 4. 组件系统4.1 组件创建…...

黑马点评day02(缓存)

2、商户查询缓存 2.1 什么是缓存? 前言:什么是缓存? 就像自行车,越野车的避震器 举个例子:越野车,山地自行车,都拥有"避震器",防止车体加速后因惯性,在酷似"U"字母的地形上飞跃,硬着陆导致的损害,像个弹簧一样; 同样,实际开发中,系统也需要"避震…...

数据库MySQL学习——day9(聚合函数与分组数据)

文章目录 1. 聚合函数1.1 COUNT() 函数1.2 SUM() 函数1.3 AVG() 函数1.4 MIN() 函数1.5 MAX() 函数 2. GROUP BY 子句2.1 使用 GROUP BY 进行数据分组2.2 结合聚合函数 3. HAVING 子句3.1 使用 HAVING 过滤分组数据3.2 HAVING 和 WHERE 的区别 4. 实践任务4.1 创建一个销售表4.…...

为React组件库引入自动化测试:从零到完善的实践之路

为什么我们需要测试&#xff1f; 我们的ReactTypeScript业务组件库已经稳定运行了一段时间&#xff0c;主要承载各类UI展示组件&#xff0c;如卡片、通知等。项目初期&#xff0c;迫于紧张的开发周期&#xff0c;我们暂时搁置了自动化测试的引入。当时团队成员对组件逻辑了如指…...

数据结构——算法复杂度

一、数据结构定义 数据结构(Data Structure)是计算机存储、组织数据的⽅式&#xff0c;指相互之间存在⼀种或多种特定关系的数据元素的集合。没有⼀种单⼀的数据结构对所有⽤途都有⽤&#xff0c;所以我们要学各式各样的数据结构&#xff0c;如&#xff1a;线性表、树、图、哈希…...

Vue3响应式原理那些事

文章目录 1 响应式基础:Proxy 与 Reflect1.1 Proxy 代理拦截1.2 Reflect 确保 `this` 指向正确1.2.1 修正 `this` 指向问题1.2.2 统一的操作返回值1.3 与 Vue2 的对比2 依赖收集与触发机制2.1 全局依赖存储结构:WeakMap → Map → Set2.2 依赖收集触发时机2.3 依赖收集核心实…...

记9(Torch

目录 1、Troch 1、Troch 函数说明举例torch.tensor()torch.arange()创建张量创建一个标量&#xff1a;torch.tensor(42)创建一个一维张量&#xff1a;torch.tensor([1, 2, 3])创建一个二维张量&#xff1a;torch.tensor([[1, 2], [3, 4]])生成一维等差张量&#xff1a;语法&am…...

机器学习模型训练模块技术文档

一、模块结构概览 import numpy as np from sklearn.model_selection import cross_validate, learning_curve from sklearn.pipeline import make_pipeline from sklearn.svm import SVC from sklearn.neighbors import KNeighborsClassifier from sklearn.preprocessing imp…...

健康养生:从微小改变开始

养生不必大刀阔斧&#xff0c;几个微小改变&#xff0c;就能让健康慢慢扎根生活。晨起别急着洗漱&#xff0c;先花 5 分钟靠墙站立&#xff0c;拉伸脊柱、调整体态&#xff0c;唤醒身体。早餐把白米粥换成杂粮粥&#xff0c;搭配水煮蛋和一小碟凉拌黄瓜&#xff0c;营养更全面。…...

某信服EDR3.5.30.ISO安装测试(二)

一、物理机启动EDR 1、修复dracut 使用DiskGenius克隆虚拟磁盘到物理磁盘&#xff0c;将虚拟机移植到物理机&#xff0c;因为磁盘UUID变化等原因&#xff0c;首次默认启动失败&#xff0c;提示&#xff1a; Starting Dracut Emergency Shell... Warning:/dev/centos/root doe…...

Leetcode:回文链表

1、题目描述 给定一个链表的 头节点 head &#xff0c;请判断其是否为回文链表。 如果一个链表是回文&#xff0c;那么链表节点序列从前往后看和从后往前看是相同的。 示例 1&#xff1a; 输入: head [1,2,3,3,2,1] 输出: true 示例 2&#xff1a; 输入: head [1,2] 输出: …...

IL2CPP 技术深度解析

IL2CPP 是 Unity 开发的高性能脚本后端&#xff0c;它将 .NET 的中间语言 (IL) 转换为 C 代码&#xff0c;再编译为原生平台二进制文件。以下是 IL2CPP 的全面技术剖析。 一、架构设计原理 1. 整体编译流程 C# 源代码 → Roslyn 编译器 → IL (.NET DLL)→ IL2CPP 转换器 →…...

AI图片修复工具,一键操作,图片更清晰!

泛黄的老相册里藏着一座记忆博物馆&#xff0c;每张照片都是时光长河中的琥珀。祖父军装照上的折痕里藏着抗美援朝的故事&#xff0c;父母结婚照褪色的红唇映照着八十年代的风尚&#xff0c;童年抓拍照上模糊的身影正重演着我们成长的轨迹。这些承载着集体记忆的影像&#xff0…...

国内短剧 vs. 海外短剧系统:如何选择?2025年深度对比与SEO优化指南

在短剧市场爆发式增长的背景下&#xff0c;国内与海外短剧系统成为创业者与企业的热门选择。本文将从市场潜力、开发成本、内容创作、盈利模式及风险五大维度&#xff0c;结合最新行业数据与案例&#xff0c;深度解析两者的优劣势&#xff0c;助您做出最优决策。 一、市场前…...

linux crash工具详解

crash 是 Linux 系统中用于分析内核转储文件&#xff08;如 vmcore 或 kdump 生成的 dump 文件&#xff09;的核心工具。它结合了调试符号和内核数据结构&#xff0c;能够直观地查看崩溃时的系统状态。以下是其详细使用方法及核心功能解析&#xff1a; 一、安装与准备 1. 安装…...

Scala day6(Class,field,Single Object)

Foreword【こんにちは】 Today, we continue to learn Scala Language, though the Scala isn’t easy for Begainner, Big Data Technology need Scala Language, Spark【Distributed Calculation FrameWork】is based on Scala Language designed. I know the learning road…...

不小心把当前的环境变量路径覆盖掉怎么办

起因 配置环境变量&#xff08;~/.bashrc&#xff09;的时候没加:$PATH&#xff0c;导致 source ~/.bashrc之后只剩下刚刚配置的环境变量了。连vim都打不开 解决 添加临时环境变量export PATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PATH再重新修改…...

unity TMP字体使用出现乱码方框

参考文章&#xff1a; Unity 设置默认字体&#xff08;支持老版及新版TMP&#xff09;_unity tmp字体-CSDN博客 原因是导入的项目package包没有连着tmp一起&#xff0c;这样在新工程中导入的tmp字体默认的是tmp自己的&#xff0c;解决方案就是替换成自己需要的tmp字体就行 替换…...

14.网络钓鱼实战

网络钓鱼实战 第一部分&#xff1a;网络钓鱼攻击详解第二部分&#xff1a;设计与分析钓鱼攻击第三部分&#xff1a;钓鱼攻击防范实践总结 目标: • 深入理解网络钓鱼攻击的实施过程 • 掌握设计和识别钓鱼攻击的技巧 • 通过模拟实践提升防范钓鱼攻击的能力 第一部分&#xf…...

pyqt写一个单片机配置界面

已经实现以下功能 1.可以选择单片机架构 2.选择完单片机架构后第二个框可以选择常见单片机型号 3.选择完常见单片机型号后第三个框可以选择内部资源如adc等&#xff08;可以选择多个内部资源&#xff09;4.选择完内部资源如adc等&#xff08;可以选择多个内部资源&#xff09;后…...

「Mac畅玩AIGC与多模态20」开发篇16 - 使用结构化输出字段控制后续流程示例

一、概述 本篇介绍如何在工作流中使用结构化输出字段作为判断依据&#xff0c;实现前后节点联动控制。通过执行 LLM 节点输出结构化 JSON&#xff0c;并使用其中的字段驱动后续判断节点执行不同路径&#xff0c;开发人员将掌握结构化字段在工作流中的引用方式与分支控制技巧。…...

Unity-Shader详解-其四

今天我们来聊Unity特有的表面着色器以及很少提到的几何着色器。 表面着色器 在前文关于光照的计算中&#xff0c;我们学会了很多&#xff1a;我们学习了一系列光照模型&#xff0c;比如专门针对漫反射的兰伯特模型和改进的半兰伯特模型&#xff0c;又比如由高光、漫反射和环境…...

Lua 元表和元方法

元表(Metatable)和元方法(Metamethod)是Lua中实现面向对象编程、操作符重载和自定义行为的重要机制。 元表 元表是一个普通的Lua表&#xff0c;可以附加到另一个表上&#xff0c;用于定义或修改该表的行为。每个表都可以有自己的元表。 setmetatable(tab,metatab) 将metatab设…...

GESP2024年3月认证C++八级( 第二部分判断题(6-10))

海伦公式参考程序&#xff1a; #include <iostream> #include <cmath> // 引入cmath库以使用sqrt函数using namespace std;double calculateTriangleArea(int a, int b, int c) {// 使用海伦公式double s (a b c) / 2.0; // 半周长return sqrt(s * (s - a) *…...

Nacos源码—3.Nacos集群高可用分析一

大纲 1.Nacos集群的几个问题 2.单节点对服务进行心跳健康检查和同步检查结果 3.集群新增服务实例时如何同步给其他节点 4.集群节点的健康状态变动时的数据同步 5.集群新增节点时如何同步已有服务实例数据 1.Nacos集群的几个问题 问题一&#xff1a;在单机模式下&#xff…...

信息系统项目管理师-软考高级(软考高项)​​​​​​​​​​​2025最新(九)

个人笔记整理---仅供参考 第九章项目范围管理 9.1管理基础 9.2项目范围管理过程 9.3规划范围管理 9.4收集需求 9.5定义范围 9.6创建WBS 9.7确认范围 9.8控制范围...

DeepSeek学术论文写作全流程指令

一、选题与领域界定 研究热点捕捉 指令: “在[研究领域]中,现有文献对[具体问题]的[哪方面]研究不足?基于近5年文献归纳3个待突破方向,需结合高频关键词和交叉学科维度。” 示例: “在深度学习医疗影像分析中,现有文献对小样本训练的泛化性研究不足?基于2019-2023年顶会…...

【ArUco boards】标定板检测

之前定位用的Charuco标定板做的&#xff08;https://blog.csdn.net/qq_45445740/article/details/143897238&#xff09;&#xff0c;因为实际工况中对标定板的尺寸有要求&#xff0c;大概是3cm*2cm这个尺寸&#xff0c;加上选用的是ChAruco标定板&#xff0c;导致每一个aruco码…...

2025 年 408 真题及答案

2025 年 408 真题 历年408真题及答案下载直通车 1、以下 C 代码的时间复杂度是多少&#xff1f;&#xff08;&#xff09; int count 0; for (int i0; i*i<n; i)for (int j0; j<i; j)count;A O(log2n)B O(n)C O(nlogn)D O(n2) 2、对于括号匹配问题&#xff0c;符号栈…...

设计模式每日硬核训练 Day 18:备忘录模式(Memento Pattern)完整讲解与实战应用

&#x1f504; 回顾 Day 17&#xff1a;中介者模式小结 在 Day 17 中&#xff0c;我们学习了中介者模式&#xff08;Mediator Pattern&#xff09;&#xff1a; 用一个中介者集中管理对象之间的通信。降低对象之间的耦合&#xff0c;适用于聊天系统、GUI 控件联动、塔台调度等…...

ByteArrayOutputStream 类详解

ByteArrayOutputStream 类详解 ByteArrayOutputStream 是 Java 中用于在内存中动态写入字节数据的输出流(ByteArrayOutputStream和ByteArrayInputStream是节点流),位于 java.io 包。它不需要关联物理文件或网络连接,所有数据都存储在内存的字节数组中。 1. 核心特性 内存缓冲…...

Linux中web服务器的部署及优化

前言&#xff1a;Nginx 和 Apache HTTP Server 是两款非常流行的 Web 服务器。 Nginx 简介&#xff1a;Nginx 是一款轻量级的高性能 Web 服务器、反向代理服务器以及电子邮件&#xff08;IMAP/POP3&#xff09;代理服务器。由俄罗斯人伊戈尔・赛索耶夫开发&#xff0c;其在处…...

使用Mathematica绘制Sierpinski地毯

在Mathematica中内置的绘制Sierpinski地毯的函数&#xff1a; SierpinskiCurve[n] gives the line segments representing the n-step Sierpiński curve. 注意&#xff0c;直接运行这个函数&#xff0c;返回的是Line对象&#xff0c;例如&#xff1a; 运行如下代码&#xf…...

Qt 信号槽机制底层原理学习

简介 Qt的信号和槽&#xff08;Signals and Slots&#xff09;是Qt开发团队创造的一种特殊回调机制&#xff0c;提供了非常简洁易用的事件触发-函数调用机制。 原理学习 虽然上层使用简单&#xff0c;但底层实现机制却复杂的不得了&#xff0c;这里简单的学习一下大概原理。…...