前言
在生活中,公共设有多个车站,乘客沿着路线在接近他们目的地的车站上下车。到达目的地的费用仅与行程有关,跟私有车辆相比,乘坐公共交通要便宜的多。同时,大量去往相同方向的乘客可以分担维修和经营车辆(公共汽车、地铁)的费用。这就是利用公共资源的好处。
在面向对象软件设计中,利用公共对象可以节省资源和提高性能。比方说,某个任务需要一个类的一百万个实例,但我们可以把这个类的一个实例让大家共享,而把某些独特的信息放在外部,节省的资源相当可观。共享的对象只提供某些内在的信息,而不能用来识别对象。专门用于设计可共享对象的一种设计模式叫做享元模式。
什么是享元模式
所谓”享元“,就是被共享的单元。实现享元模式需要两个关键组件,通常是可共享的享元对象和保存它们的池。某种中央对象(工厂是这一角色的理想候选)维护这个池,并从它返回适当的实例。享元模式的意图是复用对象,节省内存。其主要用于减少创建对象的数量,以减少内存占用和提高性能。其尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。
Flyweight
是两个具体享元类 ConcreteFlyweight1
和 ConcreteFlyweight2
的父接口(协议)。每个 ConcreteFlyweight
类维护不能用于识别对象的内在状态 intrinsicState
。 Flyweight
声明了 operation:extrinsicState
方法,由 ConcreteFlyweight
类实现。 intriinsicState
是享元部分可被共享的部分,而 extrinsicState
补充缺少的信息,让享元对象唯一。
什么时候使用享元模式
- 系统有大量相似对象
- 这些对象消耗大量内存。
- 对象的外在状态可以放到外部而轻量化
- 移除了外在状态后,可以用较少的共享对象替代原来的那组对象
- 应用程序不依赖于对象标识,因为共享对象不能提供唯一的标识
享元模式的优缺点
享元模式的优点
大大减少对象的创建,降低系统的内存,使效率提高。
享元模式的缺点
提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。
享元模式的实现
用 6 个不同样的花朵图案,画很多个随机尺寸和位置的花,首先需要设计并实现可共享的花朵,使用享元模式需要一种享元工厂和一些享元产品 。
首先,每个图案由一个唯一的 FlowerView
的实例来维护。FlowerView
是 UIImageView
的子类,用它可以绘制一朵花朵图案。
@interface FlowerView : UIImageView
{
}
- (void) drawRect:(CGRect)rect;
@end
@implementation FlowerView
- (void) drawRect:(CGRect)rect
{
[self.image drawInRect:rect];
}
@end
接着创建一个享元工厂类 FlowerFactory
, FlowerFactory
用 flowerPool
聚合了一个花朵池的引用。flowerPool
是一个保存 FlowerView
的所有实例的数据结构。如果池中没有所请求花朵的类型,就会创建一个新的 FlowerView
的实例。尽管池中对象的类是 FlowerView
, 但客户端只要求 FlowerFactory
返回 UIView
的实例。kTotalNumberOfFlowerTypes
是所支持花朵类型的总数。
typedef enum
{
kAnemone,
kCosmos,
kGerberas,
kHollyhock,
kJasmine,
kZinnia,
kTotalNumberOfFlowerTypes
} FlowerType;
@interface FlowerFactory : NSObject
{
@private
NSMutableDictionary *_flowerPool;
}
- (UIView *) flowerViewWithType:(FlowerType)type;
@end
@implementation FlowerFactory
- (UIView *) flowerViewWithType:(FlowerType)type
{
// lazy-load a flower pool
if (_flowerPool == nil)
{
_flowerPool = [[NSMutableDictionary alloc]
initWithCapacity:kTotalNumberOfFlowerTypes];
}
// try to retrieve a flower
// from the pool
UIView *flowerView = [_flowerPool objectForKey:[NSNumber
numberWithInt:type]];
// if the type requested
// is not available then
// create a new one and
// add it to the pool
if (flowerView == nil)
{
UIImage *flowerImage;
switch (type)
{
case kAnemone:
flowerImage = [UIImage imageNamed:@"anemone.png"];
break;
case kCosmos:
flowerImage = [UIImage imageNamed:@"cosmos.png"];
break;
case kGerberas:
flowerImage = [UIImage imageNamed:@"gerberas.png"];
break;
case kHollyhock:
flowerImage = [UIImage imageNamed:@"hollyhock.png"];
break;
case kJasmine:
flowerImage = [UIImage imageNamed:@"jasmine.png"];
break;
case kZinnia:
flowerImage = [UIImage imageNamed:@"zinnia.png"];
break;
default:
break;
}
flowerView = [[FlowerView alloc]
initWithImage:flowerImage];
[_flowerPool setObject:flowerView
forKey:[NSNumber numberWithInt:type]];
}
return flowerView;
}
@end
定义 ExtrinsicFlowerState
数据结构用于保存享元对象的外部状态(显示的位置),并保存对应 FlowerView
的指针。
typedef struct
{
UIView *flowerView;
CGRect area;
} ExtrinsicFlowerState;
通过 flowerList
数组保存的每个花朵的外部状态信息 ExtrinsicFlowerState
将其绘制在屏幕上
@interface FlyweightView : UIView
{
@private
NSArray *_flowerList;
}
@property (nonatomic, retain) NSArray *flowerList;
@end
@implementation FlyweightView
@synthesize flowerList=_flowerList;
extern NSString *FlowerObjectKey, *FlowerLocationKey;
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
for (NSValue *stateValue in _flowerList)
{
ExtrinsicFlowerState state;
[stateValue getValue:&state];
UIView *flowerView = state.flowerView;
CGRect area = state.area;
[flowerView drawRect:area];
}
}
@end
随机生成 500 朵花的位置,通过数组存储这些外部状态,通过享元对象共享内部信息,将其实现。
FlowerFactory *factory = [[[FlowerFactory alloc] init] autorelease];
NSMutableArray *flowerList = [[[NSMutableArray alloc]
initWithCapacity:500] autorelease];
for (int i = 0; i < 500; ++i)
{
// retrieve a shared instance
// of a flower flyweight object
// from a flower factory with a
// random flower type
FlowerType flowerType = arc4random() % kTotalNumberOfFlowerTypes;
UIView *flowerView = [factory flowerViewWithType:flowerType];
// set up a location and an area for the flower
// to display onscreen
CGRect screenBounds = [[UIScreen mainScreen] bounds];
CGFloat x = (arc4random() % (NSInteger)screenBounds.size.width);
CGFloat y = (arc4random() % (NSInteger)screenBounds.size.height);
NSInteger minSize = 10;
NSInteger maxSize = 50;
CGFloat size = (arc4random() % (maxSize - minSize + 1)) + minSize;
// assign attributes for a flower
// to an extrinsic state object
ExtrinsicFlowerState extrinsicState;
extrinsicState.flowerView = flowerView;
extrinsicState.area = CGRectMake(x, y, size, size);
// add an extrinsic flower state
// to the flower list
[flowerList addObject:[NSValue value:&extrinsicState
withObjCType:@encode(ExtrinsicFlowerState)]];
}
// add the flower list to
// this FlyweightView instance
[(FlyweightView *)self.view setFlowerList:flowerList];
总结
享元模式使用共享技术有效地支持大量细粒度的对象。在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。需要注意的是划分外部状态和内部状态,否则可能会引起线程安全问题,这些类必须有一个工厂对象加以控制。