美文网首页
iOS 设计模式之建造者模式

iOS 设计模式之建造者模式

作者: 点滴86 | 来源:发表于2024-04-27 22:48 被阅读0次

    建造者模式

    Builder模式,中文翻译为建造者模式或者构建者模式,也叫做生成器模式。

    为什么需要建造者模式?

    需要定义一个资源池配置类DMResourcePoolConfig。这里的资源池,可以理解为线程池、连接池、对象池等。在这个资源池配置类中,有以下几个成员变量,也就是可配置项。

    builder01.png

    因为maxTotal、maxIdle、minIdle不是必填变量,所以在创建DMResourcePoolConfig对象的时候,通过init方法,给这几个参数传递nil,来表示使用默认值。

    @interface DMResourcePoolConfig : NSObject
    
    @property (nonatomic, strong, readonly) NSString *name;
    
    @property (nonatomic, assign, readonly) NSInteger maxTotal;
    
    @property (nonatomic, assign, readonly) NSInteger maxIdle;
    
    @property (nonatomic, assign, readonly) NSInteger minIdle;
    
    @end
    
    @interface DMResourcePoolConfig ()
    
    @property (nonatomic, strong) NSString *name;
    
    @property (nonatomic, assign) NSInteger maxTotal;
    
    @property (nonatomic, assign) NSInteger maxIdle;
    
    @property (nonatomic, assign) NSInteger minIdle;
    
    @end
    
    @implementation DMResourcePoolConfig
    
    - (instancetype)initWithName:(NSString *)name maxTotal:(NSNumber *)maxTotal maxIdle:(NSNumber *)maxIdle minIdle:(NSNumber *)minIdle
    {
        if (self = [super init]) {
            if (name.length == 0) {
                @throw [NSException exceptionWithName:@"DMResourcePoolConfigException" reason:@"name 不能为空" userInfo:nil];
            }
            self.name = name;
            if (maxTotal) {
                if ([maxTotal integerValue] < 0) {
                    @throw [NSException exceptionWithName:@"DMResourcePoolConfigException" reason:@"maxTotal 不能为负数" userInfo:nil];
                } else {
                    self.maxTotal = [maxTotal integerValue];
                }
            } else {
                self.maxTotal = 8;
            }
            if (maxIdle) {
                if ([maxIdle integerValue] < 0) {
                    @throw [NSException exceptionWithName:@"DMResourcePoolConfigException" reason:@"maxIdle 不能为负数" userInfo:nil];
                } else {
                    self.maxIdle = [maxIdle integerValue];
                }
            } else {
                self.maxIdle = 8;
            }
            if (minIdle) {
                if ([minIdle integerValue] < 0) {
                    @throw [NSException exceptionWithName:@"DMResourcePoolConfigException" reason:@"minIdle 不能为负数" userInfo:nil];
                } else {
                    self.minIdle = [minIdle integerValue];
                }
            } else {
                self.minIdle = 0;
            }
        }
        
        return self;
    }
    
    @end
    

    现在DMResourcePoolConfig 只有4个可配置项,对应的init函数中,也只有4个参数,参数的个数不多。但是如果可配置项逐渐增多,变成了8个、10个,甚至更多,继续沿用现在的设计思路,init函数的参数列表会变得很长,代码在可读性和易用性上都会变差。解决这个问题的办法就是用setter方法来成成员变量赋值。配置项name是必填的,把它放到init函数中设置,其它配置项都不是必填的,用setter方法设置。

    @interface DMResourcePoolConfig : NSObject
    
    @property (nonatomic, strong, readonly) NSString *name;
    
    @property (nonatomic, assign) NSInteger maxTotal;
    
    @property (nonatomic, assign) NSInteger maxIdle;
    
    @property (nonatomic, assign) NSInteger minIdle;
    
    @end
    
    @interface DMResourcePoolConfig ()
    
    @property (nonatomic, strong) NSString *name;
    
    @end
    
    @implementation DMResourcePoolConfig
    
    - (instancetype)initWithName:(NSString *)name
    {
        if (self = [super init]) {
            if (name.length == 0) {
                @throw [NSException exceptionWithName:@"DMResourcePoolConfigException" reason:@"name 不能为空" userInfo:nil];
            }
            self.name = name;
        }
        return self;
    }
    
    - (void)setMaxTotal:(NSInteger)maxTotal
    {
        if (maxTotal < 0) {
            @throw [NSException exceptionWithName:@"DMResourcePoolConfigException" reason:@"maxTotal 不能为负数" userInfo:nil];
        } else {
            _maxTotal = maxTotal;
        }
    }
    
    - (void)setMaxIdle:(NSInteger)maxIdle
    {
        if (maxIdle < 0) {
            @throw [NSException exceptionWithName:@"DMResourcePoolConfigException" reason:@"maxIdle 不能为负数" userInfo:nil];
        } else {
            _maxIdle = maxIdle;
        }
    }
    
    - (void)setMinIdle:(NSInteger)minIdle
    {
        if (minIdle < 0) {
            @throw [NSException exceptionWithName:@"DMResourcePoolConfigException" reason:@"minIdle 不能为负数" userInfo:nil];
        } else {
            _minIdle = minIdle;
        }
    }
    
    @end
    
    

    通过init方法设置必填项,通过setter方法设置可选配置项。如果还要解决下面三个问题,现在的设计思路就不满足了。

    • name是必填项,如果必填的配置项有很多,把这些必填配置项都放到init方法中设置,那init方法又会出现参数列表很长的问题。如果把必填项也通过setter方法设置,那校验这些必填项是否已经填写的逻辑就无处安放了。
    • 假设配置项之间有一定的依赖关系。比如,maxIdle和minIdle要小于等于maxTotal。继续使用现在的设计思路,这些配置项之间的依赖关系的校验逻辑就无处安放了。
    • 如果希望DMResourcePoolConfig类对象是不可变对象,也就是,对象在创建好之后,就不能修改内部属性值。要实现这个功能,就不能在DMResourcePoolConfig类中暴露setter方法。
      为了解决这些问题,建造者模式就派上用场了。可以把校验逻辑放置到Builder类中,先创建建造者,并通过setter方法设置建造者的变量值,然后使用build方法真正创建对象之前,做集中的校验,校验通过之后才会创建对象。
    @interface DMResourcePoolConfigBuilder : NSObject
    
    @property (nonatomic, strong) NSString *name;
    
    @property (nonatomic, assign) NSInteger maxTotal;
    
    @property (nonatomic, assign) NSInteger maxIdle;
    
    @property (nonatomic, assign) NSInteger minIdle;
    
    - (DMResourcePoolConfig *)build;
    
    @end
    
    @implementation DMResourcePoolConfigBuilder
    
    - (instancetype)init
    {
        if (self = [super init]) {
            self.maxTotal = 8;
            self.maxIdle = 8;
            self.minIdle = 0;
        }
        
        return self;
    }
    
    - (void)setName:(NSString *)name
    {
        if (name == nil || name.length == 0) {
            @throw [NSException exceptionWithName:@"DMResourcePoolConfigBuilderException" reason:@"name 不能为空" userInfo:nil];
        }
        _name = name;
    }
    
    - (void)setMaxTotal:(NSInteger)maxTotal
    {
        if (maxTotal < 0) {
            @throw [NSException exceptionWithName:@"DMResourcePoolConfigBuilderException" reason:@"maxTotal 不能为负数" userInfo:nil];
        } else {
            _maxTotal = maxTotal;
        }
    }
    
    - (void)setMaxIdle:(NSInteger)maxIdle
    {
        if (maxIdle < 0) {
            @throw [NSException exceptionWithName:@"DMResourcePoolConfigBuilderException" reason:@"maxIdle 不能为负数" userInfo:nil];
        } else {
            _maxIdle = maxIdle;
        }
    }
    
    - (void)setMinIdle:(NSInteger)minIdle
    {
        if (minIdle < 0) {
            @throw [NSException exceptionWithName:@"DMResourcePoolConfigBuilderException" reason:@"minIdle 不能为负数" userInfo:nil];
        } else {
            _minIdle = minIdle;
        }
    }
    
    - (DMResourcePoolConfig *)build
    {
        // 校验逻辑放到这里来做,包括必填项校验、依赖关系校验、约束条件校验等
        if (self.name == nil || self.name.length <= 0) {
            // name是必填项
            @throw [NSException exceptionWithName:@"DMResourcePoolConfigBuilderException" reason:@"name 不能为空" userInfo:nil];
        }
        
        if (self.maxIdle > self.maxTotal) {
            @throw [NSException exceptionWithName:@"DMResourcePoolConfigBuilderException" reason:@"maxIdle 小于等于maxTotal" userInfo:nil];
        }
        
        if (self.minIdle > self.maxTotal || self.minIdle > self.maxIdle) {
            @throw [NSException exceptionWithName:@"DMResourcePoolConfigBuilderException" reason:@"minIdle 小于等于maxTotal、maxIdle" userInfo:nil];
        }
        
        return [[DMResourcePoolConfig alloc] initWithBuilder:self];
    }
    
    @end
    
    @interface DMResourcePoolConfig : NSObject
    
    @property (nonatomic, strong, readonly) NSString *name;
    
    @property (nonatomic, assign, readonly) NSInteger maxTotal;
    
    @property (nonatomic, assign, readonly) NSInteger maxIdle;
    
    @property (nonatomic, assign, readonly) NSInteger minIdle;
    
    // 建造者模式
    - (instancetype)initWithBuilder:(DMResourcePoolConfigBuilder *)builder;
    
    @end
    
    @interface DMResourcePoolConfig ()
    
    @property (nonatomic, strong) NSString *name;
    
    @property (nonatomic, assign) NSInteger maxTotal;
    
    @property (nonatomic, assign) NSInteger maxIdle;
    
    @property (nonatomic, assign) NSInteger minIdle;
    
    @end
    
    @implementation DMResourcePoolConfig
    
    - (instancetype)initWithBuilder:(DMResourcePoolConfigBuilder *)builder
    {
        if (self = [super init]) {
            self.name = builder.name;
            self.maxTotal = builder.maxTotal;
            self.maxIdle = builder.maxIdle;
            self.minIdle = builder.minIdle;
        }
        
        return self;
    }
    
    @end
    
    

    使用代码如下

    DMResourcePoolConfigBuilder *builder = [[DMResourcePoolConfigBuilder alloc] init];
    builder.name = @"dbConnectionPool";
    builder.maxTotal = 16;
    builder.maxIdle = 10;
    builder.minIdle = 8;
    DMResourcePoolConfig *config = [builder build];
    

    使用建造者模式创建对象,还能避免对象存在无效状态。使用建造者模式来构建对象,代码实际上是有点重复的,DMResourcePoolConfig类中的成员变量,要在Builder类中重新定义一遍。

    与工厂模式有何区别?

    建造者模式是让建造者类来负责对象的创建工作。工厂模式是由工厂类来负责对象创建的工作。工厂模式是用来创建不同但是相关类型的对象(继承同一父类或者实现同一协议的一组子类),由给定的参数来决定创建那种类型的对象。建造者模式是用来创建一种类型的复杂对象,通过设置不同的可选参数,“定制化”地创建不同的实例对象。

    相关文章

      网友评论

          本文标题:iOS 设计模式之建造者模式

          本文链接:https://www.haomeiwen.com/subject/crtzxjtx.html