做者简介 joey 蚂蚁金服·数据体验技术团队html
继前文Typescript玩转设计模式 之 结构型模式(上)以后,本周带来的是系列文章之三,讲解的是3种结构性模式:java
为子系统中的一组接口提供一个一致的界面,Facade模式定义一个高层接口,这个接口使得这个子系统更加容易使用。git
外观模式包含如下角色:程序员
案例:领导提出要实现一个产品功能,但又不想了解其中的细节。github
// 主意
class Idea {};
// 需求
class Requirement {};
// 开发包
class Development {};
// 发布包
class Release {};
// 产品经理
class PD {
analyze(idea: Idea) {
console.log('PD 开始需求');
return new Requirement();
}
}
// 开发者
class Developer {
develop(requirement: Requirement) {
console.log('程序员开始开发');
return new Development();
}
}
// 测试者
class Tester {
test(develop: Development) {
return new Release();
}
}
// 外观方法,领导不须要关注具体的开发流程,只要说出本身的想法便可
// 而不用外观方法的话,也能够访问到子系统,只是须要了解其中的细节
function addNewFunction(idea: Idea) {
const pd = new PD();
const developer = new Developer();
const tester = new Tester();
const requirement = pd.analyze(idea);
const development = developer.develop(requirement);
const release = tester.test(development);
console.log('发布');
}
// 领导
class Leader {
haveAGoodIdea() {
const idea = new Idea();
addNewFunction(idea);
}
}
function facadeDemo() {
const leader = new Leader();
leader.haveAGoodIdea();
}
facadeDemo();
复制代码
运用共享技术有效地支持大量细粒度的对象。编程
享元模式包含如下角色:设计模式
// 书籍类,书的基本信息和借阅信息都是属性
// 但同一本书能够被屡次借出,对借阅记录来讲,同一本书的屡次借阅记录里存储的书的信息是冗余的
class OriginBookRecord {
// 书的基本信息
ISBN: string;
title: string;
// 借阅信息
id: string;
time: string;
constructor(ISBN: string, title: string, id: string, time: string) {
this.ISBN = ISBN;
this.title = title;
this.id = id;
this.time = time;
}
checkout(time: string) {
this.time = time;
}
}
// 书籍管理者
class OriginBookRecordManager {
books: Map<string, OriginBookRecord>;
add(ISBN: string, id: string, title: string, time: string) {
const book = new OriginBookRecord(ISBN, title, id, time);
this.books.set(id, book);
}
checkout(id: string, time: string): void {
const book = this.books.get(id);
if (book) {
book.checkout(time);
}
}
}
// 享元模式,分离内部状态和外部状态,将能共享的部分分离出来
// 本案例中,书的基本信息和借阅信息分离开来,同一本书能够有多条借阅记录
class LibraryBook {
ISBN: string;
title: string;
constructor(ISBN: string, title: string) {
this.ISBN = ISBN;
this.title = title;
}
}
// 享元工厂
class LibraryBookFactory {
books: Map<string, LibraryBook>;
createBook(ISBN: string, title: string): LibraryBook {
let book = this.books.get(ISBN);
if (!book) {
book = new LibraryBook(ISBN, title);
this.books.set(ISBN, book);
}
return book;
}
}
// 将享元工厂实现为单例
const libraryBookFactory = new LibraryBookFactory();
// 借阅记录,此时记录对象不须要保存书的属性,只须要保存一个书的引用,减小了存储空间
class BookRecord {
book: LibraryBook;
id: string;
time: string;
constructor(id: string, book: LibraryBook, time: string) {
this.book = book;
this.time = time;
this.id = id;
}
checkout(time: string) {
this.time = time;
}
}
class BookRecordManager {
bookRecords: Map<string, BookRecord>;
add(id: string, ISBN: string, title: string, time: string): void {
const book = libraryBookFactory.createBook(ISBN, title);
const bookRecord = new BookRecord(id, book, time);
this.bookRecords.set(id, bookRecord);
}
checkout(id: string, time: string) {
const bookRecord = this.bookRecords.get(id);
if (bookRecord) {
bookRecord.checkout(time);
}
}
}
复制代码
使用享元模式须要符合如下条件:bash
为其余对象提供一种代理以控制对这个对象的访问。ide
代理模式包含如下角色:post
为一个对象在不一样的地址空间提供局部表明,延迟获取远程对象。
class RemoteResource {
getContent(): string {
return '读取远程文件内容';
}
}
class RemoteRecourceProxy {
getContent() {
const resource = this.request();
return resource.getContent();
}
request(): RemoteResource {
console.log('千辛万苦从远程拿到了文件')
return new RemoteResource();
}
}
function remoteProxyDemo() {
const resource = new RemoteRecourceProxy();
const content = resource.getContent();
console.log(content);
}
remoteProxyDemo();
复制代码
若是须要建立一个资源消耗较大的对象,先建立一个消耗相对较小的对象,真实对象只在须要时才会被真正建立。
// 大图片,绘制会消耗较多资源
class BigImage {
private name: string;
constructor(name: string) {
this.name = name;
this.draw();
}
// 绘制
draw(): void {
console.log('绘制 ${this.name},须要消耗大量资源');
}
// 预览
preview(): void {
console.log(`展现 ${this.name} 的预览效果`);
}
getName(): string {
return this.name;
}
}
class VirutalBigImageProxy {
private image: BigImage;
private name: string;
// 虚代理先建立一个大图片的代理,而不真正建立实际对象
constructor(name: string) {
this.name = name;
}
// 只有在要预览时,才真正绘制图像
preview(): void {
if (!this.image) {
this.image = new BigImage(this.name);
}
this.image.preview();
}
getName(): string {
if (!this.image) {
console.log('返回虚代理里保存的名称');
return this.name;
}
console.log('实际图片已经被建立,返回实际图片的名称');
return this.image.getName();
}
}
function virutalProxyDemo() {
const image1 = new VirutalBigImageProxy('图1');
const image2 = new VirutalBigImageProxy('图2');
// 读取图1的名称,此时不须要真正绘制大图片,只须要返回虚代理里存储的数据便可,减少开销
console.log(image1.getName());
// 只有在真正须要使用大图片时,才建立大图片对象
image2.preview();
}
virutalProxyDemo();
复制代码
控制对原始对象的访问,保护代理用户对象应该有不一样的访问权限的时候。
class SecretDoc {
read(): string {
return '机密文件内容';
}
}
class ProtectionSecretDocProxy {
private name: string;
private doc: SecretDoc;
constructor(name: string) {
this.name = name;
this.doc = new SecretDoc();
}
// 提供相同的方法名,可是加了权限控制的代码
read(): string {
if (this.name === '远峰') {
const content = this.doc.read();
return content;
}
return '';
}
}
function protectionProxyDemo() {
const doc1 = new ProtectionSecretDocProxy('远峰');
console.log(`远峰读出了: ${doc1.read()}`);
const doc2 = new ProtectionSecretDocProxy('其余人');
console.log(`其余人读出了: ${doc2.read()}`);
}
protectionProxyDemo();
复制代码
在访问对象时执行一些附加的操做。
class Resource {
content: string;
constructor(content: string) {
this.content = content;
}
read(): string {
return this.content;
}
write(content: string): Promise<null> {
return new Promise(resolve => {
setTimeout(() => {
this.content = content;
resolve();
}, 1000);
})
}
}
// 智能代理,多了一个是否上锁的属性,以及相关对锁的操做
class SmartResourceProxy {
lock: boolean;
resource: Resource;
constructor() {
this.resource = new Resource('文件内容');
}
read(): string|Error {
if (this.lock) { return new Error('别人正在写'); }
console.log('正在读');
return this.resource.read();
}
write(content: string) {
console.log('正在写')
this.lock = true;
this.resource.write(content)
.then(() => {
this.lock = false;
});
}
}
function smartProxyDemo() {
const resource = new SmartResourceProxy();
// 能读到内容
console.log(resource.read());
resource.write('新的文件内容');
// 因为别人正在写,读不到内容
try {
resource.read();
} catch (e) {
console.error(e);
}
}
smartProxyDemo();
复制代码
代理模式公有优势:
不一样代理模式有各自的优势:
本文介绍了前4种结构型模式,对后续模式感兴趣的同窗能够关注专栏或者发送简历至'tao.qit####alibaba-inc.com'.replace('####', '@'),欢迎有志之士加入~