iOS设计模式之工厂方法

1,024 阅读3分钟

定义

工厂模式:定义创建对象的接口,让子类决定实例化哪一个类,工厂方法使得一个类的实例化延迟到其子类。

使用场景

下列情况会适合使用工厂模式:

  • 编译时无法准确与其要创建的对象类
  • 类想让其子类决定在运行时创建什么
  • 类有若干个辅助类为其子类,而你想将返回某个子类这一信息局部化

在Cocoa Touch框架中,NSNumber就是典型的工厂方法构造。NSNumber通过类方法numberWithBool:接口实例化NSCFBoolean实例,将bool值参数传递给实例化对象。

类图结构

工厂方法模式的类结构图如下:

抽象的Product定义了创建对象的接口init。ConcreteProduct实现Product接口,返回自身的实例。Creator定义了返回Product对象的工厂方法。Creator可以有个一默认实现,返回默认的ConcreteProduct对象。ConcreteCreator是Creator的子类,重载工厂方法,返回ConcreteProduct的对象实例。创建时,根据ConcreteCreator实例来创建对应的ConcreteProduct实例,隐藏了ConcreteProduct的复杂创建过程。

代码实现

下面我们来用代码实现下工厂模式。

需求如下:实现一个画布页面背景,根据用户操作或者默认行为生成不同的背景图,背景图类型样式会随着需求不断的新增。

实现如下:

@interface XQCanvasView : UIView

- (instancetype)initWithFrame:(CGRect)frame;

@end

XQCanvasView是背景图的父类,定义一个创建接口initWithFrame

@interface XQPaperCanvasView : XQCanvasView

@end

@implementation XQPaperCanvasView

- (instancetype)initWithFrame:(CGRect)frame{
    if (self = [super initWithFrame:frame]) {
        UIImageView *imageView = [UIImageView new];
        imageView.image = [UIImage imageNamed:@"paper.jpg"];
        imageView.frame = frame;
        [self addSubview:imageView];
    }
    return self;
}

@end

XQPaperCanvasView实现父类的创建接口initWithFrame,增加纸纹理的背景

@interface XQClothCanvasView : XQCanvasView

@end

@implementation XQClothCanvasView

- (instancetype)initWithFrame:(CGRect)frame{
    if (self = [super initWithFrame:frame]) {
        UIImageView *imageView = [UIImageView new];
        imageView.image = [UIImage imageNamed:@"cloth.jpg"];
        imageView.frame = frame;
        [self addSubview:imageView];
    }
    return self;
}

@end

XQClothCanvasView实现父类创建接口,增加布纹理的背景

@interface XQCanvasViewGenerator : NSObject

- (XQCanvasView *)canvasViewWithFrame:(CGRect)frame;

@end

@implementation XQCanvasViewGenerator

- (XQCanvasView *)canvasViewWithFrame:(CGRect)frame{
    return [[XQCanvasView alloc] initWithFrame:frame];
}

@end

XQCanvasViewGenerator为生产者的父类,定义一个创建画布背景图XQCanvasView的接口。默认实现返回一个空白的XQCanvasView

@interface XQPaperCanvasViewGenerator : XQCanvasViewGenerator

@end

@implementation XQPaperCanvasViewGenerator

- (XQCanvasView *)canvasViewWithFrame:(CGRect)frame{
    return [[XQPaperCanvasView alloc] initWithFrame:frame];
}

@end

XQPaperCanvasViewGenerator实现父类的canvasViewWithFrame工厂方法,生成XQPaperCanvasView的纸纹理背景对象。

@interface XQClothCanvasViewGenerator : XQCanvasViewGenerator

@end

@implementation XQClothCanvasViewGenerator

- (XQCanvasView *)canvasViewWithFrame:(CGRect)frame{
    return [[XQClothCanvasView alloc] initWithFrame:frame];
}

@end

XQClothCanvasViewGenerator实现父类的canvasViewWithFrame工厂方法,生成XQClothCanvasView的布纹理背景对象。

上面就是所有工厂方法涉及的类,下面看使用:

@interface XQCanvasViewController : UIViewController

- (void)loadCanvasViewWithGenerator:(XQCanvasViewGenerator *)generator;

@end

@interface XQCanvasViewController ()

@property (nonatomic, strong)XQCanvasView *canvasView;

@end

@implementation XQCanvasViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self loadCanvasViewWithGenerator:[XQClothCanvasViewGenerator new]];
}


- (void)loadCanvasViewWithGenerator:(XQCanvasViewGenerator *)generator{
    [self.canvasView removeFromSuperview];
    XQCanvasView *aCanvas = [generator canvasViewWithFrame:self.view.bounds];
    self.canvasView = aCanvas;
    [self.view addSubview:aCanvas];
}

@end

画布视图控制器XQCanvasViewController定义了一个加载背景图的方法loadCanvasViewWithGenerator,供外部调用配置不同的背景图。在viewDidLoad中,调用[self loadCanvasViewWithGenerator:[XQClothCanvasViewGenerator new]];生成默认的布纹理背景图。loadCanvasViewWithGenerator方法实现中,先移除上一次的背景图self.canvasView,然后再用生成器generator生成对应的不同背景图。

当需求新增其他类型复杂的背景图时,只需新增对应的XQCanvasView背景图子类,与其对应的XQCanvasViewGenerator生成器子类,在调用的地方传递新生成的XQCanvasViewGenerator生成器子类即可。

总结

工厂方法是在面向对象软件设计中应用非常普遍的设计模式。工厂方法从代码中消除了对应用特有类的耦合,只需处理产品Product的抽象接口即可。