设计模式之开放-封闭原则(引申出Objective-C中继承、Category、Protocol三者的区别,这点面试常问)

  1. 开放封闭原则(OCP原则The Open-Closed Principle)是面向对象的核心设计所在。它是说,软件开发实体(类、模块、函数等)应该可以扩展,但是不能修改。

这个原则有两个特征,一个是说“对于扩展是开放的”,另一个是说“对于更改是封闭的”。

我们在编写任何app时,不要寄希望于需求是固定的、不变化的,这是不现实也是不科学的想法,既然需求一定会有变化,那么如何应对这种变化?也就是说。我们所设计的代码可以相对容易的修改,不至于新的需求一来,就将整个程序推倒重写?怎么样的设计才能面对需求的改变却可以保持相对稳定,从而使得app可以在第一个版本后不断推出新的版本呢?开放-封闭原则可以给我们答案。

答案:代码要容易维护又不容易出问题的最好办法是,就是多扩展,少修改。

就比如说,你作为一个公司的老板,规定九点上班,不允许迟到,但是有几个公司骨干,老是迟到,你怎么办?

仔细想想,其实迟到不是主要问题,每天保证8小时工作量,是老板最需要的。甚至说,这个也不是问题,按时完成业绩目标,才是最重要的指标。于是应该改变管理方式,弹性上班,早到早走,晚到晚走等。这其实就是对工作时间或者业绩成绩修改的封闭,对时间制度扩展的开放。

开放-封闭原则的意思是,在设计的时候,要时刻考虑,尽量让这个类足够好,写好了就不要去修改了,如果新的需求来了,我们增加一些类就完事了,原来的代码能不动就不动。绝对对修改关闭是不可能的,无论模块是多么的封闭,多会存在一些无法对之封闭的变化。既然不可能完全封闭,设计人员必须对他设计的模块应该对哪种变化进行封装做出选择。他必须先猜测出最有可能发生的变化种类,然后构造抽象来隔离那些变化。

在我们最初编写代码时,先假设变化不会发生,当变化发生时,我们就创建抽象来隔离以后发生的同类变化。保证在面对需求时,对新需求的改变是通过增加新代码进行的,而不是更改现有代码。(要做到这一步,强烈推荐《大话设计模式》这本书,我所有关于设计模式的博客,都是从这本书中学习而来,只是这本书籍代码是C#实现的,而我学习的时候,是带入了个人对OC以及iOS开发的理解)

开发人员应该仅对程序中呈现出频繁变化的那些部分做出抽象,然而,对应用程序中每个部分都刻意的进行抽象同样不是好主意,拒绝不成熟的抽象和抽象本身一样重要。

 

2. Objective-C中继承、Category、Protocol三者的区别

众所周知,OC是单继承,新出的Swift也是单继承。那么在iOS开发中,我们怎么实现类似多继承的关系?

这是一道面试\笔试题,基本应聘时,如果面试官是从事iOS开发的,很有可能会问到这道题。事实上,我们可以利用OC中的Category和Protocol来达到多继承的效果。如果仅仅只是想要回答这个题目,这样说足够了。

可是,有很多面试官是不会满足于这样一个回答的,会追问你,那么Category和Protocol有什么区别呢?即使没有这样的面试题,我想基于自身驱动,这也是必然要了解,学习的。

 

利用继承、多态可以很好的保持"对扩展开放,对更改封闭"(即上文提到的开放-封闭原则OCP),这也是面向对象语言保持开放-封闭原则最常见的办法。OC中还有另外两种语法来支持OCP:即Category和Protocol。

Protocol只是声明一套接口,并不能提供具体实现,变相的也算是一种抽象基类的实现方式(OC本身语法并不支持抽象基类)。

Category可以为已有的类提供额外的接口和具体的实现。

Protocol只能提供一套公用的接口声明,并不能提供具体实现,它的行为是,我只负责声明,而不管谁去实现,去如何实现。这样的话,我定义一套接口,可以使任意的类都用不同的方式去实现接口中的方法,就是为遵守了protocol的类提供了一些额外访问这个类的一些接口,像delegate和dataSource用protocol实现是最好的。

Category是对一个功能完备的类的一种补充、扩展,就像一个东西基本功能都完成了,可以用category为这个类添加不同的组件,使得这个类能够适应不同情况的需求(但是这些不同需求最核心的需求要一致)。当然,当某个类非常大的时候,使用category可以按照不同的功能将类的实现分在不同的模块中。还有,虽然category可以访问已有类的实例变量,但不能创建新的实例变量,如果要创建新的实例变量,请使用继承。

继承,它基于Protocol和Category之间,既可以像protocol一样只提供纯粹的接口,也可以像Category一样提供接口的完整实现,可以自由定义类的实例变量(这一点,Protocol倒是可以声明实例变量,但是也仅仅是声明而已),而且继承还可以对类以后的方法进行改写,所以继承的力量是最强大的。

在iOS开发中,继承是完全可以完成protocol和category的功能的,那么在开发过程中多多使用继承体系可好?

需要注意的是使用继承还有很大的代价问题。使用继承来进行扩展是一种耦合度很高的行为,对父类可以说是完全依赖,如果继承体系太过复杂,会造成难以维护的问题。如果仅仅只是对类进行扩展,并不建议使用继承,毕竟使用protocol和category是很简单、轻松的。除此之外,在开发过程中,我们应该尽量将界面、功能相似的类的代码提取到基类里面,然后各个子类继承自这个基类,实现各自的其他特殊部分。这样可以大大的优化代码,如果需要修改的话,只需要这倒对应子类修改即可。

category是可以被继承的。在某个父类中定义了category,那么他所有的子类都具有该category;
在需要为某个类创建私有成员方法时,也用category的方式来实现。
Category不能完全代替子类,有以下几个最大的缺点:

    1. 当在Category中覆盖一个继承的方法,在Category中的方法可以通过向super类发送一个消息来调用被继承的方法。但是,如果Category中覆盖的那个方法已经在这个类的其它Category定义过了,则之前定义的方法将没有机会被程序调用
    2. 在Category中无法确定其能够可靠的覆盖某个方法,而这个方法已经在其它的Category中定义过。这个问题在使用Cocoa框架时尤其 突出。当你想覆盖某个框架已经定义好的方法时,该方法已经在其它Category中实现,这样就无法确定哪个定义和实现会被最先使用,带来很大的不确定性。

此博客参考博客链接:http://bbs.itheima.com/thread-117162-1-1.html

posted @ 2015-09-14 13:43  紫忆  阅读(2034)  评论(0编辑  收藏  举报