面向对象的思想

5、面向对象的设计思想 一 面向对象设计思想 1.1 面向过程的设计思想与面向对象的设计思想 例如:我要去新疆 面向过程: 我开车,我挂挡,我踩油门,我过河北,我过陕西... 面向对象: 我命令车去新疆 车怎么去不关我事 信息封装在这这个类的内部 我不用去了解车整个开动的过程 面向对象可以对代码重复的使用,比如上面我要去新疆,先造一个车的类,稍后在去新疆的时候直接调用... 阅读详情

这部分内容比较抽象,枯燥,无聊,但是却十分重要!希望大家能够对这块重视起来,他可以帮助你编写出高质量,优秀的面向对象代码!!
20世纪90年代期间,一些软件研究人员和从业人员在一起研究一种基于对象概念的新方法。先前的计算机变成方法将数据和过程分开,但是许多分析员发现,这种分离是不自然的。因为这和人类认识自然界事物的方式是不一致的,人类认识一个事物的通常过程是这样的:首先看到这个东西,然后会观察这个东西外部是什么样的,里面有什么,它能干什么。这里的外部和里面有什么属于事物的属性,能干什么是事物的行为特征,很多时候这两个部分是不可分离的,至少人类的思维模式是这样的。因此人们在长期的实践和研究中提出了基于对象的编程方法学,这就是我们现在广为使用的面向对象的编程思想。
面向对象的编程思想是比较契合人类认知思维的,为了使得所有人都能够使用这个原则去对一些事物进行抽象,面向对象提出了一系列的原则,这些原则和基本方法可以帮助我们更好地进行对事物的描述。

1. 四个特征

面向对象和面向过程有很大的不同的,它有以下4个基本特征。

抽象性

计算机软件科学就是一门高度抽象的学科,编程语言作为一个重要的工具,抽象性是所有编程语言的共性。面向对象的抽象性和面向过程的抽象性是不一样的,面向对象的抽象方式,是从整体上对一个对象的数据或者属性,行为进行的抽象。忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节。比如,我们要设计一个学生成绩管理系统,考察学生这个对象时,我们只关心他的班级、学号、成绩等,而不用去关心他的身高、体重这些信息。抽象包括两个方面,一是过程抽象,二是数据抽象。过程抽象是指任何一个明确定义功能的操作都可被使用者看作单个的实体看待,尽管这个操作实际上可能由一系列更低级的操作来完成。数据抽象定义了数据类型和施加于该类型对象上的操作,并限定了对象的值只能通过使用这些操作修改和观察。

继承性

这个特性是每个抽象对象类之间的纵向关系描述,它是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。对象的一个新类可以从现有的类中派生,这个过程称为类继承。新类继承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父类)。派生类可以从它的基类那里继承方法和实例变量,并且类可以修改或增加新的方法使之更适合特殊的需要。这也体现了大自然中一般与特殊的关系。继承性很好的解决了软件的可重用性问题。比如说,所有的Windows应用程序都有一个窗口,它们可以看作都是从一个窗口类派生出来的。但是有的应用程序用于文字处理,有的应用程序用于绘图,这是由于派生出了不同的子类,各个子类添加了不同的特性。

封装性

这个特性和抽象性有一些关联,对一个事物的抽象就是需要忽略一些不重要的细节,这些细节的忽略基本就是通过封装性来完成。封装性是面向对象的特征之一,是对象和类概念的主要特性。封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。一旦定义了一个对象的特性,则有必要决定这些特性的可见性,即哪些特性对外部世界是可见的,哪些特性用于表示内部状态。在这个阶段定义对象的接口。通常,应禁止直接访问一个对象的实际表示,而应通过操作接口访问对象,这称为信息隐藏。事实上,信息隐藏是用户对封装性的认识,封装则为信息隐藏提供支持。封装保证了模块具有较好的独立性,使得程序维护修改较为容易。对应用程序的修改仅限于类的内部,因而可以将应用程序修改带来的影响减少到最低限度。

多态性

多态性是指允许不同类的对象对同一消息作出响应。比如同样的加法,把两个时间加在一起和把两个整数加在一起肯定完全不同。又比如,同样的选择编辑-粘贴操作,在字处理程序和绘图程序中有不同的效果。多态性包括参数化多态性和包含多态性。多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题。多态性允许一个对象对相似的消息进行不同的响应,从而做出不同的处理。多态性有两种形式:横向多态和纵向多态。横向多态通常是指一个类内部多个方法接口之间的多态,通常这个特性的实现就是通过同样的函数名,返回值,但是参数不一样的方式实现的。纵向多态通常是指类之间的泛华关系,通常是通过类的继承和override覆写完成的。

2. 六个关系

依赖
关联
继承(泛化)
实现
组合
聚合
这六个关系在下面的7个原则中会有描述,请详见下一小结。

3. 七个原则

基本是5+2原则,5个是SOLID, 2个是CL
5个SOLID:
S: 单一职责原则(Single Responsibility Principle)
O: 开闭原则(Open Close Principle)
L: 里氏替换原则(Liskov Substitution Principle)
I: 接口隔离原则(Interface Segregation Principle)
D: 依赖倒置原则(Dependence Inversion Principle)
2个CL:
合成/聚合复用原则(Composite/Aggregate Reuse Principle CARP)
迪米特法则(Law Of Demeter)
在进一步说明以上的7个原则之前,我们应当知道:这些原则只是对面向对象设计原则在不同角度,不同层次,不同方式的抽象描述而已,他们之间关系是相互支撑,相互补充,相互影响的。请不要孤立地看待某个原则!

开闭原则(Open Close Principle)

就像牛顿的三大定律是经典物理学的奠基石一样,开闭原则也是面向对象设计的奠基石,开闭原则通常简写为OCP。
那么什么是开闭原则呢?开闭原则的含义是这样的:一个软件实体应该对扩展开放,对修改关闭。这个说法最早是由Bertrand Meyer提出的,原文的内容如下:
Software entities should be open for extension, but closed for modification.
这样的说法似乎比较抽象,用好理解的话来说就是:在设计一个软件模块的时候,应该是这个模块可以在不被修改的情况下可以灵活地扩展软件功能。这听起来好像比较矛盾,怎么可能既不修改代码,又要添加软件的新功能呢?但是实际上面向对象的思想和设计原则努力的目标就是这个,并且现在也有很多模式可以做到这一点。比如说,软件热插拔技术就是一个典型的例子,人们可以随时编写一个可插拔模块,然后以某种方式在不修改原来软件代码的情况下直接添加新的功能,甚至你都不用停止软件的运行!!
所有的软件系统都有一个共同的性质,那就是它们都会随着时间的流逝而不停地发生着各种变化,即唯一不变的就是不停地改变。在软件系统面临新的需求的时候,系统的设计必须是稳定而又灵活的。我们不可能一旦有新的需求提出的时候,我们就需要大规模地去修改我们的代码,这样牵一发而动全身的方式在软件开发中成本实在是太高了。在现实生活中,假如某人感冒发烧了,他去看医生,医生肯定是针对感冒发烧的症状开出处方,不可能医生让他去查一下心脏或者脚哪里有没有毛病的。但是在软件世界中,这样的事情经常会发生。一个模块需要修改,另外一个完全不相干的模块也需要进行修改,这是完全不可理喻的,但是这确实是每天都会发生的事情。为什么呢?原因就是你的软件设计有问题!你的软件和开闭原则相违背。满足开闭原则的设计可以使得我们的软件有着无可比拟的优越性:
1. 通过扩展已有的软件系统,可以以极地的代价实现新的功能,以满足对软件新的需求
2. 已有的软件模块,特别是最重要的抽象层模块,不能再修改,这就使得变化中的软件系统有一定的稳定性和延续性。
总的来说,考虑开闭原则会使得你的软件系统以不变应万变!

怎么才能做到符合开闭原则

抽象化是关键

解决这个问题的关键就是在于抽象。在想java这样的纯面向对象的编程语言中,可以给系统定义出一个一劳永逸,不再修改的抽象设计,此设计允许有无穷无尽的行为在实现层被实现。在java语言中,可以给出一个或者多个抽象的java类或者interface,这些抽象的表述表明了一个类别应该拥有的能力和特征签名。这个抽象遇见了所有可能的扩展,在任何情况下实现层可以任意添加自己特有的一些功能,这样的话就使得我们的抽象类,也就是既有的代码不用修改就能添加新的行为,这是对修改关闭。同时,由于我们在设计这些抽象类的时候,只是在高度抽象的层次上对业务逻辑进行描述,没有具体的描述在很多情况下就是通用的。也就是说,我们的抽象层允许任何形式的扩展,这也就是对扩展开放。

对可变性进行封装

开闭原则从另外一个角度描述可以是这样的:对可变性进行封装,这句话的意思是找到软件系统中可能会发生变化的部分,然后将他们封装起来,只是对外界提供抽象定义的,统一的,通用的交互方式。在面向对象思想经典圣经GOF中说道:考虑你的设计中什么可能发生变化。与通常将焦点放在找到什么会导致我们系统设计发生改变的思考方式正好相反,我们考虑的不是什么会导致设计改变,而是考虑允许什么发生变化而不会导致重新设计。对可变性进行封装意味着:
1. 一种可变性不应该散落在代码的很多角落里面,而应该被封装到一个对象里面,同一个可变性的不同表象意味着同一个继承等级结构中的具体子类。继承应该是封装变化的一种手段,而不是从一般的对象生成特殊对象的手段。
2. 一种可变性不应该与另外一种可变性混淆在一起。
很明显,对可变性进行封装的思想正是从工程的角度解释了如何实现开闭原则。

里氏替换原则(Liskov Substitution Principle)

从开闭原则中我们可以看出,面向对象的设计重要原则就是创建抽象层次,并且从抽象中导出具体描述。具体化可以给出不同的版本,每一个版本都给出不同的实现。从抽象到具体的过程,在编程语言中通常就是继承。针对继承关系我们就要使我们的设计符合里氏替换原则(LSP)。
那么什么是里氏替换原则呢?这个原则的表述比较抽象,如下:
如果对每一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序在所有的对象o1换成o2的时候,程序的行为完全没有变化,那么就可以说T2类型是T1类型的子类型。
换个简单的说法就是,一个软件实体如果使用一个基类的话,那么一定可以使用其子类实现相同的功能,而且不会引起任何问题。举个例子,现在有一个Father类,他有一个子类Son,现在如果某个符合LSP的软件某块的接口是这样的:

method(Father obj)

那么,如果我们传递的参数是子类的对象时,软件任然会正常运行,不会出错。
LSP是面向对象继承复用的基石。只有当衍生类可以替换掉基类,并且软件单位的功能不会收到任何影响是,基类才能真正被复用,而衍生类也可以在基类的基础上添加新的行为。这里需要说明的是,上面的原则反过来则代换不成立,如果你使用一个父类的对象替换子类的对象话,那么可能会出错,因为子类很有可能在父类的基础上添加了新的方法,而软件模块恰好使用了这个方法,这个时候如果你代换的话,就会导致软件崩溃。
接下来,我们来看一下在Java语言中对LSP的支持,这个支持是在编译层次上的。现在假如我们有一个类Father,这个类有一个public方法叫做speak,然后我们新建一个叫做Son的类继承自Father类,Son类当然也会有speak方法。现在我们可不可以将这个speak方法访问权限变成private呢?答案是不可以,编译器会报错的!这就是java中的语法规则:
1. 子类不能将父类继承的方法权限缩小
从LSP角度来看待这个问题的话,那就是如果我们的子类可以将speak方法设置private的话,那么如果我们在一个软件系统中使用LSP代换父类对象的话,那么运行就出错了!因为子类的方法是private的,不能访问!这个情景就像父亲本来会说话,但是儿子不会说话了,现在儿子所在的场合又必须要说话!这就有问题了(虽然显示生活中,这样的事情可能发生),因此java编译器肯定不能让这样的代码编译通过。但是需要注意的是,java语言对LSP的支持粒度是比较大的,支持的范围是有限的。因为所有的支持都是在语法层次上的,并没有在实际的逻辑上进行支持,这里最著名的问题就要数西方人一直争论的问题:正方形是不是长方形的子类。

依赖倒置原则(Dependence Inversion Principle)

个人认为,依赖倒置原则是7个面向对象原则中最不好理解的的一个原则,这个原则描述的内容相对抽象。实现开闭原则的关键是抽象化,并且从抽象化导出具体化实现。如果说开闭原则是面向对象的设计目标的话,那么依赖倒置原则就是这个设计的主要机制。
依赖倒置原则讲的内容就是:要依赖于抽象,而不是具体。

什么倒转,为什么要倒转?

理解DIP的关键就在于立即倒转这个概念,那么到底什么是倒转呢?简单来说,传统的过程性系统的设计方法倾向于是高层次的模块依赖于低层次的模块实现;抽象的层次依赖于具体的层次。举个例子,假如现在你有一家公司,这家公司具体是做什么行业的以及怎么管理等关键问题需要依赖于你这家公司具体主营的业务以及公司的组织结构和盈利模式等。这就是上层依赖底层,抽象依赖具体。咋一看这么思考问题确实没有什么问题,但是如果我们仔细想一下这个问题就会得出不一样的结论。这个问题关键就在于我们的关注点在什么地方,如果我们的关注点在底层,在具体的问题上那么我们会认为上层依赖下层是合理的;但是如果我们关注上层的抽象的业务逻辑的话,那么我们会认为具体的业务实践应该以我们最高的商业规则为指导,在抽象层的规范下运作。这两种思考模式本身没有什么问题,但是在处理具体的问题时就不一样了。我们知道现实生活中,事物的变化是每时每刻都在进行的,如果我们一直关注具体的细节问题的话,那么我们会非常劳累,陷入各种具体繁杂的事务中去。这就好像,你让某人帮你买瓶饮料,如果你的只是想喝点东西解渴的话,你根本就没必要告诉某人说你要喝什么牌子的什么型号的饮料。你并不会依赖于他给你买的是什么饮料,而他则会依赖于你的要求,因为你要求的是饮料,他决不能给你买个面包。
抽象的层次含有宏观的和重要的商务逻辑,这应该决定具体的层次实现的。抽象层依赖于具体层次显然是不合理的,依赖倒转原则(DIP)就是要把错误的依赖关系再倒转过来。

依赖关系的种类

DIP将是依赖关系的倒转,因此我们需要弄明白什么是依赖关系,都有哪些关系。通俗来讲,依赖就是两个实体之间有关联,一个实体的正常功能发挥需要另外一个实体的协助,比如电脑的运行需要供电设备的协助,那么这两个实体之间就存在依赖关系。那么依赖表明两个实体之间有关联,在软件设计上这个关系称为耦合,因此从狭义上将依赖关系在软件设计上我们可以理解为耦合关系。耦合有很多中,但是整体分为以下3类:
1. 零耦合关系:如果两个类之间没有任何关系,就称为零耦合
2. 具体耦合关系:具体性耦合关系发生在两个具体的类之间,也就是说这两个类本身没有依赖关系,但是实例化之后,这两个类的实例化对象之间可能存在耦合关系。举个例子吧,现在有两个类:人类和凳子类,这两个类之间没有任何逻辑上的依赖关系;但是如果我们把它们具体化之后:有一个特定的人叫张三,有一个特定的凳子是木凳,现在张三的某种行为可能需要这个木凳的存在(比如他在休息),因此他们之间的关系叫做具体耦合关系。
3. 抽象耦合关系:抽象耦合关系发生在一个具体的类和一个抽象类或者接口之间,使两个必须发生关系的类之间存在最大的灵活性。

依赖倒转原则的表述

现在我们可以来说明一下到底什么是依赖倒转原则。简单来说,依赖倒转原则(DIP)要求客户端依赖于抽象的耦合,DIP的表述如下:
抽象不应该依赖于细节,细节应该依赖于抽象。英文原文表达:
Abstractions should not depend upon details. Details should depend upon abstractions.
DIP的另一种表述如下:
要针对接口编程,不要针对实现编程。针对接口编程的含义就是,应该使用java的interface和抽象类来进行类的变量声明,方法定义。不要针对实现编程的意思就是说,不应该使用具体的java进行变量的声明,方法的定义等。要保证做到这一点,一个具体的java类应该只实现java接口和抽象类中声明过的方法,不应该给出多余的方法。倒转依赖关系强调一个系统内部的实体之间关系的灵活性。

怎么做到DIP

根据DIP的表述,我们知道将类之间的关系以抽象的方式耦合是实施的关键。由于一个抽象耦合关系总是要涉及从抽象类继承,并且需要保证再任何引用到基类的地方子类都能进行代换,因此我们上面将的LSP是实现依赖倒转的基础。在抽象层次上进行耦合虽然有灵活性,但是也带来了额外的复杂性。在某些情况下,如果一个具体类发生变化的可能性很小,那么抽象耦合能发挥的好处便十分有限,这是使用具体的耦合关系反而会更好,是的代码更加清晰易懂。DIP是面向对象设计的核心原则,java的很多设计模式都是以DIP为指导进行设计的。

接口隔离原则(Interface Segregation Principle)

接口隔离原则(ISP)讲的是:使用多个专门的接口总比使用单一的复杂接口要好。换句话说,从一个客户类的角度来讲,一个类对另一个类的依赖应该是建立在最小的接口上的。这里有一点需要说明,我们说的接口不是单单指java中的interface,而是两个软件实体之间的一种关于协作的契约,而java的interface只是这种契约在java语言上的一种实现而已。我们更多的描述是在于逻辑上的抽象。

划分我们的角色

如果将接口理解成一个类所提供的所有方法的特征集合也就是一种在逻辑上才存在的概念的话,我们可以理解实施ISP的关键就是在于接口角色类型的划分。一个接口就相当于剧本中角色,而这个角色在舞台上由哪个演员来实现就相当于接口的实现。因此,一个接口就应该简单地代表一个角色,而不是多个角色。如果系统涉及多个角色的话,那么每一个角色应该有一个独立的接口代表。我们需要根据我们实际的业务需求,将我们的接口角色进行合理的划分,尽量避免繁杂臃肿的接口实现。

定制服务

如果我们将接口就简单地理解成java的interface的话,那么我们可以这么认为:ISP讲的就是为同一个角色提供不同宽度的接口,以对应不同的客户端。在现实生活中,这种方式经常见到,比如我们去银行办理业务的时候,比如两个客户都是存钱,客户A要存10W,客户B要存1000W,那银行提供的服务肯定是不一样的(客户B的服务肯定是VIP啊~~~),这就叫做服务定制,或者特殊服务。

不要污染现有的接口

任何使得现有接口变得臃肿的行为都是对接口的污染!我们在任何情况下都不能将一个和现有接口业务逻辑不一致的内容添加到接口中去,一个接口所代表的角色应该就是唯一的,简单的,明了的。有些时候,我们为了省事,将多个接口合并到一个接口中去,美其名曰:代码优化整合!其实就是对接口的污染,其结果只能是使得我们的软件系统中由于出现了畸形的代码而变得的丑陋不堪且难以维护。

纯粹的接口,纯粹的行为

一个接口应该尽量简单而纯粹,不要有任何多余的内容。总之一句话:不要向客户端提供任何它不需要的方法!!

合成/聚合复用原则(Composite/Aggregate Reuse Principle CARP)

合成/聚合复用原则(CARP)讲的内容就是在一个新的对象里面使用一些已经存在的对象,使之成为新对象的一部分,新的对象通过委派达到复用已有功能的目的。这个原则还有一个比较简单的描述:要尽量使用合成/聚合,而不要使用继承实现。合成和聚合的概念是如此地相近,以至于我们一直以来都弄不清他们之间的区别,下面我们就看一看究竟什么是合成,什么是聚合。

合成和聚合的含义

从广义角度来说,合成(Composition)和聚合(Aggregation)都是关联(Association)的特殊种类。同时,合成和聚合都有“拥有”的含义。但是需要注意的是,合成和聚合拥有的程度是不一样的。合成是一种意味比较强烈的拥有关系,也就是说一个实体是属于另外一个实体不可或缺的一部分,部分脱离整体存在没有意义,整体脱离部分则不是一个整体(有点类似于,台湾和中国的关系)。而聚合的拥有意味就比较弱了,一个实体在某种情况下需要另外一个实体的存在,他们在一起相互作用形成一个整体。举个例子,我们要描述“一个坐在凳子上的人”这个事物的时候,人和凳子就是聚合的含义,人离开了这个凳子照样可以存在,因为他还可以坐在别的凳子上;凳子离开了人同样可以单独存在,因为别的人可以坐在这个凳子上。说白了,就是他们只是在某些场景下协作形成一个整体而已,在逻辑上并没有太多的关联。

CARP复用的种类

CARP讲的说就是面向对象设计里面的复用问题,只是它强调合成/聚合复用,而不是继承复用。下面我们比较说明一下合成/聚合复用和继承复用的方式。

合成/聚合复用

由于合成或者聚合可以将已有的对象纳入到一个新的对象中,并使之成为新对象的一部分。因此这里新的对象就是在复用已有对象的功能,这样做有以下好处:
1. 新的对象操作成分对象的唯一方法就是通过该成分对象的接口
2. 这种复用属于黑箱复用,即新类不必关心已有类的内部实现只是关系已有类的对外接口,这和DIP讲的内容是一致的。
3. 这种复用支持包装
4. 这种复用所需的依赖较少
5. 每一个新的类可以将焦点集中放在一个任务上
6. 这种复用可以运行时进行,比较灵活
一般而言,如果一个角色得到了更多的责任,那么他可以使用合成/聚合关系将新的责任委派给已有的合适对象。这就像人们之间的合作,你不可能任何事情都是亲力亲为,有些事情你完成不了的你就必须要委派给被人来帮你完成。(比如你没有对象,你需要委派婚恋机构帮你找到你的那个他/她,如果你还是一直自己寻找的话,那真的就要当一辈子的光棍了~~)。当然这种方式也是有一定的缺点的,主要就是再这种方式中新的对象会有很多委派的对象要管理(比如说你要和婚恋机构保持联系,有的时候他们真的很烦,总是各种电话骚扰……)。

继承复用

合成/聚合复用作为复用手段,可以应用到几乎任何环境中去。而与此不同的是,继承只能应用到很有限的环境中去。换言之,尽管继承是一种重要的复用手段,但是你还是要首先考虑合成/聚合,而不是继承。
整体来说,继承的种类分为两种:实现继承和抽象继承。实现继承是说从一个实现类继承得到子类,比如从“张三”继承得到“小张三”,抽象继承是说从一个抽象的类继承或者一个java interface继承实现你的新类,比如从人继承得到张三。通常而言,在实践中应该尽量使用抽象继承而不是实现继承。继承复用也是有优点的:
1. 新的实现比较容易,因为超类的大部分功能可以通过继承关系自动进入子类
2. 修改或扩展继承而来的实现较为容易
但是继承复用也是有缺点的:
1. 继承复用破坏包装,因为继承复用将超类的实现细节暴露给了子类。由于超类的内部细节对于子类是透明的,因此这种复用是透明的复用,也称“白箱复用”。
2. 如果超类的实现改变时,那么子类的实现也会不得不改变。因此当一个基类发生改变的时候,这种改变就会延续传递给每一个子类,在某些情况我们可能需要重新设计我们的子类。其根本原因就是基类和子类之间的耦合关系太过于强烈所致。
3. 从基类继承而来的实现是静态的,不能在运行时发生改变,因此没有足够的灵活性。
由于上述的种种特点,我们应该尽量使用合成/聚合复用,而不是继承复用,这是一个重要的面向对象设计原则。从实体之间的关系上讲,合成/聚合关系更像是一种“has a”关系,而继承则是“is a”的关系,这种关系更加密切,因此如果一旦需要发生改变,那么影响的范围是比较广泛的。
在现实生活中,合成/聚合模式和继承模式,更像是一个人混社会的方式。合成/聚合模式是我们普通人的模式,我们有什么自己搞不定的困难了,各种依赖朋友帮忙解决,家里面基本不能帮我们解决;而继承模式则是高富帅们的方式,遇到任何问题,他们都能搞定,因为家里的权势实在是太大,他一生下来就从家族里面继承来了众多宝贵的社会资源!!所以说,对于大部分人而言合成/聚合模式比较靠谱,如果你是高富帅那继承模式就OK!

迪米特法则(Law Of Demeter)

迪米特法则(Law Of Demeter)又叫最少知识原则,就是说一个对象应该对其他的对象拥有尽可能少的了解。LOD其实是降低对象之间的耦合性的一种有效手段,它的表述比较多,通常有下面几个:
1. 只与你直接的朋友通讯
2. 不要和“陌生人”讲话
3. 每一个软件单位对其他单位都只有最少的知识,而且局限于与本单位联系密切的单位。
要注意这里表述中的“直接”,“陌生人”,“密切”等关键字的表述是比较模糊的,没有统一定性的描述,这就使得LOD在不同的环境下有不同的解释。如果两个类之间不必直接通讯,那么这两个类就不应该直接发生相互作用。如果其中的一个类需要调用另外一个类的某一个方法的话,应该通过第三者转发实现。

朋友圈和陌生人

这是LOD中重要的两个概念,首先我们需要明确什么是朋友圈,朋友圈就是由某个对象的“朋友”组成的范围。那么什么是朋友呢?符合以下条件的就可以称作是朋友:
1. 当前对象本身
2. 以参数的形式传入到当前对象方法中的对象
3. 当前对象的实例变量直接引用的对象
4. 当前对象的实例变量如果是一个聚集(比如java中的list),那么聚集中的元素也是朋友
5. 当前对象创建的所有对象
除此之外的对象都是陌生人,我们的对象不应该和他们直接通讯。

单一职责原则(Single Responsibility Principle)

这个原则讲的事情比较简单:一个优良的系统设计,强调模块间保持低耦合、高内聚的关系,在面向对象设计中这条规则同样适用,所以面向对象的第一个设计原则就是单一职责原则。单一职责,强调的是职责的分离,在某种程度上对职责的理解,构成了不同类之间耦合关系的设计关键,因此单一职责原则或多或少成为设计过程中一个必须考虑的基础性原则。说白了就是,你的对象的职责应该足够简单,精简,以至于和他任何逻辑上不相关的职责的事情他都不会去做!

使用面向对象思想进行Android布局 使用面向对象的思想来进行Android的界面开发布局。运用好了可以有利于我们进行一劳永逸的编程,提高开发效率 立即下载

相关推荐

手把手配置:用Synopsys VDK为英飞凌TC4x系列MCU做虚拟原型开发

本文详细介绍了如何使用Synopsys VDK为英飞凌TC4x系列MCU进行虚拟原型开发,涵盖开发环境搭建、关键外设虚拟化配置、AUTOSAR栈集成及虚拟调试与性能调优全流程。通过虚拟原型技术,开发者可提前6-12个月启动软件开发,显著提升汽车电子项目的开发效率。

weixin_30606461的博客 290

Android面向对象编程范式

后来,诸如 C++、Java、Python 和 C# 等语言也采用了这种方法,在行业中产生了更易于管理、可持续和可维护的项目。然而,随着时间的推移,软件范式出现是为了解决各种问题、缺陷和最重要的需求。通过创建类的对象,我们可以访问和实现该类的属性和方法。我们可以使用数据库对象提供的函数访问数据,但我们不知道它是如何存储和管理的。现在,让我们来看看 OOP 的基础知识以及支撑这个使我们的项目更加模块化的范式。1- 类能将代码分解为更小的可管理组件。2- 可重用性:基础类或数据类型的抽象增加了代码的可重用性。

虎哥LoveDroid 348

激光雷达接收芯片选型指南:APD、SiPM、SPAD阵列的优缺点对比

本文深入对比了激光雷达接收芯片APD、SiPM和SPAD阵列的核心技术原理与性能差异。APD作为线性模式工作的稳健选择,动态范围大;SiPM具备单光子探测和高增益优势;而SPAD阵列凭借像素级数字读出和集成TDC,在时间分辨率与系统集成度上领先。文章从灵敏度、时间精度、动态范围及系统成本等多维度剖析,为不同应用场景(如车载雷达、Flash LiDAR、消费电子)的芯片选型提供实战指南。

ik6789012的博客 656

Android常用面向对象设计模式

对于开发人员来说,设计模式有时候就是一道坎,但是设计模式又非常有用,过了这道坎,它可以让你水平提高一个档次。而在android开发中,必要的了解一些设计模式又是必须的,因为设计模式在Android源码中,可以说是无处不在。下面要说的java面向对象的设计模式,对于想深入学习设计模式的同学,这里推荐两本书:《Android源码设计模式解析与实战》、《大话设计模式》。 前言 设计模式(Desig...

博由心生,福泽一生 2022

18、偏微分方程数值计算:粘性不可压缩流与电磁学的有限元方法

本文系统介绍了偏微分方程数值计算在粘性不可压缩流与电磁学中的应用,重点探讨了稳态与非稳态Navier-Stokes问题的混合有限元方法及求解技术,包括弱形式构建、半离散与时间离散格式、牛顿迭代法等。同时,详细阐述了电磁学中Maxwell方程的有限元与时域有限差分法(FDTD)求解方法,涵盖时间谐波与时域波方程、边界条件处理、稳定性分析与误差估计。结合MATLAB代码示例和实际案例,展示了速度场、压力、电场与磁场的数值模拟过程,并通过流程图与表格对比了两类问题的方法原理、应用场景与收敛性特性。最后展望了多物理

lemon的博客 87

【乐企-工具篇】生成发票左上角二维码

【代码】【乐企-业务篇】生成发票左上角二维码。

九七的博客 707

那些年搞不懂的高深术语——依赖倒置•控制反转•依赖注入•面向接口编程

那些年搞不懂的高深术语——依赖倒置•控制反转•依赖注入•面向接口编程  雪姬 2016-05-23 20:13:13 干货教程 评论(1) 作者:熬夜与烟 那些年,空气中仿佛还能闻到汉唐盛世的余韵,因此你决不允许自己的脸上有油光,时刻保持活力。然而,你一定曾为这些“高深术语”感到过困扰。也许时至今日,你仍对它们一知半解。不过就在今天,这一切都将彻底改变!我将带领你以一种全新的高清

junecauzhang的专栏 855

作为一名爬虫人如何实现千万级数据爬取?

爬虫门槛不高,高就高在往后余生的每一次实操都随时会让你崩溃!Bro

吴秋霖的博客 1万+

(转载)依赖、关联、聚合、组合

类与类图 1) 类(Class)封装了数据和行为,是面向对象的重要组成部分,它是具有相同属性、操作、关系的对象集合的总称。 2) 在系统中,每个类具有一定的职责,职责指的是类所担任的任务,即类要完成什么样的功能,要承担什么样的义务。一个类可以有多种职责,设计得好的类一般只有一种职责,在定义类的时候,将类的职责分解成为类的属性和操作(即方法)。 3) 类的属性即类的数据职责,类的操作即类的行为...

David 4万+

ArcGIS实验教程——实验二:ArcGIS地理配准完整操作步骤

本文介绍ArcGIS 10.2中地理配准的完整操作流程,涵盖加载扫描地图、启用地理配准工具条、添加控制点(含经纬度输入与坐标系统选择)、均匀分布控制点以提升精度,并最终通过“更新地理配准”完成栅格数据的空间定位。强调控制点数量、分布及坐标系匹配的重要性,附带实验数据下载链接与获取方式,适用于初学者掌握扫描矢量化前的关键几何校正技能。

智慧空间实验室 9万+

YOLOv13改进:注意力机制 | 动态稀疏注意力的双层路由方法BiLevelRoutingAttention | CVPR2023

BiLevelRoutingAttention方法对小目标检测效果比较好。可能是因为BRA模块是基于稀疏采样而不是下采样,一来可以保留细粒度的细节信息,二来同样可以达到节省计算量的目的。

①答疑群聊服务;②YOLO大模型知识问答系统;③计算机视觉论文生成智能体; 1435

面向对象编程思想的六大原则

面向对象的六大原则1.单一职责原则 单一职责原则英文全称:Single Responsibility Principle 简称SRP 定义:就一个类而言,应该仅有一个引起它变化的原因。简单来说,其实就是一个类只负责一个工作。就和流水线上的员工一样,每个人只负责一个工作。 注意:单一职责的划分界限并不是总是那么清晰,很多时候都是需要靠个人经验来界定的。 2.开闭原则 开闭原则英文全称:Ope

论一个程序员的自我修养 2734

大数据领域数据产品的团队激励与绩效管理

在大数据领域,数据产品的成功离不开高效协作的团队。团队激励与绩效管理的目的在于激发团队成员的工作热情和创造力,提高工作效率和质量,确保数据产品能够按时、高质量地交付。本文章的范围涵盖了大数据领域数据产品团队激励与绩效管理的各个方面,包括激励机制的设计、绩效评估的方法、团队沟通与协作等。本文将按照以下结构进行阐述:首先介绍核心概念与联系,明确相关概念的定义和相互关系;接着讲解核心算法原理和具体操作步骤,为团队激励与绩效管理提供理论支持和实践指导;然后分析数学模型和公式,帮助读者深入理解绩效评估的原理;

Java大师兄的博客 1018

面向对象思想总结

  一、面向过程的思想和面向对象的思想 面向对象和面向过程的思想有着本质上的区别,作为面向对象的思维来说,当你拿到一个问题时,你分析这个问题不再是第一步先做什么,第二步再做什么,这是面向过程的思维,你应该分析这个问题里面有哪些类和对象,这是第一点,然后再分析这些类和对象应该具有哪些属性和方法。这是第二点。最后分析类和类之间具体有什么关系,这是第三点。       面向对象有一个非常重要的设计...

每一天都是余生 2万+

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

JAVA面向对象思想

JAVA语言的思想就是面向对象,它是基于面向过程的。(C语言是面向过程的,其强调的是功能行为。)而面向对象强调的是功能和属性封装体,也就是说JAVA强调的是对象。如果说C语言着重个体的功能,那么JAVA则着重讲整体功能,或者说对象的功能。相对于C语言来讲,JAVA的出现也是编程思想的一种转变,从而使得编程变得好懂、易学。 例: C语言面向过程: 打开冰箱。 存储大象。 关上冰箱。 强调

a18937103206的专栏 1274

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

面向对象思想

面向对象思想: 基于过程思想:给一个需求,通过分析需求,然后一步一步的去实现!完成一个功能的具体步骤! 面向对象思想:创建对象,使用对象调用功能!(主要:对象) 举例: 面向过程: 打水:自己打水--->拿起杯子--->走到打水的地方--->打水 面向对象: 打水:让别人给我打水(只管水给我就可以了)---->喝水 面向对象思想的特点: 1)更符合我们的一些思想行

yrr0924的博客 290

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

全面化讲解面向对象思想

我们先来初步了解一下面向对象 初识面向对象 面向对象,他是我们开发的一种编程思想 编程思想 面向对象 : 就是一种编程思想 面向过程 : 也是一种编程思想 前言 学习面向对象,最重要的是理解,基于面向对象的编程思想,不断结合,使得语法认知更加透彻,并细致入微总结每一个语法点,关于面向对象的语法,前期需非常努力,后期也会持续加强,但学习时不可以疏忽懈怠 面向对象类和对象的概念理解 给大家一个概念,什么样算是面向对象呢? 生成一辆汽车过程示例 它包括了生产汽车的全部过程方法组成等等等等 这里,了

Jan's的博客 482

简述Java面向对象的基本思想_简述面向对象思想

简述Java中的面向对象(OOP)思想Java是一种面向对象的语言,那么什么是面向对象(Object Oriented Programming)?首先,什么是对象(Object)?Java中有一种思想叫做“万物皆对象”,对象就是具有某些特殊属性(成员变量)和行为方式(方法)的实体。现实生活中的任何事物都可以看作是对象,无论是具体或抽象的事物,比如:一个动物或者是城市的变化。具有两个特征:属性(Pr...

weixin_39968436的博客 726

Java 面向对象思想

一、浅谈Java面向对象三大特征:封装、继承、多态 对象,是具体存在现实中的事物,比如人,比如手机。 那么如果将对象用代码表示出来呢?首先我们要知道我们表示的是什么?我们表示的是人 二、面向对象的抽象、接口特征 参考博客有: 接口和抽象类的区别---相信你看完不会再混淆了_不悔的青春-优快云博客_接口与抽象类的区别 ...

weixin_60808029的博客 1817

Java面向对象思想

目录 面向过程思想概述 面向对象思想概述 举例 1:面向对象思想 2:面向对象的思想特点 3:把大象装进冰箱 4:开发,设计,特征 类与对象关系 类的定义 成员变量和局部变量的区别 类和对象的内存关系 总结 面向过程思想概述 l我们来回想一下,这几天我们完成一个需求的步骤:首先是搞清楚我们要做什么,然后...

moonlightpeng的博客 484

java的面向对象思想

1、初识面向对象 2、类与对象的关系 3、对象的创建分析 4、面向对象三大特性(封装、继承、多态) 5、抽象类和接口 6、四种内部类详解(成员内部类、静态内部类、局部内部类、匿名内部类)

m0_72541008的博客 1222

在编程里,什么是面向对象的思想?

这些概念老生常谈,但校长发现,不少同学对它们的理解还是停留在“考试会写代码”的层面,今天就给你拆开讲透。这个话题,听起来很玄乎,但凡是写过代码的同学都知道,几乎所有主流编程语言都在大谈“面向对象”:Java号称“生而为对象”,Python在用面向对象“秀优雅”,连C++的诞生都带着面向对象的使命。面向对象的核心思想,是把世界看作由一个个对象组成,而每个对象都是一个独立的“小世界”,有自己的属性和行为。于是,聪明的程序员们就开始琢磨:有没有一种更“优雅”的编程方式,能让代码更易读、好维护,还能少掉头发?

m0_61562916的博客 535

java面向对象思想

抽象是指将现实世界中的事物抽象成类和对象,忽略其非本质的细节,只关注其本质特征和行为。在 Java 中,抽象通过抽象类和接口来实现。抽象类不能被实例化,它可以包含抽象方法和具体方法;接口是一种特殊的抽象类型,它只包含抽象方法。Java 面向对象思想通过对象、类、封装、继承、多态和抽象等概念,提供了一种结构化、模块化的编程方式,使得代码更加易于理解、维护和扩展。在实际开发中,合理运用面向对象思想可以提高开发效率和代码质量。

qq_36633637的博客 438
上一篇: Android Training学习笔记之适配不同的设备
下一篇: Android基于wifi的无线HID设备实现
createchance
博客等级 码龄15年 156粉丝 90原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值