大白话TypeScript第二章面向对象编程

发布于:2025-02-25 ⋅ 阅读:(13) ⋅ 点赞:(0)

大白话TypeScript第二章面向对象编程

第二阶段主要是学习面向对象编程相关的 TypeScript 知识,面向对象编程就像是把代码按照不同的“角色”或者“事物”来组织,每个“角色”有自己的特点和行为,这样能让代码更有条理,也更容易维护和扩展。下面详细介绍类和继承、接口这两部分内容。

1. 类和继承

类就像是一个模板,你可以用这个模板来创建很多个类似的对象。比如说,我们要描述“人”这个概念,人有名字、年龄,还会说话,那我们就可以创建一个“人”的类。

// 定义一个 Person 类
class Person {
    // 类的属性,这里定义了 name 和 age 属性
    name: string;
    age: number;

    // 构造函数,当你创建这个类的对象时会自动调用,用来初始化对象的属性
    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }

    // 类的方法,这里定义了一个 sayHello 方法,用来让这个人打招呼
    sayHello() {
        console.log(`大家好,我叫 ${this.name},我今年 ${this.age} 岁了。`);
    }
}

// 使用 Person 类创建一个对象,就像用模板做出一个具体的东西
let person1 = new Person('张三', 20);
// 调用对象的方法
person1.sayHello();

在上面的代码里,Person 就是一个类,它有 nameage 两个属性,还有一个 sayHello 方法。我们通过 new Person('张三', 20) 创建了一个 person1 对象,然后调用 sayHello 方法让这个人打了招呼。

继承

继承就像是儿子可以继承爸爸的一些特点和能力。在编程里,一个类可以继承另一个类的属性和方法,这样可以避免重复编写代码。

// 定义一个 Student 类,它继承自 Person 类
class Student extends Person {
    // 新增一个属性,学生还有班级信息
    classInfo: string;

    // 构造函数,这里除了接收 name 和 age,还接收 classInfo
    constructor(name: string, age: number, classInfo: string) {
        // 使用 super 调用父类的构造函数,初始化父类的属性
        super(name, age);
        this.classInfo = classInfo;
    }

    // 重写父类的 sayHello 方法,让学生打招呼时多说一些信息
    sayHello() {
        console.log(`大家好,我叫 ${this.name},我今年 ${this.age} 岁了,我在 ${this.classInfo} 班级。`);
    }
}

// 创建一个 Student 对象
let student1 = new Student('李四', 18, '高三一班');
// 调用重写后的 sayHello 方法
student1.sayHello();

在这个例子中,Student 类继承了 Person 类,所以 Student 对象也有 nameage 属性,还能调用 sayHello 方法。同时,Student 类又新增了 classInfo 属性,并且重写了 sayHello 方法,让打招呼的内容更丰富。

访问修饰符

访问修饰符就像是给类的属性和方法加上了不同的“锁”,控制它们在不同地方的访问权限。主要有三种:public(公共的,谁都能访问)、private(私有的,只有在类的内部能访问)、protected(受保护的,在类的内部和子类中能访问)。

class Animal {
    // public 属性,谁都能访问
    public name: string;
    // private 属性,只有在 Animal 类内部能访问
    private age: number;

    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }

    // 可以在类内部访问 private 属性
    getAge() {
        return this.age;
    }
}

let animal1 = new Animal('小狗', 3);
console.log(animal1.name); // 可以访问 public 属性
// console.log(animal1.age); // 这里会报错,因为 age 是 private 属性
console.log(animal1.getAge()); // 通过类的方法访问 private 属性
2. 接口

接口就像是一份合同,规定了某个对象必须有哪些属性和方法。当一个对象要遵守这个“合同”时,就必须实现接口里规定的内容。

// 定义一个 PersonInterface 接口
interface PersonInterface {
    // 规定必须有 name 属性,类型是 string
    name: string;
    // 规定必须有 age 属性,类型是 number
    age: number;
    // 规定必须有 sayHello 方法,返回值类型是 void(也就是没有返回值)
    sayHello(): void;
}

// 定义一个类 Person2,实现 PersonInterface 接口
class Person2 implements PersonInterface {
    name: string;
    age: number;

    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }

    sayHello() {
        console.log(`你好,我叫 ${this.name},我 ${this.age} 岁了。`);
    }
}

// 创建一个 Person2 对象
let person2 = new Person2('王五', 22);
person2.sayHello();

在这个例子中,PersonInterface 接口规定了一个对象必须有 nameage 属性和 sayHello 方法。Person2 类实现了这个接口,所以它必须按照接口的要求来定义这些属性和方法。这样做的好处是,代码的结构更清晰,也更容易进行团队协作和代码维护。

面向对象编程和面向过程编程有什么区别?

面向过程编程

面向过程编程就像是按照一份详细的菜谱做菜。你把做菜的过程拆分成一个一个步骤,然后按照顺序依次执行这些步骤,最终做出一道菜。每一个步骤都很明确,做完一个步骤再做下一个步骤,整个过程是线性的。

代码示例:计算两个数的和,然后将结果乘以 3
# 第一步:定义一个函数来计算两个数的和
def add_numbers(a, b):
    return a + b

# 第二步:定义一个函数来将一个数乘以 3
def multiply_by_three(num):
    return num * 3

# 第三步:按照顺序调用这些函数
num1 = 5
num2 = 3
sum_result = add_numbers(num1, num2)
final_result = multiply_by_three(sum_result)

print(final_result)

在这个例子中,我们把计算过程拆分成了几个独立的函数,每个函数完成一个特定的任务。然后按照顺序依次调用这些函数,最终得到我们想要的结果。整个程序的执行过程就像是沿着一条直线,一步一步地走下去。

面向对象编程

面向对象编程就像是组织一场演出。你有不同的角色,每个角色都有自己的特点和能力。你不用去关心每个角色具体是怎么完成任务的,只需要告诉他们要做什么。这些角色就像是一个个对象,他们封装了自己的数据和行为。

代码示例:同样是计算两个数的和,然后将结果乘以 3
class Calculator:
    def __init__(self):
        # 初始化一个属性来存储结果
        self.result = 0

    def add(self, a, b):
        # 计算两个数的和,并将结果存储在 self.result 中
        self.result = a + b
        return self.result

    def multiply_by_three(self):
        # 将 self.result 乘以 3
        self.result = self.result * 3
        return self.result

# 创建一个 Calculator 对象
calc = Calculator()
# 调用 add 方法计算两个数的和
sum_result = calc.add(5, 3)
# 调用 multiply_by_three 方法将和乘以 3
final_result = calc.multiply_by_three()

print(final_result)

在这个例子中,我们创建了一个 Calculator 类,它就像是一个角色。这个角色有自己的属性 result 来存储计算结果,还有两个方法 addmultiply_by_three 来完成具体的计算任务。我们通过创建这个类的对象 calc,然后调用对象的方法来完成计算。我们不用去关心这些方法内部是怎么实现的,只需要知道调用这些方法能得到我们想要的结果就行。

两者的区别总结

  • 组织方式
    • 面向过程编程是按照步骤来组织代码的,就像做菜的步骤一样,一个接着一个。
    • 面向对象编程是按照对象来组织代码的,每个对象有自己的属性和方法,就像演出中的每个角色有自己的特点和能力。
  • 可维护性
    • 面向过程编程如果程序变得复杂,步骤会越来越多,代码会变得难以维护和扩展。就像菜谱越来越长,很难修改和添加新的步骤。
    • 面向对象编程通过将数据和行为封装在对象中,使得代码的结构更清晰,更容易维护和扩展。就像演出中的角色可以独立更换和调整,不影响整个演出的进行。
  • 复用性
    • 面向过程编程中的函数复用性相对较低,因为函数通常是针对特定的任务编写的。
    • 面向对象编程中的对象和类可以很方便地复用,你可以创建多个相同类的对象,也可以继承一个类来创建新的类。就像演出中的角色可以在不同的演出中重复使用,或者基于一个角色创建新的角色。
    • 加粗样式

有哪些常见的设计模式可以应用于面向对象编程?
以下为你介绍几种在 TypeScript 面向对象编程中常见的设计模式,同时结合大白话解释和代码示例。

1. 单例模式

大白话解释

单例模式就好比世界上独一无二的宝藏,在整个程序运行期间,这个“宝藏”只有一份。不管是谁,在什么时候去获取它,拿到的都是同一个东西。就像一个国家的总统,无论什么时候提到总统,指的都是同一个人。

代码示例
class President {
    // 静态私有属性,用于存储单例实例
    private static instance: President;

    // 私有构造函数,防止外部直接实例化
    private constructor() {}

    // 静态方法,用于获取单例实例
    public static getInstance(): President {
        if (!this.instance) {
            this.instance = new President();
        }
        return this.instance;
    }

    // 示例方法
    public introduce(): void {
        console.log("我是这个国家的总统。");
    }
}

// 获取总统实例
const president1 = President.getInstance();
const president2 = President.getInstance();

// 检查两个实例是否相同
console.log(president1 === president2); // 输出: true
president1.introduce();

2. 工厂模式

大白话解释

工厂模式就像是一个神奇的工厂,你只要告诉工厂你想要什么产品,工厂就会按照要求生产出来给你,你不用关心产品具体是怎么制造的。就像你去蛋糕店,告诉店员你要一个巧克力蛋糕,店员就会从厨房拿出一个巧克力蛋糕给你,你不用知道蛋糕是怎么做出来的。

代码示例
// 定义蛋糕基类
abstract class Cake {
    abstract taste(): void;
}

// 定义巧克力蛋糕类
class ChocolateCake extends Cake {
    taste(): void {
        console.log("这是巧克力蛋糕,味道浓郁。");
    }
}

// 定义草莓蛋糕类
class StrawberryCake extends Cake {
    taste(): void {
        console.log("这是草莓蛋糕,味道清甜。");
    }
}

// 定义蛋糕工厂类
class CakeFactory {
    createCake(cakeType: string): Cake {
        switch (cakeType) {
            case "chocolate":
                return new ChocolateCake();
            case "strawberry":
                return new StrawberryCake();
            default:
                throw new Error("不支持的蛋糕类型");
        }
    }
}

// 使用蛋糕工厂
const factory = new CakeFactory();
const chocolateCake = factory.createCake("chocolate");
const strawberryCake = factory.createCake("strawberry");

chocolateCake.taste();
strawberryCake.taste();

3. 观察者模式

大白话解释

观察者模式就像是一群粉丝在关注明星。明星的一举一动(状态变化)都会通知给粉丝,粉丝们根据明星的动态做出相应的反应。明星就是被观察的对象,粉丝就是观察者。

代码示例
// 定义观察者接口
interface Observer {
    update(message: string): void;
}

// 定义具体观察者类(粉丝类)
class Fan implements Observer {
    constructor(private name: string) {}

    update(message: string): void {
        console.log(`${this.name} 收到消息: ${message}`);
    }
}

// 定义被观察对象类(明星类)
class Celebrity {
    private observers: Observer[] = [];

    // 添加观察者
    addObserver(observer: Observer): void {
        this.observers.push(observer);
    }

    // 移除观察者
    removeObserver(observer: Observer): void {
        const index = this.observers.indexOf(observer);
        if (index !== -1) {
            this.observers.splice(index, 1);
        }
    }

    // 通知所有观察者
    notifyObservers(message: string): void {
        this.observers.forEach(observer => observer.update(message));
    }

    // 明星发布新状态
    postStatus(status: string): void {
        const message = `明星发布了新状态: ${status}`;
        this.notifyObservers(message);
    }
}

// 创建明星和粉丝对象
const celebrity = new Celebrity();
const fan1 = new Fan("张三");
const fan2 = new Fan("李四");

// 粉丝关注明星
celebrity.addObserver(fan1);
celebrity.addObserver(fan2);

// 明星发布新状态
celebrity.postStatus("今天很开心!");

4. 装饰器模式

大白话解释

装饰器模式就像是给人穿衣服,人本身有自己的基本属性和行为,但是可以通过穿上不同的衣服(装饰器)来增加额外的功能。比如一个人穿上雨衣就可以防雨,穿上防晒衣就可以防晒。

代码示例
// 定义人基类
class Person {
    show(): void {
        console.log("这是一个普通人。");
    }
}

// 定义装饰器基类
abstract class Decorator {
    constructor(protected person: Person) {}
    abstract show(): void;
}

// 定义具体装饰器类(雨衣装饰器)
class RaincoatDecorator extends Decorator {
    show(): void {
        this.person.show();
        console.log("穿上了雨衣,可以防雨。");
    }
}

// 定义具体装饰器类(防晒衣装饰器)
class SunscreenDecorator extends Decorator {
    show(): void {
        this.person.show();
        console.log("穿上了防晒衣,可以防晒。");
    }
}

// 创建一个人对象
const person = new Person();
// 给人穿上雨衣
const raincoatPerson = new RaincoatDecorator(person);
// 再给穿上雨衣的人穿上防晒衣
const raincoatSunscreenPerson = new SunscreenDecorator(raincoatPerson);

raincoatSunscreenPerson.show();

这些设计模式在 TypeScript 面向对象编程中能帮助你让代码更灵活、可维护和可扩展。你可以根据具体的业务场景选择合适的设计模式。

如何优化面向对象程序的性能?
在 TypeScript 里优化面向对象程序的性能,就像是给一辆车做保养和升级,让它跑得更快、更稳、更省燃料。下面从几个常见的方面来详细说说怎么优化,还会配上代码示例。

1. 减少对象的创建

大白话解释

创建对象就像是造房子,每次造房子都得花不少时间和材料。要是频繁地创建对象,程序就会变慢。所以能不创建新对象就不创建,尽量复用已有的对象。

代码示例

没优化前:

class Point {
    constructor(public x: number, public y: number) {}
}

function calculateDistance() {
    // 每次调用函数都创建新的 Point 对象
    const point1 = new Point(1, 2);
    const point2 = new Point(3, 4);
    const dx = point2.x - point1.x;
    const dy = point2.y - point1.y;
    return Math.sqrt(dx * dx + dy * dy);
}

// 多次调用函数,频繁创建对象
for (let i = 0; i < 1000; i++) {
    calculateDistance();
}

优化后:

class Point {
    constructor(public x: number, public y: number) {}
}

// 提前创建好对象,复用它们
const point1 = new Point(1, 2);
const point2 = new Point(3, 4);

function calculateDistance() {
    const dx = point2.x - point1.x;
    const dy = point2.y - point1.y;
    return Math.sqrt(dx * dx + dy * dy);
}

// 多次调用函数,不再频繁创建对象
for (let i = 0; i < 1000; i++) {
    calculateDistance();
}

2. 使用静态方法和属性

大白话解释

静态方法和属性就像是公共的工具和资源,不用创建对象就能用。如果一个方法或者属性和具体的对象没关系,就可以把它设成静态的,这样能避免每次创建对象都带上这些东西,节省内存。

代码示例

没优化前:

class MathUtils {
    constructor() {}

    // 非静态方法
    add(a: number, b: number): number {
        return a + b;
    }
}

// 创建对象来调用方法
const mathUtils = new MathUtils();
const result = mathUtils.add(1, 2);

优化后:

class MathUtils {
    // 静态方法
    static add(a: number, b: number): number {
        return a + b;
    }
}

// 直接通过类名调用静态方法,不用创建对象
const result = MathUtils.add(1, 2);

3. 避免不必要的继承和多态开销

大白话解释

继承和多态就像是家族传承和分工,虽然很有用,但有时候用得太复杂会增加程序的负担。如果一个类不需要继承其他类或者使用多态,就别用,这样能让程序更简单、更快。

代码示例

没优化前:

// 基类
class Animal {
    constructor(public name: string) {}
    makeSound(): void {}
}

// 子类
class Dog extends Animal {
    makeSound(): void {
        console.log('汪汪汪');
    }
}

// 如果只是想打印狗叫,没必要这么复杂的继承
const dog = new Dog('旺财');
dog.makeSound();

优化后:

// 直接定义一个简单的函数
function dogBark() {
    console.log('汪汪汪');
}

// 直接调用函数
dogBark();

4. 合理使用缓存

大白话解释

缓存就像是一个小仓库,把经常要用的东西存起来,下次要用的时候直接从仓库里拿,不用再重新计算或者查找,这样能节省时间。

代码示例
class Fibonacci {
    private cache: { [key: number]: number } = {};

    // 计算斐波那契数列
    calculate(n: number): number {
        if (n in this.cache) {
            return this.cache[n];
        }
        if (n <= 1) {
            return n;
        }
        const result = this.calculate(n - 1) + this.calculate(n - 2);
        this.cache[n] = result;
        return result;
    }
}

const fib = new Fibonacci();
// 第一次计算,会把结果存到缓存里
console.log(fib.calculate(10)); 
// 第二次计算,直接从缓存里拿结果,速度更快
console.log(fib.calculate(10)); 

通过这些方法,可以让你的 TypeScript 面向对象程序跑得更快、性能更好。


网站公告

今日签到

点亮在社区的每一天
去签到