一、解释下多态性(polymorphism),封装性(encapsulation),内聚(cohesion)以及耦合(coupling)。
1封装(Encapsulation)是面向对象方法的重要原则,就是把对象的属性和操作(或服务)结合为一个独立的整体,并尽可能隐藏对象的内部实现细节。[
2继承是面向对象最显著的一个特性。继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。[1] Java继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。这种技术使得复用以前的代码非常容易,能够大大缩短开发周期,降低开发费用。比如可以先定义一个类叫车,车有以下属性:车体大小,颜色,方向盘,轮胎,而又由车这个类派生出轿车和卡车两个类,为轿车添加一个小后备箱,而为卡车添加一个大货箱。
3.定义:
多态:指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)
2.实现多态的技术称为:动态绑定(dynamic binding),是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。
4. 所谓耦合是指在一个软件结构内不同模块之间互连程度的度量,
而低耦合就是指在设计一个软件时,作为一个完整的软件系统,对于各种模块与模块之间应尽可能的是其独立的存在,让每个模块尽可能的独立完成其特定的功能。
如果某两个模块间的关系比较复杂的话,最好首先考虑进一步的模块划分。这样有利于修改和组合。
所谓内聚是指一个模块内各个元素彼此结合的紧密程度。而高内聚就是指在一个模块的内部,应该尽可能的让每个元素都紧密的相连。
也就是充分利用每一个元素的功能,各施所能,以最终实现某个功能。
二、聚合、继承以及组合
继承关系即isa 关系,子类继承父类的属性方法;比如:我 is a 人;再比如菱形、圆形和方形都是形状的一种,那么他们都应该从形状类继承而不是聚合/组合关系。
聚合/组合关系即has a关系,两个对象之间是整体和部分的关系;比如:我 has a 头;再比如电脑是由显示器、CPU、硬盘这些类聚合成电脑类,而不是从电脑类继承。
聚合:表示两个对象之间是整体和部分的弱关系,部分的生命周期可以超越整体。如电脑和鼠标;
组合:表示两个对象之间是整体和部分的强关系,部分的生命周期不能超越整体,或者说不能脱离整体而存在。组合关系的“部分”,是不能在整体之间进行共享的。如人和眼睛的关系;不过,如果你要说,眼睛可以移植,是不是说可以脱离人而存在,它们就不是组合关系了?其实,UML中对象的关系都是在相应的软件环境或实际场景下定义的,这里区别聚合和组合的关系。关键还是在于它们之中整体和部分的关系强、弱,以及它们之间的依附关系。如果刚才说眼睛可以移植给别人,那你也可以把它认为是聚合,这都要结合实际场景来说明。
三、你是如何理解干净的代码(Clean Code)与技术负债(Technical Debt)的。
技术负债比喻由漫无计划的软件架构,或者匆忙的软件开发引起的后果。又称为设计负债。
四、重构技巧
1. 提取类/抽离方法:正如上面提到的,像“臃肿的类”(一个类提供了本该有几个类提供的功能)这种代码异味应该将原有类中的方法和属性移动到适当数目的新类中去。旧类中对应新类的方法和属性应该被移除。另外,有时候一些类过于臃肿是因为它包含了被其他类使用本应该是其他类的成员方法的成员方法。这些方法也应该被迁移到合适的类中。
2. 提取方法:像上面提到的“过长的方法”这种代码异味可以通过从旧方法中提取代码到一个或多个新方法中消除。
3. 分离条件:许多时候,一个方法很长是因为包含好几个分支语句(if-else)。这些分支条件可以被提取和移动到几个单独的方法中。这确实能大大改善代码可读性和可理解性。
4. 引入参数对象/保留全局对象:在我做代码审查时发现另外一个很常见的情况 - 好几个参数被传入方法。问题主要与需要从已有方法中增加或者移除一个方法参数有关。在这种场景,建议将相关方法参数组成一个对象(引入参数对象),让方法传递这些对象而不是每个单独的参数。
5. 用符号常量替换魔法数字:对于有意义的并且到处被使用的字面常量,应该为它们分配一个命名常量。这能大大增强代码可读性和可理解性。
6. 重命名方法:正如上面提到的,模糊不清的方法名会影响代码的可使用性。这些模糊不清的名称应该重命名为有意义的可能与业务术语有关的名称,来帮助开发者通过业务上下文更好地理解代码。这很需要技巧并且要求开发者与业务专家一起协作来理清代码需要满足的业务需求。有趣的是,这种重构方法看起来似乎非常容易理解,但是常常被许多开发者忽视,虽然在Eclipse这种IDE的refactor菜单项中经常出现这一项。
五、SOLID 原则
SRP |
The Single Responsibility Principle |
单一责任原则 |
OCP |
The Open Closed Principle |
开放封闭原则 |
LSP |
The Liskov Substitution Principle |
里氏替换原则 |
DIP |
The Dependency Inversion Principle |
依赖倒置原则 |
ISP |
The Interface Segregation Principle |
接口分离原则 |
六、设计模式
从广义角度讲设计模式是可解决一类软件问题并能重复使用的设计方案;
从狭义角度讲设计模式是对被用来在特定场景下解决一般设计问题的类和相互通信的对象的描述,是在类和对象的层次描述的可重复使用的软件设计问题的解决方案.
模式体现的是程序整体的构思,也会出现在分析或者是概要设计阶段, 包括创建型模式、结构型模式和行为型模式.
模式的核心思想是通过增加抽象层,把变化部分从那些不变部分里分离出来.
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
总体来说设计模式分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
八、设计原则
1.KISS 原则是用户体验的高层境界,简单地理解这句话,就是要把一个产品做得连白痴都会用,因而也被称为“懒人原则”。换句话说来,”简单就是美“。KISS 原则源于 David Mamet(大卫马梅)的电影理论,后来被逐渐延伸扩展到其他领域。
2、YAGNI原则
YAGNI是 You aren'tgonna need it 的缩写,意思是"你不会需要它"。
这是"极限编程"提倡的原则,指的是你自以为有用的功能,实际上都是用不到的。因此,除了最核心的功能,其他功能一概不要部署,这样可以大大加快开发。
它背后的指导思想,就是尽可能快、尽可能简单地让软件运行起来(do thesimplest thing that could possibly work)
3、ROT原则
ROT是"Rule ofthree"的缩写,称为"三次原则",指的是某个功能出现第三次时才进行"抽象化"。
这样的好处是:
(1)省事。如果一种功能只有一到两个地方会用到,就不需要在"抽象化"上面耗费时间了。
(2)容易发现模式。"抽象化"需要找到问题的模式,问题出现的场合越多,就越容易看出模式,从而可以更准确地"抽象化"
4、DRY原则 DRY是 Don't repeat yourself 的缩写,意思是"不要重复自己"。 它的涵义是,系统的每一个功能都应该有唯一的实现。也就是说,如果多次遇到同样的问题,就应该抽象出一个共同的解决方法,不要重复开发同样的功能。
九、反模式
1.结构反模式
2.方法反模式
3.设计反模式
4.编程反模式
在你有足够的信息能确定在哪优化、如何优化之前,就展开优化。
花大量时间来辩论和决定琐碎、太主观的问题的这种趋势。
对问题的过度分析,阻碍了行动和进展。
上帝类是控制很多其它类,以及有很多依赖类,也就有更大的责任。
认为更多的类必然使得设计更加复杂,导致对新增类或把大类分解为一些小类感到恐惧。
复杂的软件系统趋势在于重实现它们所运行的平台的特点,或平台所使用的语言,通常都比较烂。
使用未命名的数字或字符串字面量,而不是在代码里命名为常量。
严格地依靠数字来做决定。
无用类本身没有真正的责任,经常用来指示调用另一个类的方法或增加一层不必要的抽象层。
十 排序
排序算法 |
平均时间复杂度 |
冒泡排序 |
O(n2) |
选择排序 |
O(n2) |
插入排序 |
O(n2) |
希尔排序 |
O(n1.5) |
快速排序 |
O(N*logN) |
归并排序 |
O(N*logN) |
堆排序 |
O(N*logN) |
基数排序 |
O(d(n+r)) |