TypeScript中的面向对象编程

3,648 阅读4分钟

面向对象也即是OOP(Object Oriented Programming),是计算机的一种编程架构,OOP的基本原则是计算机是由子程序作用的单个或者多个对象组合而成,包含属性和方法的对象是类的实例,面向对象的基本特性是:封装、继承、多态、抽象。

一直到今天对于JavaScript是否是面向对象的语言定论仍然没有一致的结果,因为JavaScript(ES5)中没有类的概念,而是直接使用对象来实现编程,使用原型来实现继承,一种基于对象和事件驱动的弱类型动态语言。由于这些不一而足的缺点,微软推出了JavaScript的超集语言--TypeScriptTypeScript就是一个基于类的面向对象编程语言

1. SOLID原则

在早期的软件开发中,开发者通常使用过程式的程序语言编写代码。在过程式的语言中,程序遵循自顶向下的原则开发,并且逻辑都包裹在函数中。当程序员意识到过程式的语言无法提供他们需要的抽象层次、可维护性和复用性时,出现了OOP,并总结了5个OOP开发者需要遵守的原则,以便更容易地创建出可维护和可拓展的系统。

  • 单一职责原则(SRP):表明软件组件(函数、类、模块)必须专注与单一的任务(只有单一的职责);
  • 开/闭原则(OCP):表明软件设计时必须考虑到代码的扩展性,程序的发展必须最少地修改已有的代码(对已有的修改封闭);
  • 里氏替换原则(LSP):表明只要继承的是同一个接口,程序里任意一个类都可以被其他对象(应该可以是在不改变程序正确性的前提下被它的子类)所替换;
  • 接口隔离原则(ISP):表明应该将非常大的接口拆分成一些小的更具体的接口,多个特定客户端接口要好于一个宽泛用途的接口;
  • 依赖反转原则(DIP):表明一个方法应该遵从依赖于抽象借口而不是一个实例(类的概念);

2. 类

我们用类去描述一个对象或者实例,一个类由名字、属性、方法组成。

class Person {
  public name: string;
  public surname: string;
  public email: Email;
  constructor(name: string, surname: string, email: Email) {
    this.name = name;
    this.surname = surname;
    this.email = email;
  }
  greet() {
    console.log(`Hi, ${this.name}`);
  }
}

var me : Person = new Person('lewis', 'lewislv', 'lewis@163.com');

一个类要遵循单一职责原则(SRP),可以让我们更容易的看出它的作用,进而去扩展它,这样就用到类的封装和抽象的概念;

3. 接口

在传统的面向对象的编程中,一个类可以扩展另一个类,也可以实现一个或多个接口;实现一个接口可以被看作是签署了一份协议,必须遵守它的规则。接口的规则是属性和方法的签名,我们必须实现它们;

在TypeScript中接口有两点不同:

  • 接口可以扩展其他接口或者类;
  • 接口可以定义数据和行为而不只是行为;
interface UserInterface {
    name: string;
    password: string;
}

var user: UserInterface = {
    name: '',
    password: ''  // 容易遗漏错误性
}

4. 继承

面向对象编程最重要的特性之一就是可以扩展已有的类,它允许我们创建一个类(子类),从已有的类(父类)上继承所有的属性和方法,子类可以包含父类中没有的属性和方法;

class Teacher extends Person {
    teach() {
        console.log('Welcome to class!');
    }
}

var teacher = new Teacher('lewis', 'lewislv', 'lewis@163.com');
teacher.teach();
teacher.greet();

有时我们希望子类能提供父类中同名方法的特殊实现,可以用保留字super达到这个目的。

class Teacher extends Person {
    public subjects: string [];
    constructor(name: string, surname: string, email: Email) {
        super(name, surname, email);
        this.subjects = subjects;
    }
    greet() {
        super.greet();
        console.log(`I teach ${this.subjects!}`);
    }
}

var teacher = new Teacher('lewis', 'lewislv', 'lewis@163.com');
teacher.teach();
teacher.greet();

5. 范型类和范型约束

如同范型函数一样,范型类能够帮助我们避免重复代码,有时候我们可能会需要约束范型类,一个可行的解决方案是在范型类或函数内使用typeof操作符来验证参数范型的类型;

interface MyInterface {
    doSomething();
}
interface MySecondInterface {
    doSomethingElse();
}

class Example<T extends MyInterface, MySecondInterface> {
    private genericProperty: T;
    useT() {
        this.genericProperty.doSomething();
        this.genericProperty.doSomethingElse(); // 编译错误
    }
}

6. 命名空间

TypeScript提供了命名空间特性,主要用于组织代码,如果在写一个大型应用,这时可以用命名空间包裹那些有联系的接口、类和对象,并使代码更加容易跟踪和理解。

namespace app {
    export class UserModel {
        // ...
    }
}

当声明一个命名空间时,所有实体部分默认都是私有的,可以使用export关键字导出公共部分,也可以在命名空间内声明另一个命名空间;

namespace app {
    export namescpace models {
        export class UserModel {
            // ...
        }
    }
}

总结

以上就是TypeScript的一些面向对象编程的概念,TypeScript的好处很明显,在编译时就能检查出很多语法问题而不是在运行时。目前国内使用Typescript的团队并不多,我觉得写出的代码是否易于维护、优雅,不在于用了什么框架、语言,而在于开发者本身,但是不能保证每个人都不犯错,诚然好的框架和语言能间接帮助开发者写出规范的代码,所以如果有一定技术沉淀的团队还是建议早点使用TypeScript

更多精彩内容欢迎关注我的个人公众号【天道酬勤Lewis】