内容概要
几个字尾的基本概念
“-Oriented”含义
- “-Oriented”翻译,导向的,定向的
- “Object-oriented”相信所有程序都是由对象构成的
- 综上,开发时,写代码,心中需要有面向对象的信仰,写各种class实现需求。
“-Based”含义
- “-Based”翻译,根基,以…为基础
- “Requirement-based”基于需求,有先后的顺序。简单来说,就是先进行需求分析,基于分析得到的结果,再进行后续的开发活动。
“-Driven”含义
- “-Driven”翻译,引导,受到驱策的(不是驱动)
- “Model-driven”开发之前,先制定一个模型(为开发引导方向),不违背该模型前提下,进行实际开发活动
- “Use Case-driven”以用户的使用需求为方向,进行开发。
“-Centered”含义
- “-Centered”翻译,围绕…为中心
- “Architecture-centered”一切开发活动都围绕架构,就像在一颗圣诞树上挂灯饰。
重新认识对象(Object)
OOP思想中,我们所认识的一切东西,都是对象(Object)
如何才能算认识?
能够说出其特点并能与其他对象进行比较,而特点包括:
- 对象的特征或者属性
- 对象的行为
面向对象什么意思?
相信我们身边的一切,都是有相应的class与之对应
举个栗子:
- 自然界中,鸟的特征:有翅膀,有眼睛;鸟的行为,鸣叫,飞行。
- 代码界中,对象是由数据(Data)与函数(Function)组成。
类的用途 - 叙述软件对象
类(Class)是群体(或集合),而对象是类的一个个体,需要明白对象与类的关系。
类是一群具有共同重要特性的对象。类的定义即是说明这群对象有什么重要的特性(包括对象的特征及其行为),而软件中,对象以数据来表达特征,以函数来表达行为。
代码示例:
另外,实例(Instance):与对象含义相近似,而在计算机角度,实例偏向于,开辟了的存储空间。
<基类/子类>结构用途
继承(Inheritance):子类会继承父类的所有特性(特征以及其行为),也可以理解成子类是父类的扩展的一种形式。
对众多对象分门别类之后,可以形成一个继承体系。
可以这么看,Person是一个大的集合,而Teacher、Student因为自身拥有独特的特性(在满足人全部特性的前提下,老师需要教书这一行为,而学生需要做作业这一行为),所以它们是大集合中的一部分,也就是子集。
而定义子类时候,使用关键字“extends”,这翻译过来正是扩展的意思,也就是在父类的基础上再拓展自己独特的部分。
代码示例:
注意:继承的是父类的定义,而不是继承父类的定义项的具体数值。例如,父亲类年龄项,然后有个父亲对象年龄被设置成30,儿子类继承父亲类后,继承的是年龄项的这个定义,而与它后来被设置的具体数值是没有关联的。
|
|
Android中的<基类/子类>结构
Android本身就提供许多完整的框架基类,而我们大多数情况下的开发,都基于框架上,继承相关类并做出相应的扩展,从而实现需求。
例如:View继承框架(部分)
类与类的组合关系:接口(interface)与实现类的关系(implements),接口会定义一个等待实现的方法,实现类需要实现接口定义的方法,换一种说法,接口定义了一种格式,而实现类需要符合这种格式才能与接口对接(implements),就像电脑的USB接口,那么U盘就必须要有一个与这个接口相匹配的对接处才能接上电脑。
例如:Java中,提供了一个Thread基类和一个Runnable接口,那么这两个元素构成了一个框架。Thread基类有一个start方法,而Runnable接口有一个run方法。那么如何使用这个框架?Thread类需要传入一个Runnable接口的实现类,那么,就需要构造一个Runnable接口实现类。上述过程可以想像成,你需要打开U盘里面的一个文件(Thread.start();),首先你要接入U盘(传入一个Runnable接口实现类),而你需要这个U盘是一个能够符合USB规格的U盘才能接入(需要构造一个实现Runnable接口的实现类),而且你还需要保证U盘里面有你想要的文件(也就是根据需求,实现run方法)。
综上:这个框架工作原理就是,Thread基类先创建一个小线程,然后该线程通过Runnable接口,呼叫实现类实现后的run方法。
现在,换一种方式来思考,Thread类与Runnable接口,直接看的话,看似没有什么关系,但是通过理解上面的工作原理,那么就可以这么认为了:把Runnable塞入Thread中(就像把USB接口镶嵌在笔记本电脑上),然后Thread就看成一个抽象函数(不是真的是抽象函数,只是从结构上认为),而实现类的方法会覆盖掉Thread类中的run抽象方法,这样就变成了实现类是Thread类的子类了(注意,两者不是继承关系)。
原本的结构:
将Runnable塞入Thread中后的结构:
那么,这样不就是基类子类的结构关系了吗?
也说明了组合关系可以变成这样一个基类子类的关系。
这样可以得出一个结论:
基类子类的结构可以呈现两种意义:
- 继承关系
- 组合关系
代码示例:
工作原理,即基类子类工作原理:
new子类(Task)时候,会自动new基类(Thread)的对象,而run方法又被Task实现了,所以能如此使用
<基类/子类>结构的接口
卡榫函数(Hook函数) - 指的是接口中的方法函数,起结合基类与子类,实现PnP(Plug and Play)的作用,所谓PnP,通俗来讲,就是能够自由地接上,拔下,自由更换的意思,能够跨平台适用。
以Template Method设计模式为例
AbstractClass(基类)通过两个Hook函数与子类相连接,原理:ConcreateClass(子类)重写了Hook函数,从而使基类与子类建立了关联,基类在调用这两个Hook函数时候会调用子类重写的两个方法。
卡榫函数(Hook函数)及应用框架之基本原则:将变化的(Variant)与不变化的(Invariant)分离,一般而言,分离之后将不变的部分写在父类中,变化的部分写在子类中,而其目的就是为了让基类可以被复用。
IoC机制(Inversion of Control),继承体系中,基类函数可以主动调用子类函数(反向),而子类函数调用基类函数则是(正向)
,而基类函数主动调用子类函数正是典型的IoC机制。
基类与子类间,控制权在基类上(因为可以通过Hook函数调用子类函数),因为通常基类先写而子类后写,基类(前辈)拥有控制权控制子类(后辈)的情况,称作控制反转。
- 默认行为(Default函数)
基类有一个重要功能:可事先定义一定量的默认(预设)行为供子类继承或者调用。
主动型与被动型API
首先通过例子来进行初步了解:
解析:
- 基类通过Hook函数调用子类具体函数这种IoC机制的调用形式称做主动型API
- 子类正向调用基类,站在基类角度,是被调用的,因此称为被动型API
也就是说,主要是在基类的角度来区分两者的。
- 由基类定义并实现某个方法,而由子类来调用,即是被动型API
- 由基类定义某个方法,而由子类来实现,最终由基类本身来调用该方法,即是主动型API(实现该种方法弹性更高,设计更科学)
历史上的一些应用?
先举一个反例,1990年代初,Orbix 系统使用了被动型API,子类调用基类定义并实现的方法,当子类越来越多的时候,那么被调用的基类的方法就被固定死了,因为稍有改动就会影响到全部调用它的子类,这里子类指应用程序,而基类指系统。主动权在子类上,因此该系统不久就走向了灭亡。
Windows 系统采用了主动型API,95年时采用COM/DCOM框架定义了一个接口,而应用程序则需要实现这样一个接口,最后被该系统框架调用,控制权在系统中;
2001年改进后采用了.NET框架,建立了基类子类结构,而COM/DCOM额外定义了接口,没有基类子类的结构;.NET框架通过Hook函数与应用程序相连接,实现IoC机制,系统掌握全部控制权;但其中也有被动型API,但需要在调用主动型API的前提下才能调用被动型API。
因此,API十分重要,接口(Interface)是连接的关键,拥有接口的控制权,则拥有了主导的地位
Android中的API范例
Activity:
开发过程中,能够实现各种各样的功能与效果,看似很自由,实际上,我们都是在根据需求来实现一些系统接口,而这些接口正是掌握在Google定义的Activity基类中,再仔细看。
onCreate方法为例
实现原理:Activity基类先调用onCreate方法接口,而onCreate这个接口又连接到UserActivity(开发者写的Activity)子类中的onCreate方法。
即:onCreate这个接口是Activity基类定义的,再通过UserActivity子类实现,最后由Activity基类调用UserActivity的具体实现。
- 再看onClickListener的onClick方法,由基类OnClickListener来定义,由App开发者来实现,最后由OnClickListener基类来调用。Android中主动型API处处可见。
总结,接口与类
OOP中,将接口定义为一种特殊的类。因为OOP中,一切都是由类构成的。
C++中类别有三种:
- 具象类(所有函数都是有具体实现的)
- 抽象类(有一个或者多个函数是抽象的)
- 纯粹抽象类(全部函数都是抽象的,而Java中将该类别称为接口-Interface)
IoC机制在Java中的实现(也就是我们最常用的使用接口实现类)
以Runnable接口为例
直接来看,Task与Thread是通过Runnable接口来进行关联的,并不存在基类子类的关系,但是因为Thread的构造方法需要传入一个Runnable接口的实现类,可以认为Thread类内部有一个Runnable接口,再将Runnable接口中的run方法放入Thread类中,而Task实现了Thread定义的run方法,那么Thread与Task的基类子类关系就成立了。
IoC机制代码简单实现
学习资源推荐
Android从程序员到架构师之路系列视频 - 高焕堂;