正文
结构
享元模式包含以下角色:
-
Flyweight(抽象享元类):通常是一个接口或抽象类,在抽象享元类中声明了具体享元类公共的方法,这些方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法来设置外部数据(外部状态)。
-
ConcreteFlyweight(具体享元类):它实现了抽象享元类,其实例称为享元对象;在具体享元类中为内部状态提供了存储空间。通常我们可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象。
-
UnsharedConcreteFlyweight(非共享具体享元类):并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可设计为非共享具体享元类;当需要一个非共享具体享元类的对象时可以直接通过实例化创建。
-
FlyweightFactory(享元工厂类):享元工厂类用于创建并管理享元对象,它针对抽象享元类编程,将各种类型的具体享元对象存储在一个享元池中,享元池一般设计为一个存储“键值对”的集合(也可以是其他类型的集合),可以结合工厂模式进行设计;当用户请求一个具体享元对象时,享元工厂提供一个存储在享元池中已创建的实例或者创建一个新的实例(如果不存在的话),返回新创建的实例并将其存储在享元池中。
示例
// 书籍类,书的基本信息和借阅信息都是属性
// 但同一本书可以被多次借出,对借阅记录来说,同一本书的多次借阅记录里存储的书的信息是冗余的
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