美文网首页iOS开发笔记iOS开发架构知识
工厂模式三部曲-简单工厂模式

工厂模式三部曲-简单工厂模式

作者: 刘小壮 | 来源:发表于2016-04-04 19:16 被阅读7512次
    该文章属于<简书 — 刘小壮>原创,转载请注明:

    <简书 — 刘小壮> http://www.jianshu.com/p/a523144d8d7a


    之前写过一篇关于简单工厂模式的博客,后来再看感觉之前写的不太好,而且不够详细。这两天正好有时间,打算把之前简单工厂模式的文章重写,这次要写关于工厂模式的一系列文章,而不只是一篇文章。

    这系列文章将会从浅入深,讲述三种工厂模式的设计,分别是:简单工厂模式、工厂方法模式、抽象工厂模式。由于反射机制可以简化工厂模式,所以这系列文章将会给出没有使用反射机制,和使用了反射机制的两种实现代码。

    本人理解可能不够深刻,这一系列文章中存在的问题,欢迎大家提出,谢谢!😊


    博客配图

    什么是简单工厂模式?

    简单工厂模式中定义一个抽象类,抽象类中声明公共的特征及属性,抽象子类继承自抽象类,去实现具体的操作。工厂类根据外界需求,在工厂类中创建对应的抽象子类实例并传给外界,而对象的创建是由外界决定的。外界只需要知道抽象子类对应的参数即可,而不需要知道抽象子类的创建过程,在外界使用时甚至不用引入抽象子类

    简单工厂模式将抽象子类的创建,和关于抽象子类相关的业务逻辑分离,降低对象间的耦合度。由于工厂类只是为外界创建对象,所以并不需要实例化工厂类对象,只需要为外界提供类方法即可。外界需要什么类型的抽象子类,只需要传递对应的参数即可。外界不需要知道具体的抽象子类,只需要使用抽象类即可。

    简单工厂模式主要包含三部分:
    • 工厂类:根据外界的需求,决定创建并返回哪个具体的抽象子类。
    • 抽象类:定义抽象子类所需的属性和方法,子类通过继承自抽象类获得这些方法。
    • 抽象子类:继承自抽象类,是具体操作的实现者,根据需求重写父类继承过来的方法。

    业务场景

    简单工厂模式主要适用于抽象子类的业务逻辑相同,但具体实现不同的情况。不同的操作子类执行同样的方法,最后的结果却是不同的,这也是多态的一种表现方式。

    这里用一个简单的加减乘除的基础运算例子当作需求,下面的UML类图和代码都会依据这个场景来实现。假设现在需要实现一个简单的加减乘除运算,这些运算具体操作都是类似的,都有两个被操作的值和一个运算方法,只是运算符不同,这种情况就适合用简单工厂模式。

    UML类图

    根据上面提出的业务场景来画一张类图,由于在Mac中没找到比较好的画类图的工具,所以简单的画了一下,主要体现具体结构。

    简单工厂模式

    从上面图中我们可以看出,图中定义了一个运算抽象类,所有的运算抽象子类继承自这个运算抽象类。运算抽象类有两个参与运算的属性,通过调用getResult方法来获取这两个值最后运算的结果,调用方式都一样,只是最后的结果不同。抽象类并不参与运算,运算的结果通过运算抽象子类重载getResult方法去实现。

    上图中还定义了一个简单工厂类,这个简单工厂类就是用于实现运算抽象子类实例化的逻辑,通过外界传进来的type参数,并将实例完成的运算操作类返回。

    普通方式代码实现

    首先定义抽象类,抽象类中将会包含参与运算的抽象子类的属性和行为(方法)。
    @interface Operation : NSObject
    @property (nonatomic, assign) CGFloat numberOne;
    @property (nonatomic, assign) CGFloat numberTwo;
    - (CGFloat)getResult;
    @end
    
    @implementation Operation
    - (CGFloat)getResult {
        return 0;
    }
    @end
    
    定义抽象类之后,需要创建负责具体运算的抽象子类,也就是操作类,简单的定义了一下,代码不太多就全贴出来了。
    @interface OperationAdd : Operation
    @end
    
    @implementation OperationAdd
    - (CGFloat)getResult {
        return self.numberOne + self.numberTwo;
    }
    @end
    
    @interface OperationSub : Operation
    @end
    
    @implementation OperationSub
    - (CGFloat)getResult {
        return self.numberOne - self.numberTwo;
    }
    @end
    
    @interface OperationMul : Operation
    @end
    
    @implementation OperationMul
    - (CGFloat)getResult {
        return self.numberOne * self.numberTwo;
    }
    @end
    
    @interface OperationDiv : Operation
    @end
    
    @implementation OperationDiv
    - (CGFloat)getResult {
        if (self.numberTwo == 0) {
            NSLog(@"除数不能为零");
            return 0;
        } else {
            return self.numberOne / self.numberTwo;
        }
    }
    @end
    
    下面先定义了四个静态变量,这四个静态变量声明了创建对象的类型,在后面反射部分代码中也会用到这四个静态变量。
    static NSString *kOperationAdd = @"OperationAdd";
    static NSString *kOperationSub = @"OperationSub";
    static NSString *kOperationMul = @"OperationMul";
    static NSString *kOperationDiv = @"OperationDiv";
    
    现在具体参与运算的类都已经定义完成,就需要定义工厂类了。工厂类的职责就是根据外界需要,创建对应的抽象子类实例并返回给外界。
    @interface OperationFactory : NSObject
    + (Operation *)CreateOperationWithType:(NSString *)type;
    @end
    
    @implementation OperationFactory
    + (Operation *)CreateOperationWithType:(NSString *)type {
        if ([kOperationAdd isEqualToString:type]) {
            return [OperationAdd new];
        } else if ([kOperationSub isEqualToString:type]) {
            return [OperationSub new];
        } else if ([kOperationMul isEqualToString:type]) {
            return [OperationMul new];
        } else if ([kOperationDiv isEqualToString:type]) {
            return [OperationDiv new];
        }
        return nil;
    }
    @end
    
    上面我们就将工厂设计模式的定义都完成了,现在需要的就是外界直接拿来使用。上面工厂类直接定义的类方法,因为外界获取某个具体的抽象子类时,并没有必要将工厂类实例化,工厂类只是完成一个实例化操作。
    - (void)viewDidLoad {
        Operation *oper = [OperationFactory CreateOperationWithType:kOperationAdd];
        oper.numberOne = 13;
        oper.numberTwo = 24;
        NSLog(@"result : %f", [oper getResult]);
    }
    

    到目前为止简单工厂模式的代码就写完了,可以看到外界想进行什么类型的运算,只需要将传入的运算类型参数改一下即可,工厂类就会实例化不同的抽象子类进行运算。但是这种工厂类的设计,有一个很大的问题,就在于每次增加或删除某个算法时,都需要对工厂类进行修改,这是不符合开放封闭原则的。对于这个问题,我们后面会通过反射机制来进行处理。

    工厂模式也是对面向对象编程三大特性之一的多态的一个很好的表述,下面先简单的介绍一下多态的特性。

    简单介绍一下多态

    面向对象编程三大特性之一就有多态,多态是指在程序运行时,相同的消息可能会发给继承自同一个父类的不同子类型的对象,虽然是同一个方法,但是运行时系统会根据当前对象所属的子类型作出不同的响应。

    面向对象三大特性中,继承和封装都是为了代码重用,继承可以继承自父类的特征和属性,封装可以将实现细节封装,外界调用实现某些功能。而多态则是为了接口重用

    多个子类继承同一个父类,就会具有和父类相同的行为和特征,子类可以对父类的方法进行重写,所以可能同一个方法每个子类的实现都不同。通过父类指针指向任意子类对象并调用相同方法,可能会得到不同的结果。

    简单的说就是系统允许将当前类的指针,指向任何继承自当前类的子类,并且不会报错。由于子类继承自父类,所以和父类有相同的特征(方法)。当前类的指针向指向的子类对象发送消息,系统会根据具体的子类对父类方法的实现,作出不同的响应

    例如下面这行代码:

    Operation *obj = [OperationFactory CreateOperationWithType:kOperationAdd];
    

    在上面这个例子中,OperationFactory工厂类将会返回Operation的子类实例,Operation的子类分别继承自同一父类,并且对其getResult方法进行了重写。Operation实例化的obj指针可能指向任何Operation的子类,并对其发送getResult消息。最终的结果会根据obj指针指向的子类有不同的结果,这就是多态

    我对多态的了解非常浅薄,有不对之处还请多多指出,这里只是顺带提了一下😊。

    配合反射机制优化代码

    我之前写过一篇文章,详细讲了一下反射机制,所以这里不就对反射机制详细介绍了。

    在上面的代码中,我们会发现工厂类创建抽象子类的代码都是相同的,只是创建的具体对象不同,而且如果抽象子类很多的话,会有过多的条件语句。编程中这种重复代码我们都要将其简化,不然写出的代码就像垃圾代码一样,这也是新手和老手的区别之一。

    在这里我们可以利用反射机制来简化代码,根据外面需要的操作子类的类型,反射出具体的类。在上面我们已经定义了一些NSString类型的静态变量,这些静态变量的值就是反射需要的字符串,外界只需要使用这些静态变量即可,不用自己手打字符串,也防止了错误的发生。修改之后外界不需要发生任何变化,只需要知道这些静态变量即可,只对工厂类进行修改。

    只需将OperationFactory的创建方法改一下实现,其他地方不受影响。

    + (Operation *)CreateOperationWithType:(NSString *)type {
        return [NSClassFromString(type) new];
    }
    

    改完之后的代码非常符合面向对象编程的开放封闭原则,即当外界需求发生变化时,只对现有代码进行扩展,不对其原有代码进行修改的原则。

    现在假设再增加一个其他运算功能,只需要再创建一个继承自抽象类的抽象子类,在抽象子类中重写getResult方法来实现运算,并且在上面定义的静态变量中加入一个对应的变量。其他地方都不会受到影响,这就是一个比较好的面向对象的设计。

    到此为止,我们简单工厂模式就讲完了,后续还有两篇文章继续讲工厂方法模式和抽象工厂模式的文章,文章中不足之处,希望大家多多提出,谢谢!😊


    前段时间写了关于工厂模式的系列文章,这系列文章理解起来比较难懂。应广大读者的需要,这段时间专门给这系列文章补了Demo

    Demo只是来辅助读者更好的理解文章中的内容,应该博客结合Demo一起学习,只看Demo还是不能理解更深层的原理Demo中代码都会有注释,各位可以打断点跟着Demo执行流程走一遍,看看各个阶段变量的值。

    Demo地址刘小壮的Github

    相关文章

      网友评论

      • 绘色:问一下,如果是cell控件怎么用简单工厂模式,这样都分别在抽象子类实现的话,那样会有很多的重复代码呀 ?
      • 草原野马:清晰。
      • 小蜗牛吱呀之悠悠:谢谢楼主,学习到了~
        刘小壮:@一只努力学习的小蜗牛 :blush:
      • wingching:说得比较简单明了,谢谢作者的分享!mark
        刘小壮:@wingching :blush:
      • 鱼鱼鱼四只鱼:其实配合反射机制是灵活了,但是,也增加了一个隐形的约束,就是type需要跟类保持一致
        刘小壮:@geohot 是的,尽量把type的定义集中在一起,这样可以降低影响。
      • JimTian1988:大神你好 对于你说的那四种类型加减乘除可否用枚举类型来表达更好?
        JimTian1988:@刘小壮 嗯 谢谢回复
        刘小壮:@BossKing10086 其实都是一个意思,都是为了保证让外界使用更方便。如果从性能上来说,用枚举的Switch比静态变量的if else好一些,这里使用静态变量主要是为了配合反射机制,项目中可以根据业务需求自行定制。 :blush:

      本文标题:工厂模式三部曲-简单工厂模式

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