做者简介 joey 蚂蚁金服·数据体验技术团队html
本文是typescript设计模式系列文章的最后一篇,介绍了最后5个对象行为型的设计模式~java
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,全部依赖于它的对象都获得通知并被自动更新。git
观察者模式包含如下角色:github
目标向观察者发送关于改变的“详细信息”,而无论它们须要与否。由目标维护观察者。算法
// 场景:顾客点菜后,服务员记下顾客的信息,菜作好后广播通知顾客领取
// 观察者基类
class Observer {
take(msg: string): void {}
}
// 目标基类
class Subject {
set: Set<Observer> = new Set();
// 注册回调
add(observer: Observer): void {
this.set.add(observer);
}
// 注销回调
remove(observer: Observer): void {
this.set.delete(observer);
}
// 触发全部已注册的回调
notify(msg: string): void {
this.set.forEach(observer => {
observer.take(msg);
});
}
}
// 具体目标,服务员类
class Waiter extends Subject {
// 菜作完后通知全部注册了的顾客
ready(): void {
this.notify('ready');
}
}
// 具体观察者,顾客类
class Client extends Observer {
name: string;
// 初始化时将自身注册到目标,以便接收通知
constructor(name: string, waiter: Waiter) {
super();
this.name = name;
waiter.add(this);
}
take(msg: string) {
console.log(`顾客 ${this.name} 收到了消息显示状态是<${msg}>, 到吧台领取了菜`);
}
}
function observerPushDemo() {
const waiter = new Waiter();
// 顾客点菜后,等待服务员通知
const bob = new Client('Bob', waiter);
const mick = new Client('Mick', waiter);
// 菜准备好后,服务员广播通知顾客能够到吧台领取了
waiter.ready();
}
复制代码
目标除了“最小通知”外什么也不送出,而在此以后由观察者显式地向目标询问细节。观察者里维护了目标对象。typescript
// 场景:顾客点菜后,收到通知从服务员处询问详细信息
// 观察者基类
class Observer {
take(subject: Subject): void {}
}
// 目标基类
class Subject {
set: Set<Observer> = new Set();
// 注册回调
add(observer: Observer): void {
this.set.add(observer);
}
// 注销回调
remove(observer: Observer): void {
this.set.delete(observer);
}
// 触发全部已注册的回调
notify(): void {
this.set.forEach(observer => {
observer.take(this);
});
}
}
// 具体目标,服务员类
class Waiter extends Subject {
status = 'doing';
// 与推模式的区别是,只发送通知,不发送详细数据
ready(): void {
this.status = 'ready';
this.notify();
}
// 提供访问详细数据接口,让观察者访问详细数据
getStatus(): string {
return this.status;
}
}
// 具体观察者,顾客类
class Client extends Observer {
name: string;
// 初始化时将自身注册到目标,以便接收通知
constructor(name: string, waiter: Waiter) {
super();
this.name = name;
waiter.add(this);
}
// 与推模式的区别是,收到通知后,没有数据传入,须要从目标里读取
take(waiter: Waiter) {
const msg = waiter.getStatus();
console.log(`顾客 ${this.name} 收到通知,询问服务员后发现状态是 <${msg}> 后领取了菜`);
}
}
function observerPushDemo() {
const waiter = new Waiter();
// 顾客点菜
const bob = new Client('Bob', waiter);
const mick = new Client('Mick', waiter);
// 菜准备完后,服务员通知了下全部顾客状态改变了,但没有发送内容出去,须要顾客再询问一下服务员才知道最新状态
waiter.ready();
}
复制代码
容许一个对象在其内部状态改变时改变它的行为。对象看起来彷佛修改了它的类。设计模式
状态模式包含如下角色:bash
// 帐户有几种状态:正常,透支,受限
// 帐户类,表明状态模式中的环境
class Account {
private name: string;
private state: State;
// 余额
private balance = 0;
// 初始时为正常状态
constructor(name: string) {
this.name = name;
this.state = new NormalState(this);
console.log(`用户 ${this.name} 开户,余额为 ${this.balance}`);
console.log('--------');
}
getBalance(): number {
return this.balance;
}
setBalance(balance: number) {
this.balance = balance;
}
setState(state: State) {
this.state = state;
}
// 存款
deposit(amount: number) {
this.state.deposit(amount);
console.log(`存款 ${amount}`);
console.log(`余额为 ${this.balance}`);
console.log(`帐户状态为 ${this.state.getName()}`);
console.log('--------');
}
// 取款
withdraw(amount: number) {
this.state.withdraw(amount);
console.log(`取款 ${amount}`);
console.log(`余额为 ${this.balance}`);
console.log(`帐户状态为 ${this.state.getName()}`);
console.log('--------');
}
// 结算利息
computeInterest() {
this.state.computeInterest();
}
}
// 状态抽象类
abstract class State {
private name: string;
protected acc: Account;
constructor(name: string) {
this.name = name;
}
getName() {
return this.name;
}
abstract deposit(amount: number);
abstract withdraw(amount: number);
abstract computeInterest();
abstract stateCheck();
}
// 正常状态类
class NormalState extends State {
acc: Account;
constructor(acc: Account) {
super('正常');
this.acc = acc;
}
deposit(amount: number) {
this.acc.setBalance(this.acc.getBalance() + amount);
this.stateCheck();
}
withdraw(amount: number) {
this.acc.setBalance(this.acc.getBalance() - amount);
this.stateCheck();
}
computeInterest() {
console.log('正常状态,无须支付利息');
}
// 状态转换
stateCheck() {
if (this.acc.getBalance() > -2000 && this.acc.getBalance() <= 0) {
this.acc.setState(new OverdraftState(this.acc));
} else if (this.acc.getBalance() == -2000) {
this.acc.setState(new RestrictedState(this.acc));
} else if (this.acc.getBalance() < -2000) {
console.log('操做受限');
}
}
}
// 透支状态
class OverdraftState extends State {
acc: Account;
constructor(acc: Account) {
super('透支');
this.acc = acc;
}
deposit(amount: number) {
this.acc.setBalance(this.acc.getBalance() + amount);
this.stateCheck();
}
withdraw(amount: number) {
this.acc.setBalance(this.acc.getBalance() - amount);
this.stateCheck();
}
computeInterest() {
console.log('计算利息');
}
// 状态转换
stateCheck() {
if (this.acc.getBalance() > 0) {
this.acc.setState(new NormalState(this.acc));
} else if (this.acc.getBalance() == -2000) {
this.acc.setState(new RestrictedState(this.acc));
} else if (this.acc.getBalance() < -2000) {
console.log('操做受限');
}
}
}
// 受限状态
class RestrictedState extends State {
acc: Account;
constructor(acc: Account) {
super('受限');
this.acc = acc;
}
deposit(amount: number) {
this.acc.setBalance(this.acc.getBalance() + amount);
this.stateCheck();
}
withdraw(ammount: number) {
console.log('帐号受限,取款失败');
}
computeInterest() {
console.log('计算利息');
}
// 状态转换
stateCheck() {
if (this.acc.getBalance() > 0) {
this.acc.setState(new NormalState(this.acc));
} else if (this.acc.getBalance() > -2000) {
this.acc.setState(new OverdraftState(this.acc));
}
}
}
function stateDemo() {
const acc = new Account('Bob');
acc.deposit(1000);
acc.withdraw(2000);
acc.deposit(3000);
acc.withdraw(4000);
acc.withdraw(1000);
acc.computeInterest();
}
复制代码
定义一系列的算法,把它们一个个封装起来,而且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。数据结构
策略模式包含如下角色:框架
// 火车票类:环境类
class TrainTicket {
private price: number;
private discount: Discount;
constructor(price: number) {
this.price = price;
}
setDiscount(discount: Discount) {
this.discount = discount;
}
getPrice(): number {
return this.discount.calculate(this.price);
}
}
// 折扣接口
interface Discount {
calculate(price: number): number;
}
// 学生票折扣
class StudentDiscount implements Discount {
calculate(price: number): number {
console.log('学生票打7折');
return price * 0.7;
}
}
// 儿童票折扣
class ChildDiscount implements Discount {
calculate(price: number): number {
console.log('儿童票打5折');
return price * 0.5;
}
}
// 军人票折扣
class SoldierDiscount implements Discount {
calculate(price: number): number {
console.log('军人免票');
return 0;
}
}
function strategyDemo() {
const ticket: TrainTicket = new TrainTicket(100);
// 从环境中获取到身份信息,而后根据身份信息获取折扣策略
const discount: Discount = getIdentityDiscount();
// 注入折扣策略对象
ticket.setDiscount(discount);
// 根据策略对象获取票价
console.log(ticket.getPrice());
}
复制代码
定义一个操做中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类能够不改变一个算法的结构便可重定义该算法的某些特定步骤。
模板方法包含如下角色:
模板方法是基于继承的一种模式。
下面是一个组件渲染的例子,模拟React组件渲染流程。
// 组件基类
class Component {
// 模板方法,把组件渲染的流程定义好
setup() {
this.componentWillMount();
this.doRender();
this.componentDidMount();
}
private doRender() {
// 作实际的渲染工做
}
componentWillMount() {}
componentDidMount() {}
}
class ComponentA extends Component {
componentWillMount() {
console.log('A组件即将被渲染');
}
componentDidMount() {
console.log('A组件渲染完成');
}
}
class ComponentB extends Component {
componentWillMount() {
console.log('B组件即将被渲染');
}
componentDidMount() {
console.log('B组件渲染完成');
}
}
// 渲染A和B组件,生命周期的流程都是相同的,已经在模板方法里定义好了的
function templateMethodDemo() {
const compA = new ComponentA();
compA.setup();
const compB = new ComponentB();
compB.setup();
}
复制代码
提供一个做用于某对象结构中的各元素的操做表示,它使咱们能够在不改变各元素的类的前提下定义做用于这些元素的新操做。
访问者模式包含如下角色:
一个公司有两种员工,正式工和临时工,他们有不一样的工时和薪酬结算方法。
// 员工接口
interface Employee {
accept(handler: Department): void;
}
// 全职员工类
class FulltimeEmployee implements Employee {
private name = '';
// 全职员工按周薪计算薪酬
private weeklyWage = 0;
// 工做时长
private workTime = 0;
constructor(name: string, weeklyWage: number, workTime: number) {
this.name = name;
this.weeklyWage = weeklyWage;
this.workTime = workTime;
}
getName(): string {
return this.name;
}
getWeeklyWage(): number {
return this.weeklyWage;
}
getWorkTime(): number {
return this.workTime;
}
// 实现接口,调用访问者处理全职员工的方法
accept(handler: Department) {
handler.visitFulltime(this);
}
}
// 临时员工类
class ParttimeEmployee implements Employee {
private name = '';
// 临时员工按时薪计算薪酬
private hourWage = 0;
// 工做时长
private workTime = 0;
constructor(name: string, hourWage: number, workTime: number) {
this.name = name;
this.hourWage = hourWage;
this.workTime = workTime;
}
getName(): string {
return this.name;
}
getHourWage(): number {
return this.hourWage;
}
getWorkTime(): number {
return this.workTime;
}
// 实现接口,调用访问者处理临时工的方法
accept(handler: Department) {
handler.visitParttime(this);
}
}
// 部门接口
interface Department {
visitFulltime(employee: FulltimeEmployee): void;
visitParttime(employee: ParttimeEmployee): void;
}
// 具体访问者——财务部,结算薪酬实现部门接口
class FADepartment implements Department {
// 全职员工薪酬计算方式
visitFulltime(employee: FulltimeEmployee) {
const name: string = employee.getName();
let workTime: number = employee.getWorkTime();
let weekWage: number = employee.getWeeklyWage();
const WEEK_WORK_TIME = 40;
if (workTime > WEEK_WORK_TIME) {
// 计算加班工资
const OVER_TIME_WAGE = 100;
weekWage = weekWage + (workTime - WEEK_WORK_TIME) * OVER_TIME_WAGE;
} else if (workTime < WEEK_WORK_TIME) {
if (workTime < 0) {
workTime = 0;
}
// 扣款
const CUT_PAYMENT = 80;
weekWage = weekWage - (WEEK_WORK_TIME - workTime) * CUT_PAYMENT;
}
console.log(`正式员工 ${name} 实际工资为:${weekWage}`);
}
// 临时工薪酬计算方式
visitParttime(employee: ParttimeEmployee) {
const name = employee.getName();
const hourWage = employee.getHourWage();
const workTime = employee.getWorkTime();
console.log(`临时工 ${name} 实际工资为:${hourWage * workTime}`);
}
}
// 具体访问者——人力资源部,结算工做时间,实现部门接口
class HRDepartment implements Department {
// 全职员工工做时间报告
visitFulltime(employee: FulltimeEmployee) {
const name: string = employee.getName();
let workTime: number = employee.getWorkTime();
// 实际工做时间报告
let report = `正式员工 ${name} 实际工做时间为 ${workTime} 小时`;
const WEEK_WORK_TIME = 40;
if (workTime > WEEK_WORK_TIME) {
// 加班时间报告
report = `${report},加班 ${WEEK_WORK_TIME - workTime} 小时`;
} else if (workTime < WEEK_WORK_TIME) {
if (workTime < 0) {
workTime = 0;
}
// 请假时间报告
report = `${report},请假 ${WEEK_WORK_TIME - workTime} 小时`;
}
console.log(report);
}
// 临时工工做时间报告
visitParttime(employee: ParttimeEmployee) {
const name: string = employee.getName();
const workTime: number = employee.getWorkTime();
console.log(`临时工 ${name} 实际工做时间为 ${workTime} 小时`);
}
}
// 员工集合类
class EmployeeList {
list: Array<Employee> = [];
add(employee: Employee) {
this.list.push(employee);
}
// 遍历员工集合中的每个对象
accept(handler: Department) {
this.list.forEach((employee: Employee) => {
employee.accept(handler);
});
}
}
function visitorDemo() {
const list: EmployeeList = new EmployeeList();
const full1 = new FulltimeEmployee('Bob', 3000, 45);
const full2 = new FulltimeEmployee('Mikel', 2000, 35);
const full3 = new FulltimeEmployee('Joe', 4000, 40);
const part1 = new ParttimeEmployee('Lili', 80, 20);
const part2 = new ParttimeEmployee('Lucy', 60, 15);
list.add(full1);
list.add(full2);
list.add(full3);
list.add(part1);
list.add(part2);
// 财务部计算薪酬
const faHandler = new FADepartment();
list.accept(faHandler);
// 人力资源部出工做报告
const hrHandler = new HRDepartment();
list.accept(hrHandler);
}
复制代码
本文介绍了最后5种对象行为型模式,多谢你们对于系列文章的支持~对团队感兴趣的同窗能够关注专栏或者发送简历至'tao.qit####alibaba-inc.com'.replace('####', '@'),欢迎有志之士加入~