深度掌握TS继承(中)

TS中的继承
1. 理解子类

(1)什么是子类?

TypeScript 支持继承类,即我们可以在创建类的时候继承一个已存在的类,这个已存在的类称为父类,继承它的类称为子类。

有两个类,比如 A 类和 B 类,如果满足 A 类 is a kind of B类,那么 A 类就是 B 类的子类
比如:A 类是顾客类,B 类是人类,因为顾客类 a kind of 人类成立【顾客类是人类的一种】,所以顾客类是人类的子类。

(2) 子类如何继承父类的属性和方法?

以顾客类为例子:顾客类继承了父类【人类】的非私有的属性和方法,也具备子类独有的属性和方法 。

顾客类继承父类【人类】的全部非私有的属性和方法外,还有哪些独有的属性和方法呢?
顾客类独有属性:顾客等级,顾客编号
顾客类独有方法:购买

(3) 初步理解为什么要用继承?

举例:宠物管理项目中的狗狗类,兔子类,小猫类都是宠物,尽管每个宠物都有独有属性和方法,比如狗狗类的品种,看家方法;兔子类的肤色属性等。但这些类都包含了 name, buymoney【购买价格】,healthstatus【健康状况】,friendshipstar【和主人的友谊星级数】这些属性,如果每一个类都写这些属性,那么就非常臃肿,可以把这些属性提取出来放到一个宠物类中,其他类都继承这个宠物类。当然继承还有更多好处,下面借助汽车租赁功能的实现来更深度的掌握继承。

(4) 示例:汽车租赁管理功能

需求1:汽车租赁功能实现: 有小轿车,大巴,卡车三种类型的车,顾客可以租任意一种或多种不同类型的车,按照租用的天计算租金, 同时为了响应国家对各类车安全的管理, 对在租赁期内有过各种超载,超乘客数,酒后驾车等违规的车需额外支付一定的费用。

需求2:计算退回费用:最终退回顾客的费用为押金扣除使用天数,如押金不足需额外支付不足部分。

小轿车、大巴、卡车共同属性: 品牌 ( brand ) VechileNo ( 车牌号 ),days ( 租赁天数 ), total ( 支付的租赁总费用 ) deposit ( 押金 )。

小轿车、大巴、卡车共同方法: 计算租赁车的价格 ( calculateRent) ,支付押金的方法( payDesposit),安全规则方法(safeShow)

父类:Vechile 交通工具。

子类: 小轿车型号type属性。

代码实现:

// 父类:Vechile   交通工具。
class Vechile {
  static count: number = 3;
  public brand: string;// 品牌
  public vechileNo: string;// 车牌号
  public days: number;// 租赁天数
  public total: number = 0;// 支付的租赁总费用
  public deposit: number;// 押金
  constructor(brand_: string, vechileNo_: string, days_: number, deposit_: number) {
    this.brand = brand_;
    this.vechileNo = vechileNo_;
    this.days = days_;
    this.deposit = deposit_;
    console.log("constructor Vechile=>this.brand:", this.brand)
  }
  // 计算租赁车的价格 ( calculateRent)
  public calculateRent() {
    console.log("calculateRent来自Vechile=>this.brand:", this.brand)

    console.log(this.brand + " 车牌号为:" + this.vechileNo + "开始被租");
    return 0;
  }
  //支付押金的方法( payDesposit)
  payDesposit() {
    console.log(this.brand + " 车牌号为:" + this.vechileNo + " 支付了:" + this.deposit);
  }

  //  安全检测方法(safeShow)
  public safeShow() {
    console.log("车规则....");
    console.log(this.brand + " 车牌号为:" + this.vechileNo + " 违规了:");
  }
}

// 子类Car类 独有属性为type_ 
class Car extends Vechile {
  // public brand: string = "nobrand"
  public type: string;//车的型号
  constructor(brand_: string, vechileNo_: string, days_: number,
    deposit_: number, type_: string) {
    //  Vechile.call(this,brand_, vechileNo_, days_, total_, deposit_)
    super(brand_, vechileNo_, days_, deposit_);
    this.type = type_;
  }
  // 根据车的型号来获取租用一天该型号车的租金
  public getPriceByType() {
    let rentMoneyByDay: number = 0;//每天的租金
    if (this.type === "普拉多巡洋舰") {
      rentMoneyByDay = 800;
    } else if (this.type === "凯美瑞旗舰版") {
      rentMoneyByDay = 400;
    } else if (this.type === "威驰智行版") {
      rentMoneyByDay = 200;
    }
    return rentMoneyByDay;
  }
  // private 是私有的访问修饰符 只允许在本类中方法
  // protected 是受保护的访问修饰符【修饰符是用来控制方法或属性访问的范围】,可以被本类和子类中使用,不能在类的外部使用
  // public // 可以被本类和子类中使用,也可以在类的外部使用 默认是public
  public calculateRent() {//方法重写 [override]
    // this.safeShow();// 寄生组合继承模式 middle()
    super.calculateRent();  //=Vechile.prototype.calculateRent.call(this)
    console.log("Car:", Car.count)
    console.log("this.brand:", this.brand)
    return this.days * this.getPriceByType();
  }
  checkIsWeigui(isOverWeight: boolean) {
    if (isOverWeight) {
      this.total = this.total + 500;
    }
  }
}
let car = new Car("普拉多", "京3A556", 3, 100000, "凯美瑞旗舰版");
console.log(car.calculateRent());

class Bus extends Vechile {
  public seatNum: number // 座位数
  constructor(brand_: string, vechileNo_: string, days_: number,
    deposit_: number, seatNum_: number) {
    //  Vechile.call(this,brand_, vechileNo_, days_, total_, deposit_)
    super(brand_, vechileNo_, days_, deposit_);//使用父类的构造函数的好处
    this.seatNum = seatNum_;
    if (this.seatNum > 200) {
      throw new Error("座位数不能超过200");
    }
  }
  public getPriceBySeatNum() { //计算租赁价格
    let rentMoneyByDay: number = 0;//每天的租金
    if (this.seatNum <= 16) {
      rentMoneyByDay = 800;
    } else if (this.seatNum > 16) {
      rentMoneyByDay = 1600;
    }
    return rentMoneyByDay;
  }
  public calculateRent() {
    super.calculateRent();
    return this.days * this.getPriceBySeatNum();
  }
  checkIsOverNum(isOverWeight: boolean) {
    if (isOverWeight) {
      this.total = this.total + 2000;
    }
  }
}

class Truck extends Vechile {
  ton!: number // 座位数
  constructor(brand_: string, type_: string,
    days_: number, deposit_: number, ton_: number) {
    super(brand_, type_, days_, deposit_);
    this.ton = ton_;
    if (this.ton < 300 || this.ton > 2000) {
      throw new Error("吨数在300-2000吨之间");
    }
  }

  checkIsOverWeight(isOverWeight: boolean) {
    if (isOverWeight) {
      console.log("超载了");
      this.total = this.total + 2000;
    }
  }

  CalRentPrice() {//计算租赁价格
    let rentMoneyByDay: number = 0;//每天的租金
    if (this.ton <= 500) {//500吨
      rentMoneyByDay = 750;
    } else if (this.ton > 500) {
      rentMoneyByDay = 1350;
    }
    return rentMoneyByDay;
  }
  public calRent() {
    return this.CalRentPrice() * this.days;
  }
  public calDesposit() {
    return this.deposit;
  }
}

class Customer {
  public rentVechile() {

  }
}

/**
输出:
constructor Vechile=>this.brand: 普拉多
calculateRent来自Vechile=>this.brand: 普拉多
普拉多 车牌号为:京3A556开始被租
Car: 3
this.brand: 普拉多
1200
*/
2.TS继承中的super和方法重写【override】

(1)super: super 关键字是对父类的直接引用,该关键字可以引用父类的属性和方法。

super只能出现在子类【派生类】中,使用场景:

1.在子类的构造函数中使用 super (子类传递给父类构造函数的参数) 就表示用来调用父类构造函数 (传递给父类构造函数的参数)

2.在子类重写的方法中调用父类同名方法,super.重写的方法

注意:当子类和父类拥有同名属性时,super获取不到父类中的同名属性,一般要避免在子类中定义与父类的同名方法。

(2)方法重写(override):

条件:一定发生在继承的子类中

位置: 子类中重写父类的方法

应用场景:当父类中方法的实现不能满足子类功能需要或不能完全满足子类功能需要时,就需要在子类中进行重写

方法重写给继承带来的好处: 让所有的子类共用父类中方法已经实现了一部分功能的代码【父类方法代码在各个子类中得到了复用】

定义规则

  1. 和父类方法同名

  2. 参数和父类相同,如果是引用类型的参数,需要依据具体类型来定义。

  3. 父类方法的访问范围【访问修饰符】必须小于子类中方法重写的访问范围【访问修饰符】,同时父类方法不能是private。

脚踏实地行,海阔天空飞

你可能感兴趣的:(typescript,javascript)