享元模式的概念
在物件導向軟體設計中,利用公用對象不僅能節省資源還能提高效能。共用的對象只能提供某些內在的資訊,而不能用來識別對象。專門用於設計可共用對象的一種設計模式叫做享元模式(Flyweight pattern)。
實現享元模式需要兩個關鍵組件,通常是可共用的享元對象和儲存他們的池。某種中央對象維護這個池,並從它返回適當的執行個體。
運用共用技術有效地支援大量細粒度的對象。
公用交通(如公用汽車)已有一百多年的曆史了。大量去往相同方向的乘客可以分擔保有和經營車輛(如公用汽車)的費用。公用汽車有多個月台,乘客沿著路線在接近他們目的地的地方上下車。到達目的地的費用僅與行程有關。跟保有車輛相比,乘坐公用汽車要便宜得多。這就是利用公用資源的好處。
在物件導向軟體設計中,我們利用公用對象不僅能節省資源還能提高效能。比方說,某個人物需要一個類的一百萬個執行個體,但我們可以把這個類的一個執行個體讓大家共用,而把某些獨特的資訊放在外部,節省的資源可能相當可觀(一個執行個體與一百萬個執行個體的差別)。共用的對象只提供某些內在的資訊,而不能用來識別對象。專門用於設計可共用對象的一種設計模式叫做享元模式。
使得享元對象是輕量級的最重要原因是什麼呢?不是它們的大小,而是通過共用能夠節省的空間總量。某些對象的獨特狀態可以拿到外部,在別處管理,其餘部分被共用。比如說,原來需要一個類的一百萬個對象,但因為這個類的對象為享元,現在只要一個就夠了。這就是由於可共用的享元對象讓整個系統變得輕量的原因。通過仔細的設計,記憶體的節省非常可觀。在iOS開發中,節省記憶體意味著提升整體效能。
享元模式的執行個體應用
我們建立一個WebSiteFactory工廠類,來維護池中的享元對象,根據父類型返回各種類型的具體享元對象,代碼如下:
複製代碼 代碼如下:
#import <Foundation/Foundation.h>
#import "WebSiteProtocol.h"
@interface WebSiteFactory : NSObject
@property (nonatomic, strong) NSDictionary *flyweights; //共用對象
- (id<WebSiteProtocol>)getWebSiteCategory:(NSString *)webKey;
- (NSInteger)getWebSiteCount;
@end
複製代碼 代碼如下:
#import "WebSiteFactory.h"
#import "ConcreteWebSite.h"
@implementation WebSiteFactory
- (instancetype)init {
self = [super init];
if (self) {
_flyweights = [NSDictionary dictionary];
}
return self;
}
- (id<WebSiteProtocol>)getWebSiteCategory:(NSString *)webKey {
__block id<WebSiteProtocol> webset = nil;
[self.flyweights enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
if (webKey == key) {
webset = obj;
*stop = YES;
}
}];
if (webset == nil) {
ConcreteWebSite *concreteWebset = [[ConcreteWebSite alloc] init];
concreteWebset.webName = webKey;
webset = concreteWebset;
NSMutableDictionary *mutabledic = [NSMutableDictionary dictionaryWithDictionary:self.flyweights];
[mutabledic setObject:webset forKey:webKey];
self.flyweights = [NSDictionary dictionaryWithDictionary:mutabledic];
}
return webset;
}
- (NSInteger)getWebSiteCount {
return self.flyweights.count;
}
@end
代碼中的getWebSiteCategory方法可以返回具體的享元對象,返回的這個享元對象同時遵守WebSiteProtocol的協議,WebSiteProtocol的代碼如下:
複製代碼 代碼如下:
#import <Foundation/Foundation.h>
#import "User.h"
@protocol WebSiteProtocol <NSObject>
- (void)use:(User *)user;
@end
ConcreteWebSite的代碼如下:
複製代碼 代碼如下:
#import <Foundation/Foundation.h>
#import "WebSiteProtocol.h"
@interface ConcreteWebSite : NSObject <WebSiteProtocol>
@property (nonatomic, copy) NSString *webName;
@end
複製代碼 代碼如下:
#import "ConcreteWebSite.h"
@implementation ConcreteWebSite
- (void)use:(User *)user {
NSLog(@"網站分類:%@ 使用者名稱字:%@", self.webName, user.userName);
}
@end
User的代碼如下:
複製代碼 代碼如下:
#import <Foundation/Foundation.h>
@interface User : NSObject
@property (nonatomic, copy) NSString *userName;
@end
複製代碼 代碼如下:
#import "User.h"
@implementation User
@end
至此,享元模式的代碼已經完成了,我們來看下在用戶端怎麼使用享元模式,代碼如下:
複製代碼 代碼如下:
#import "ViewController.h"
#import "WebSiteProtocol.h"
#import "WebSiteFactory.h"
#import "ConcreteWebSite.h"
#import "User.h"
typedef id<WebSiteProtocol> WebsiteType;
@interface ViewController ()
@end
複製代碼 代碼如下:
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 通過Factory 方法返回各種具體享元對象,維護池中的享元對象
WebSiteFactory *factory = [[WebSiteFactory alloc] init];
// 返回具體的享元對象
WebsiteType type1 = [factory getWebSiteCategory:@"首頁"];
User *user1 = [[User alloc] init];
user1.userName = @"張三";
// 享元對象都具有use方法
[type1 use:user1];
WebsiteType type2 = [factory getWebSiteCategory:@"商店"];
User *user2 = [[User alloc] init];
user2.userName = @"李四";
[type2 use:user2];
WebsiteType type3 = [factory getWebSiteCategory:@"案例"];
User *user3 = [[User alloc] init];
user3.userName = @"王五";
[type3 use:user3];
NSInteger count = [factory getWebSiteCount];
NSLog(@"個數: %ld", (long)count);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
輸出如下:
2015-09-12 15:59:55.322 FlyweightPattern[42020:1723017] 網站分類:首頁 使用者名稱字:張三2015-09-12 15:59:55.322 FlyweightPattern[42020:1723017] 網站分類:商店 使用者名稱字:李四2015-09-12 15:59:55.322 FlyweightPattern[42020:1723017] 網站分類:案例 使用者名稱字:王五2015-09-12 15:59:55.323 FlyweightPattern[42020:1723017] 個數: 3
分享相同的資源以執行任務,可能比使用個人的資源完成同樣的事情更加高效。享元模式可以通過共用一部分必需的對象,來節省大量的記憶體。
何時使用享元模式
(1)應用程式使用很多個物件;
(2)在記憶體中儲存對象會影響記憶體效能;
(3)對象的多數特有狀態(外在狀態)可以放到外部而輕量化;
(3)移除了外在狀態後,可以用較少的共用對象替代原來的那組對象;
(4)應用程式不依賴於對象標示,因為共用對象不能提供唯一的標示。