做者簡介 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('####', '@'),歡迎有志之士加入~