享元模式

Objective-C编程之道 iOS设计模式解析
iOS设计模式解析-工厂模式
iOS设计模式解析-抽象工厂模式
iOS设计模式解析-外观模式
iOS设计模式解析-中介者模式
iOS设计模式解析-观察者模式
iOS设计模式解析-装饰模式
iOS设计模式解析-责任链模式
iOS设计模式解析-模板方法
iOS设计模式解析-策略模式
iOS设计模式解析-享元模式
iOS设计模式解析-代码地址

何为享元模式

实现享元模式需要两个关键组件,通常是可共享的享元对象和保存它们的池。某种中央对象维护这个池,并从它返回适当的实例。

享元模式:运用共享技术有效地支持大量细粒度的对象。

何时使用享元模式

在以下情形,自然会考虑使用这一模式。

  • 应用程序使用很多对象;
  • 在内存中保存对象会影响内幕内存性能;
  • 对象的多数持有状态(外在状态)可以放到外部而轻量化;
  • 移除了外在状态之后,可以用较少的共享对象替代原来的那组对象;
  • 应用程序不依赖于对象标识,因为共享对象不能提供唯一的标识。

下面使用可共享的花朵池绘制几百个(甚至更多)花朵图案,以说明这一模式的概念。

FlowerView
#import 
#import 

@interface FlowerView : UIImageView 

- (void) drawRect:(CGRect)rect;

@end
#import "FlowerView.h"

@implementation FlowerView

- (void) drawRect:(CGRect)rect
{
  [self.image drawInRect:rect];
}
@end

再简单不过了!它只重载了UIImageViewdrawRect:rect方法仅此而已。

FlowerFactory
#import 
#import 

typedef enum 
{
  kAnemone,
  kCosmos,
  kGerberas,
  kHollyhock,
  kJasmine,
  kZinnia,
  kTotalNumberOfFlowerTypes
} FlowerType;

@interface FlowerFactory : NSObject 
{
  NSMutableDictionary *flowerPool;
}

- (UIView *) flowerViewWithType:(FlowerType)type;

@end

FlowerFactory有一个NSMutableDictionary变量flowerPool,用来保存整个可供返回花朵的池。它也定义了根据FlowerType参数返回特定UIView实例的工厂方法flowerViewWithType:(FlowerType)typeFlowerType是根据花的名称定义的一组枚举值,显然,kTotalNumberOfFlowerTypes是所支持花朵类型的总数。

#import "FlowerFactory.h"
#import "FlowerView.h"

@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

花朵池flowerPool没有在FlowerFactoryinit方法中初始化,而是在工厂方法flowerViewWithType:中进行懒加载。池的默认容量是kTotalNumberOfFlowerTypes(也就是6)。如果请求的实例不在池中,工厂就会用适当的花朵图像创建一个新的flowerView的实例,并以type作为键,以新的实例作为值,加到池中。对于已创建的花朵,随后的请求将返回池中的实例,从池中共享花朵的机制相当简单。

客户端调用
- (void)viewDidLoad
{
    [super viewDidLoad];
    
    // construct a flower list
    FlowerFactory *factory = [[FlowerFactory alloc] init];
    NSMutableArray *flowerList = [[NSMutableArray alloc]
                                   initWithCapacity:500];
    
    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];
//        UIView *flowerView = [[FlowerView alloc]
//           initWithImage:[UIImage imageNamed:@"zinnia.png"]];
        // 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 = [[ExtrinsicFlowerState alloc]init];
        extrinsicState.flowerView = flowerView;
        extrinsicState.area = CGRectMake(x, y, size, size);
        
        // add an extrinsic flower state
        // to the flower list
        [flowerList addObject:extrinsicState];
    }
    
    // add the flower list to
    // this FlyweightView instance
    //xib中记得设置view为FlyweightView
    [(FlyweightView *)self.view setFlowerList:flowerList];
}

节省内存的事情请看下面两种方法,然后做出比较(500数字可适当改大)

UIView *flowerView = [factory flowerViewWithType:flowerType];
UIView *flowerView = [[FlowerView alloc]
          initWithImage:[UIImage imageNamed:@"zinnia.png"]];

总结:我们设计了一个可以在屏幕上显示500朵花的程序,而只用了6个不同花朵图案的实例,这些不同的花朵实例,把一些与众不同的可被标识的信息(即位置和大小)去掉,只剩下显示花朵图案的基本操作。在请求特定的花朵的时候,客户端需要向花朵实例提供某些与众不同的信息(外在状态),让它使用这些信息绘制一朵与众不同的花。读到这里你是否想起了cell的重用池呢?本节的例子很简单,demo链接在上面。

你可能感兴趣的:(享元模式)