• iOS设计模式之类族(class cluster)


    类族模式在UIKit(user interface framework)使用的范围已经远远超过我们的想象,比如,UIButton,NSArray,NSString,NSNumber等,

    例如NSNumber类

    做iOS开发的朋友们一定用过NSNumber的numberWith…方法。但大家有可能都不知道NSNumber这样的方法调用返回的不是NSNumber类本身的对象,这正是Objective-C类族的微妙之处。

    cluster3.gif

    如上图所示,Number的概念很大。而实际上NSNumber实际上是有很多隐藏的子类的,而我们通过NSNumber的numberWith…方法得到的对象正是其子类的对象,但对于使用者几乎可以不必知道这一点,只要知道它是一个NSNumber对象就OK了。

    “Simple Concept and Simple Interface”,这正是苹果设计类族的初衷,也是类族的优点所在。可以想象我们要用整数作为参数拿到一个NSNumber对象和一个布尔值参数拿到的NSNumber对象是不同的,这略微有些类似于switch逻辑(虽然是通过不同的方法),根据不同的条件提供不同的子类对象,而这一切都集中声明在公共接口类NSNumber中。通过[NSNumber numberWithInt:2]和[NSNumber numberWithBool:YES]得到的对象对应的类,一个是__NSCFNumber,另一个是__NSCFBoolean。

    例如创建按钮时调用的

    + (instancetype)buttonWithType:(UIButtonType)buttonType;

    这个方法创建的对象的类型取决于传入的按钮类型buttonType。返回的对象都继承于同一个基类UIButton
    当各个子类在外部表现相同,内部实现不同时,我们就可以使用类簇模式去实现它。这样做我们无需使用对应的子类,只需要使用基类即可。

    首先我们先来看这两个函数:

    - (BOOL)isKindOfClass:(Class)aClass;
    - (BOOL)isMemberOfClass:(Class)aClass;

    第一个函数的意思是:接收者是否是aClass类的实例或者从这个类继承的任何类的实例。如果是返回yes。
    第二个函数的意思是:接收者是否是aClass的实例,如果是返回yes。
    按照上面的解释我们执行下面两行代码输出结果应该是:isKindOfClass
    ,isMemberOfClass

     NSArray*array = [NSArray new];
     if ([array isKindOfClass:[NSArray class]]) {
        NSLog(@"isKindOfClass");
    }
    if ([array isMemberOfClass:[NSArray class]]) {
        NSLog(@"isMemberOfClass");
    }

    但是上面代码执行的输出结果却是:isKindOfClass。
    为什么会出现这种情况?isMemberOfClass为什么没有执行?array明明是NSArray的一个实例呀!难道不是吗?为了验证我们的怀疑我们在执行下面一段代码看看array到底是什么

    NSLog(@"%@",[[array class] debugDescription]);
    NSLog(@"%@", [[[NSArray arrayWithObject:@"a,b"] class] debugDescription]);

    输出:NSArray0,NSArrayI
    好吧,array果然不是NSArray的一个实例,但是上面输出了“isKindOfClass,这说明array是NSArray子类的一个实例,到底这种推断正不正确?我们再来验证一下:

    NSLog(@"%@",[[array superclass] debugDescription]);

    输出:NSArray
    好吧,果然是这样,写到这儿我们已经大致明白了,通过不同方法实例化的array都是NSArray子类的实例,NSArray是一个抽象的基类。这种模式就是类族模式。

    但是如果把NSArray换成UIButton又回出现截然相反的情况,出现这种情况的原因在于NSArray基类又对子类进行了重构,生成了多个子类。

    举个例子,我们要写个刷新控件,实现下拉刷新,上拉加载功能。
    定义抽象基类

    typedef enum {
        RefreshStateBeginRefreshing,//开始刷新
        RefreshStateEndRefreshing//结束刷新
    
    } RefreshState;
    
    /**
     控件类型
     */
    typedef enum {
        RefreshHeaderType,
        RefreshFooterType
    } RefreshViewType;
    
    @interface XZHRefreshView : UIView
    
    - (void)addRefreshTarget:(id)target action:(SEL)action forRefreshState:(RefreshState)state;
    
    + (XZHRefreshView *)refreshViewWithType:(RefreshViewType)type;
    
    @end
    
    
    @implementation XZHRefreshView
    
    + (XZHRefreshView *)refreshViewWithType:(RefreshViewType)type {
        if (type == RefreshHeaderType) {
            return [XZHRefreshHeaderView header];
        } else if (type == RefreshFooterType) {
            return [XZHRefreshFooterView footer];
        } else {
            return nil;
        }
    }
    - (void)addRefreshTarget:(id)target action:(SEL)action forRefreshState:(RefreshState)state {
        //子类实现
    }
    @end

    每个实体子类,都是继承于基类:

    @interface XZHRefreshHeaderView : XZHRefreshView
    
    @end
    
    @implementation XZHRefreshView
    
    - (void)addRefreshTarget:(id)target action:(SEL)action forRefreshState:(RefreshState)state {
        //code
    }
    @end

    举个例子我们自己写一个类族,定义一个继承与NSObject的抽象基类LULEmployee,基类中定义一个枚举类型

    LULEmployee.h 代码

    #import <Foundation/Foundation.h>

    typedef NS_ENUM(NSUInteger,LULEmployeeType) {

        WJUEmployeeTypeDevlopers,

        WJUEmployeeTypeProducters,

    };

    @interface WJUEmployee : NSObject

    +(WJUEmployee*)employeeWithType:  (WJUEmployeeType)type;

    -(void)doADayWork;

    @end

    WJUEmployee.m代码

    #import "WJUEmployee.h"

    #import "WJUEmployeeTypeDevloper.h"

    #import "WJUEmployeeTypeProducter.h"

    @implementation WJUEmployee

    +(WJUEmployee*)employeeWithType:  (WJUEmployeeType)type{

        switch (type) {

            case 0:

                return [WJUEmployeeTypeDevloper new];

                break;

            case 1:

                return [WJUEmployeeTypeProducter new];

                break;

                

            default:

                break;

        }

    }

    -(void)doADayWork{

        

    }

    @end

    WJUEmployeeTypeDevloper.h代码

    #import "WJUEmployee.h"

     @interface WJUEmployeeTypeDevloper : WJUEmployee

    @end

    WJUEmployeeTypeDevloper.m代码

    #import "WJUEmployeeTypeDevloper.h"

    @implementation WJUEmployeeTypeDevloper

    -(void)doADayWork{

        [super doADayWork];

        NSLog(@"%@",[[self class] description]);

    }

    @end

    同理WJUEmployeeTypeProducter一样 

    然后创建3个继承与基类的子类,并在子类中覆写-(void)doADayWork方法;

    类族模式最大的好处:

    • 可以隐藏抽象基类背后的复杂细节,
    • 程序员不需要记住各种类型的创建对象所需要的具体类,简化开发人员开发成本,提高开发效率.
    • 使用者只需调用基类简单的方法就可以返回不同的子类实例。

    类族缺点

    • 类族的一个缺点也显现出来,那就是已有的类族不好扩展(比如你想NSNumber再多支持一种情况,这个恐怕很难。好在这些系统的类库已经将大部分可能都做进去了,考虑得比较完善,通常你只是去用就可以了。)

    注意事项

    1.所有子类应该继承类簇的抽象基类
    2.子类应该覆写父类需要覆写的方法。
    3.使用类簇时要完善开发文档,避免团队成员错误使用。



  • 相关阅读:
    在阿里云服务器(ECS)上从零开始搭建nginx服务器
    HTML5和CSS3新特性一览
    【react】---手动封装一个简易版的redux
    【react】---17新增的生命周期
    vue单页面应用刷新网页后vuex的state数据丢失的解决方案
    [VUE]object.defineProperty的基本使用
    JavaScript / 本地存储
    转载--httpclient原理和应用
    关于mybatis mapper.xml中的if判断
    idea maven install时,打包找不到微服务common中公用的包
  • 原文地址:https://www.cnblogs.com/junhuawang/p/5695791.html
Copyright © 2020-2023  润新知