面试题答案
一键面试设计具有良好封装性和数据隐藏的TypeScript类
- 使用private和protected修饰符
- private修饰符:将类的属性和方法标记为
private
,表示这些成员只能在类内部访问。例如:
- private修饰符:将类的属性和方法标记为
class User {
private username: string;
private password: string;
constructor(username: string, password: string) {
this.username = username;
this.password = password;
}
private validatePassword(inputPassword: string): boolean {
return this.password === inputPassword;
}
}
- protected修饰符:
protected
成员可以在类内部以及子类中访问。适用于希望在继承体系中共享但不希望外部直接访问的成员。例如:
class Animal {
protected name: string;
constructor(name: string) {
this.name = name;
}
protected makeSound(): void {
console.log('Some sound');
}
}
class Dog extends Animal {
bark(): void {
this.makeSound();
console.log(`${this.name} is barking`);
}
}
- 提供访问器(getter和setter)
通过
getter
和setter
方法,可以在控制对内部数据访问的同时,提供一种安全的方式让外部代码获取和修改数据。例如:
class Temperature {
private _value: number;
constructor(value: number) {
this._value = value;
}
get value(): number {
return this._value;
}
set value(newValue: number) {
if (newValue >= -273.15) {
this._value = newValue;
} else {
throw new Error('Temperature cannot be below absolute zero');
}
}
}
- 模块封装
将相关的类封装在模块中,通过
export
关键字控制哪些类、属性和方法可以被外部模块访问。例如:
// user.ts
class User {
// 类的实现,属性和方法可能是private的
private username: string;
constructor(username: string) {
this.username = username;
}
}
export class UserService {
private users: User[] = [];
addUser(user: User): void {
this.users.push(user);
}
getUsers(): User[] {
return this.users;
}
}
在其他模块中,只能访问UserService
,而无法直接访问User
类的私有成员。
实际项目中可能遇到的挑战及解决方法
- 子类访问父类私有成员的需求
- 挑战:有时在继承体系中,子类可能需要访问父类的私有成员,而
private
修饰符会阻止这种访问。 - 解决方法:将需要在子类中访问的成员标记为
protected
。如果确实不希望子类修改某些值,可以只提供getter
方法而不提供setter
方法。
- 挑战:有时在继承体系中,子类可能需要访问父类的私有成员,而
- 第三方库与封装性的冲突
- 挑战:一些第三方库可能期望直接访问对象的属性,这与类的封装原则相冲突。
- 解决方法:可以创建适配器类,在适配器类中调用第三方库,并通过适配器类的接口来保持封装性。或者,在使用第三方库时,在内部类中封装对第三方库的调用,对外提供符合封装原则的接口。
- 调试和日志记录
- 挑战:由于数据隐藏,在调试时获取内部状态可能变得困难,并且在日志记录中也难以记录完整信息。
- 解决方法:可以添加专门的调试方法,这些方法在开发环境中返回内部数据,但在生产环境中可以被移除或限制访问。对于日志记录,可以提供一些方法来获取格式化后的相关信息用于记录日志,而不是直接暴露内部数据。