工厂模式

1、工厂模式的种类:
工厂模式是一种比较常用的设计模式,具体来说主要包含三种:
1)简单工厂模式(非严格意义上)
2)工厂方法模式
3)抽象工厂模式

2、工厂模式的作用:工厂模式是用来封装对象的创建的。

3、工厂模式的简要概述
1)简单工厂模式:将创建对象的细节封装在一个类里面,该类提供一个接口,外界可以通过这个类的这个接口得到实例对象。这个类就是工厂类。严格意义上讲,简单工厂不是一种设计模式,而更像是一种编程习惯。
2)工厂方法模式:定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。也就是通过让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的。
3)抽象工厂模式:提供一组接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。抽象工厂允许客户使用抽象的接口来创建一组相关的产品,而不需要知道(或关心)实际产出的具体产品是什么。这样一来,客户就从具体的产品中被解耦。

4、工厂模式举例
1)简单工厂模式
假设有一个小明餐馆(XiaoMingRestaurant),他家主要经营面条。如果顾客要订一碗面条,我们可以这样用代码表达:

typedef NS_ENUM(NSUInteger, NoodlesType) {
    BeijingSauceNoodle,
    ShanXiSlicedNoodle,
    LanzhouHandPulledNoodle,
};

@interface XiaoMingRestaurant ()
@property (nonatomic, strong) Noodles *noodles;
@end
@implementation XiaoMingRestaurant

- (instancetype)init {
    if (self = [super init]) {
        self.noodles = [self orderNoodles:BeijingSauceNoodle];
    }
    return  self;
}

- (Noodles *)orderNoodles:(NoodlesType) type{
    switch (type) {
        case BeijingSauceNoodle:
            return [[BeijingSauceNoodles alloc] init];
        case ShanXiSlicedNoodle:
            return [[ShanXiSlicedNoodles alloc] init];
        case LanzhouHandPulledNoodle:
            return [[LanzhouHandPulledNoodles alloc] init];
        default:
            return nil;
            break;
    }
}

@end

这样看上去似乎合理,但是如果还有很多餐厅(如小强餐厅)也要订购面条,那么orderNoodels:方法就需要在小强餐厅进行重复。
下面利用简单工厂模式来封装。

#import "NoodlesFactory.h"
#import "Noodles.h"
#import "BeijingSauceNoodles.h"
#import "ShanXiSlicedNoodles.h"
#import "LanzhouHandPulledNoodles.h"

@implementation NoodlesFactory
+ (Noodles *)orderNoodles:(NoodlesType)type {
    switch (type) {
        case BeijingSauceNoodle:
            return [[BeijingSauceNoodles alloc] init];
        case ShanXiSlicedNoodle:
            return [[ShanXiSlicedNoodles alloc] init];
        case LanzhouHandPulledNoodle:
            return [[LanzhouHandPulledNoodles alloc] init];
        default:
            return nil;
            break;
    }
}
@end
#import <Foundation/Foundation.h>
@class Noodles;
typedef NS_ENUM(NSUInteger, NoodlesType) {
    BeijingSauceNoodle,
    ShanXiSlicedNoodle,
    LanzhouHandPulledNoodle,
};

@interface NoodlesFactory : NSObject

+ (Noodles *)orderNoodles:(NoodlesType)type;
@end

客户在使用的时候就非常简单了,如下:

#import "XiaoMingRestaurant.h"
#import "Noodles.h"
#import "NoodlesFactory.h"

//#import "BeijingSauceNoodles.h"
//#import "ShanXiSlicedNoodles.h"
//#import "LanzhouHandPulledNoodles.h"

//typedef NS_ENUM(NSUInteger, NoodlesType) {
//    BeijingSauceNoodle,
//    ShanXiSlicedNoodle,
//    LanzhouHandPulledNoodle,
//};

@interface XiaoMingRestaurant ()
@property (nonatomic, strong) Noodles *noodles;
@end
@implementation XiaoMingRestaurant

- (instancetype)init {
    if (self = [super init]) {
//        self.noodles = [self orderNoodles:BeijingSauceNoodle];
        self.noodles = [NoodlesFactory orderNoodles:BeijingSauceNoodle];
    }
    return  self;
}

//- (Noodles *)orderNoodles:(NoodlesType)type {
//    switch (type) {
//        case BeijingSauceNoodle:
//            return [[BeijingSauceNoodles alloc] init];
//        case ShanXiSlicedNoodle:
//            return [[ShanXiSlicedNoodles alloc] init];
//        case LanzhouHandPulledNoodle:
//            return [[LanzhouHandPulledNoodles alloc] init];
//        default:
//            return nil;
//            break;
//    }
//}

@end

上面的这个例子的封装,就是简单的工厂模式的一个小例子。给人的感觉好像没有太大的变化,就是将一个方法搬到了另一个类中。但是在这里你要明确的是XiaoMingRestaurant类是客户,这样的客户可能有很多,将创建对象的代码集中在一个类中处理好处是有很多的。例如:如果这个工厂又增加了新的面条种类,那么在这一个类中修改就可以了。还有就是客户类(XiaoMingRestaurant)中的代码不在耦合具体的类了(如BeijingSauceNoodles类),这样就达到了解耦合的作用。

2)工厂方法模式
定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。也就是通过让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的。
在上面这个例子中,所设计的业务是比较简单的。如果我们想对上面的业务进行扩展,那么我们可能就需要使用工厂方法模式来做更合适了。
如果各种面条都有手工面和机器制作的这两种,那么这些面条又可以分为平行的两派了。
这里写图片描述

如果这样的情形用简单工厂来做的话,就会将很多这样的子类封装在一个类中,代码就会写得很长,而且可扩展性也是不好的。
现在我们定义一个面条工厂类,这个类可以当做一个抽象类来看待。
NoodlesFactory.h文件

#import <Foundation/Foundation.h>
@class Noodles;
typedef NS_ENUM(NSUInteger, NoodlesType) {
    BeijingSauceNoodle,
    ShanxiSlicedNoodle,
    LanzhouPulledNoodle
};

@protocol NoodleFactoryInterface <NSObject>
@required
//定义了一个工厂方法,该方法需要由子类来实现
- (Noodles *)createNoodles:(NoodlesType)type;
@end

@interface NoodlesFactory : NSObject<NoodleFactoryInterface>

- (Noodles *)orderNoodles:(NoodlesType)type;
@end

NoodlesFactory.m文件

@interface NoodlesFactory ()
@property (nonatomic, strong) Noodles *noodles;
@end

@implementation NoodlesFactory

- (Noodles *)orderNoodles:(NoodlesType)type {
    self.noodles = [self createNoodles:type];
    //得到对象实例后,可以noodles对象再做一些其他的操作,而不用关心具体是什么面条
    return self.noodles;
}

@end

然后我们对NoodlesFactory派生出两个子类:
HandmadeNoodlesFactory和RobotmadeNoodlesFactory。
HandmadeNoodlesFactory.h文件的实现如下:

#import "NoodlesFactory.h"
@interface HandmadeNoodlesFactory : NoodlesFactory
//工厂方法
- (Noodles *)createNoodles:(NoodlesType)type;
@end

HandmadeNoodlesFactory.m文件的实现如下:

#import "HandmadeNoodlesFactory.h"
#import "BeijingSauceHandmadeNoodles.h"
#import "ShanxiSlicedHandmadeNoodles.h"
#import "LanzhouHandmadeNoodles.h"

@implementation HandmadeNoodlesFactory

- (Noodles *)createNoodles:(NoodlesType)type {
        switch (type) {
            case BeijingSauceNoodle:
                return [[BeijingSauceHandmadeNoodles alloc] init];
            case ShanxiSlicedNoodle:
                return [[ShanxiSlicedHandmadeNoodles alloc] init];
            case LanzhouPulledNoodle:
                return [[LanzhouHandmadeNoodles alloc] init];
            default:
                return nil;
                break;
        }
}
@end

另外一个类的实现和HandmadeNoodlesFactory文件类的实现是相似的,这里就不再罗列出来了。
这时小明要想订购面条就很简单了,代码如下:

#import "XiaoMingRestaurant.h"
#import "Noodles.h"
#import "NoodlesFactory.h"
#import "HandmadeNoodlesFactory.h"
#import "RobotmadeNoodlesFactory.h"

@interface XiaoMingRestaurant ()
@property (nonatomic, strong) Noodles *noodles;
@end
@implementation XiaoMingRestaurant

- (instancetype)init {
    if (self = [super init]) {
        NoodlesFactory *factory = [[HandmadeNoodlesFactory alloc] init];
        self.noodles = [factory orderNoodles:BeijingSauceNoodle];
    }
    return  self;
}

@end

可以看到如果顾客需要吃手工面条做的北京杂酱面,那么小明餐厅只需要在手工面条工厂取面条就可以了。在这个工厂里面取面条,那么得到的都是手工类型的面条。
下面总结一下工厂方法:
工厂方法用来处理对象的创建,并将这样的行为封装在子类中。
它的格式一般是这样的:

abstract Product factoryMethod(enum type)

工厂方法是抽象的,所以依赖子类进行对象的创建。工厂方法的返回值就是返回一个产品对象,通常在父类中还会使用到子类工厂方法的返回值。工厂方法将对象的创建和使用分离开来。当然工厂方法也可以没有参数。

3)抽象工厂模式
提供一组接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。抽象工厂允许客户使用抽象的接口来创建一组相关的产品,而不需要知道(或关心)实际产出的具体产品是什么。这样一来,客户就从具体的产品中被解耦。
工厂方法模式是创建一种类型的单个产品,而抽象工厂模式则是用来创建一组相关的产品,不在是创建一个单个的产品了。将这一组产品关联在一个类中是因为它们之间存在着关联性。抽象工厂模式中单个产品创建其实用到的就是工厂方法模式。
抽象工厂模式:提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类型。
下面还是以面条的例子在说一个场景。面条是由面粉制作的,但是面粉的总类可能是不一样的,一般的面条是用小麦粉做的,还有的是用黄豆粉,还有的是用绿豆粉等等,有的面粉里面可能会放入胡椒粉,有的可能会放入盐等调味品。
下面我们看一下这个设计的整体图
这里写图片描述
这个是整个业务逻辑的结构图。下面对涉及到抽象工厂的原料工厂类进行细分和拆解。
这里写图片描述
上面这个图就能很好的说明抽象工厂。由于涉及到的类比较多,这里给出一些关于抽象工厂的关键代码。
NoodlesIngredientFactory.h文件

#import <Foundation/Foundation.h>
@class Flour;
@class Flavouring;
@protocol NoodleIngredientFactoryInterface <NSObject>

@required
- (Flour *)createFlour;
- (Flavouring *)createFlavouring;

@end

@interface NoodlesIngredientFactory : NSObject<NoodleIngredientFactoryInterface>

@end

BJNoodlesIngredientFactory.m

#import "BJNoodlesIngredientFactory.h"
#import "SaltFlavouring.h"
#import "WheatFlour.h"
@implementation BJNoodlesIngredientFactory
- (Flour *)createFlour {
    return [[WheatFlour alloc] init];
}
- (Flavouring *)createFlavouring {
    return [[SaltFlavouring alloc] init];
}
@end

同理,LZNoodlesIngredientFactory.m文件中的代码是类似的。

下面再看一下面条类(Noodles)是如何使用原料工厂的。面条类只关心原料,它不关心是那个工厂提供的。
Noodles.h文件

#import <Foundation/Foundation.h>
@class Flour;
@class Flavouring;
@class NoodlesIngredientFactory;

@interface Noodles : NSObject

//面粉的类型
@property (nonatomic, strong) Flour *flour;
//调味品
@property (nonatomic, strong) Flavouring *flavouring;
//面条原料厂
@property (nonatomic, strong) NoodlesIngredientFactory *noodlesIngredentFactory;

- (void)makeNoodles;
@end

对noodles的具体子类,以BeijingSauceNoodles为例
BeijingSauceNoodles.m文件

#import "BeijingSauceNoodles.h"
#import "NoodlesIngredientFactory.h"

@implementation BeijingSauceNoodles

- (instancetype)initBeijingSauceNoodlesWithIngredientFactory:(NoodlesIngredientFactory *)ingredientFactory {
    if (self = [super init]) {
        self.noodlesIngredentFactory = ingredientFactory;
    }
    return self;
}

+ (instancetype)beijingSauceNoodlesWithIngredientFactory:(NoodlesIngredientFactory *)ingredientFactory {
    return [[self alloc] initBeijingSauceNoodlesWithIngredientFactory:ingredientFactory];
}

- (void)makeNoodles {
    self.flour = [self.noodlesIngredentFactory createFlour];
    self.flavouring = [self.noodlesIngredentFactory createFlavouring];
}
@end

BeijingSauceNoodles.h文件

#import "Noodles.h"
@class NoodlesIngredientFactory;
@interface BeijingSauceNoodles : Noodles

+ (instancetype)beijingSauceNoodlesWithIngredientFactory:(NoodlesIngredientFactory *)ingredientFactory;
@end

通过代码我们可以看出,BeijingSauceNoodles需要一个原料工厂提供原料,但是是什么工厂来提供并不关心。
面条工厂是用来生产面条的,由它来决定该用什么原料工厂。下面我们可以看一下代码:
NoodlesFactory.h文件

#import <Foundation/Foundation.h>
@class Noodles;
typedef NS_ENUM(NSUInteger, NoodlesType) {
    BeijingSauceNoodle,
    ShanxiSlicedNoodle,
    LanzhouPulledNoodle
};

@protocol NoodleFactoryInterface <NSObject>
@required
//定义了一个工厂方法,该方法需要由子类来实现
- (Noodles *)createNoodles:(NoodlesType)type;
@end

@interface NoodlesFactory : NSObject<NoodleFactoryInterface>

- (Noodles *)orderNoodles:(NoodlesType)type;
@end

NoodlesFactory.m文件

#import "NoodlesFactory.h"
#import "Noodles.h"

@interface NoodlesFactory ()
@property (nonatomic, strong) Noodles *noodles;
@end

@implementation NoodlesFactory

- (Noodles *)orderNoodles:(NoodlesType)type {
    self.noodles = [self createNoodles:type];
    //得到对象实例后,可以noodles对象再做一些其他的操作,而不用关心具体是什么面条
    return self.noodles;
}

@end

BJNoodlesFactory.m文件

#import "BJNoodlesFactory.h"
#import "BeijingSauceNoodles.h"
#import "ShanXiSlicedNoodles.h"
#import "LanzhouHandPulledNoodles.h"
#import "BJNoodlesIngredientFactory.h"
#import "NoodlesIngredientFactory.h"

@interface BJNoodlesFactory ()
@property (nonatomic, strong) NoodlesIngredientFactory *ingredientFactory;
@end
@implementation BJNoodlesFactory
- (instancetype)initNoodlesFactoryWithIngredientFactroy:(NoodlesIngredientFactory *)noodlesIngredentFactory {
    if (self = [super init]) {
        self.ingredientFactory = [[BJNoodlesIngredientFactory alloc] init];
    }
    return self;
}
//可以看出的是BJNoodlesFactory所用的原料都是BJNoodlesIngredientFactory类提供的
- (Noodles *)createNoodles:(NoodlesType)type {
    switch (type) {
        case BeijingSauceNoodle:
            return [BeijingSauceNoodles beijingSauceNoodlesWithIngredientFactory:self.ingredientFactory];
        case ShanxiSlicedNoodle:
            return [ShanXiSlicedNoodles shanxiSlicedNoodlesWithIngredientFactory:self.ingredientFactory];
        case LanzhouPulledNoodle:
            return [LanzhouHandPulledNoodles LanzhouHandPulledNoodlesWithIngredientFactory:self.ingredientFactory];
        default:
            return nil;
            break;
    }
}
@end

小明餐厅要订购北京的杂酱面,那么他怎么做呢?
XiaoMingRestaurant.m文件

#import "XiaoMingRestaurant.h"
#import "Noodles.h"
#import "NoodlesFactory.h"
#import "BJNoodlesFactory.h"

@interface XiaoMingRestaurant ()
@property (nonatomic, strong) Noodles *noodles;
@end
@implementation XiaoMingRestaurant

- (instancetype)init {
    if (self = [super init]) {
        NoodlesFactory *factory = [[BJNoodlesFactory alloc] init];
        self.noodles = [factory orderNoodles:BeijingSauceNoodle];
    }
    return  self;
}
@end

可以看出在小明餐厅这个类中,并没有依赖具体的面条类型。这就减少了依赖,从而降低了耦合。
在这个例子中,涉及到了工厂方法模式,也包含了抽象工厂方法模式,其中抽象工厂方法模式是建立原料工厂的那个相关类中。

5、工厂模式总结

所有的工厂都是用来封装对象的创建的。简单工厂严格意义上讲不是一种设计模式,更像是一种编程习惯,但是这样做也是有很多好处的。可以对代码进行封装,将对象的创建和使用分离开来。工厂方法使用继承,把对象的创建委托给子类,由子类来实现工厂方法创建对象。抽象工厂使用对象组合,一组相关对象的创建由抽象工厂所暴露的接口来提供。所有的工厂模式都是通过减少使用者和具体类之间的依赖来降低耦合。工厂模式给我们提供了一种编程思维模式:那就是我们要针对抽象接口编程,而不是针对具体类进行编程,这样就可以提高代码的弹性!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值