前言
我们都知道面向对象有六大原则,23种设计模式。它们可以指导我们如何写出更加优秀的代码。六大原则是基础,我们面向对象编程应该尽量遵从这六大原则,才能写出优秀的代码。
23种设计模式是前人为我们总结出的解决某一类问题的办法,通过使用这些模式,我们可以更好的解决这一类问题。当然 23 种设计模式的设计前提也是建立在六大原则基础之上的。
目录
六大原则概念
六大原则是面向对象的六大原则,也就是说,我们在编写面向对象语言的时候,只有遵守了这六大原则才能写出真正的面向对象。才能拥有面向对象的思想。我们写的代码符合了这六大原则,有助有提高我们代码的质量,写出可扩展、可维护、可读性好、低耦合、高内聚的代码。
当然不符合这些原则,同样也是可以写代码的,只不过写出的代码质量就没有保证了,只能说勉强算是个代码。这好比做人一样,人也是有原则的,符合了这些原则,那就可以做一个堂堂正正的好人,不符合这些原则,也能活着。但是当不断的触犯原则,最后成了一个没有原则的人了,那么结果显然可见。如果随着程序不断的变大,代码不断的没有原则,那么最终的结果就是你的程序无法进行下一步维护了。
总之我们在写代码的时候要尽量符合这些原则!才能写出高质量代码!
单一职责
单一职责是我们优化代码的第一步
概念
概念:就一个类而言,应该仅有一个引起它变化的原因
概念可能不太好懂,简单来说就是一个类中应该是一组相关性很高的函数、数据的封装。
下面举例子:
public class Activity{
// 请求网络加载
public void requestNet(){
String url = editText.getText();
String parmas = editText.getText();
// 判断是否符合某个条件
if(xx){
}
// 继续判断
if(xxx){
}
.... 等等省略1000行
}
class Adapter{
}
// 数据类
class Data{
}
class Xxx{
}
.....
}
像上面的例子就是一个很好的反例代表,把所有的职责全部放到了 Activity 中,把所有的函数功能都放到了 requestNet
中。这样势必造成 Activity 异常的臃肿,只要一个职责发生变化就能引起 Activity 的变化。比如 Adapter 变化,要去 Activity 中修改,等等都会对 Activity 造成变化。requestNet
函数中的功能不够纯粹,里面又包含了很多其他的功能,也会导致同样的问题。
这也就是概念中提到的,就一个类而言,应该仅有一个引起它变化的原因。
好处
单一职责的好处很明显,让一个类、函数只负责某一项任务或者功能,可以达到很好的复用效果,代码的可读性也会增强,可读性好了,对应的可维护性也会增加。
当然关于职责的划分是一个很抽象的概念,每个人的划分都会不同,单一职责的划分界限并不总是那么清晰,有的时候划分的很细也会带来不方便。这是一个灵活掌握的问题,关键是设计代码的时候有没有考虑到这种思想。
开闭原则
Java 世界里最基础的设计原则,指导我们如何建立一个稳定的、灵活的系统。
概念
软件中的对象(类、模块、函数等)应该对于扩展是开放的,但是对于修改是封闭的。
在编写代码的过程中,不是一成不变的,需求的变化、升级、维护等等都需要对代码进行修改,修改的时候就不可避免地将错误引入原本已经测试过的旧代码中,破坏原有系统。因此,当软件需求发生变化的时候,我们应该优先考虑通过扩展的方式来实现变化,而不是通过修改已有代码来实现。
当然实际开发中扩展和修改是同时存在的。应该尽量少的去修改代码,想法去扩展代码。
《面向对象软件构造》一书中提到这一原则---开闭原则。这一想法认为,程序一旦开发完成,程序中的一个类的实现只应该因错误而被修改,新的或者改变的特性应该通过新建不同的类实现,新建的类可以通过继承的方式来重用原有类。
举个简单的例子:
public class Hello{
BlackPen pen = new BlackPen();
void writeHello(){
pen.write("hello world");
}
}
// Pen 类可以写出字
public class BlackPen{
public void write(String content){
System.out.println("content");
}
}
上面这个程序中我们可以通过 BlackPen
类写出字,有一天需求变了要求写出红色的字。
public class Hello{
BlackPen pen = new BlackPen();
RedPen redPen = new RedPen();
void writeHello(String flag){
switch(flag){
"XXX":
pen.wiite("hello world");
"YYY":
redPen.write("hello world")
}
}
}
// BlackPen 类可以写出黑字
public class BlackPen{
public void write(String content){
System.out.println(content);
}
}
// RedPen 类可以写出红字
public class RedPen{
public void write(String content){
System.out.println(content);
}
}
这样写通过 switch
来判断要调用那一个,如果继续添加其他颜色的笔就继续添加。这样貌似不错。但是试想 Hello 是你提供给别人的一个框架,那么别人想要继续添加可以写出黄色的 Hello Wrold ,是不是就没有办法了,非得让你去修改 Hello 方法才可以,没有了扩展性。
现在优化成
public class Hello{
Pen pen = new BlackPen();
public void setPen(Pen pen){
this.pen = pen;
}
void writeHello(){
pen.write("hello world")
}
}
public interface Pen{
write(String content);
}
// BlackPen 类可以写出黑字
public class BlackPen implement Pen{
public void write(String content){
System.out.println(content);
}
}
// RedPen 类可以写出红字
public class RedPen implement Pen{
public void write(String content){
System.out.println(content);
}
}
这样就可以扩展而不用修改 Hello 内的代码了。
开闭原则,对修改关闭,对扩展开放。并不是说完全的不能修改,比如上面内容,一开始只有一个 BlackPen 的时候,你没有想到扩展,可以那样写,但是随着业务变化,出现了不同的 Pen。这个时候就需要考虑 Pen 要有可扩展性。就不能重复的在 Hello
类中不断去修改了。而是换一种思路,让其变得具有可扩展。
好处
可以使用我们的程序更加稳定,避免修改带来的错误,增加可扩展性。当一个类中的业务不断的发生变化需求,不断的增加业务判断,就需要考虑到扩展性了。
里氏替换原则
构建扩展性更好的系统
概念
所用引用基类的地方必须能透明地使用其子类的对象。
只要父类能出现的地方,子类就可以出现,而且替换为子类也不会产生任何错误或者异常,使用者可能根本就不需要知道是父类还是子类。
里氏替换原则就是依赖于继承、多态这两大特性。
其实就是将依赖变成抽象,不依赖于具体的实现。
比如:
public class Window{
public void show(View child){
child.draw();
}
}
public abstract class View{
public abstract void draw();
public void measure(int width,int height){
// 测量视图大小
}
}
public class Button extends View{
public void draw(){
// 绘制按钮
}
}
public class TextView extends View{
public void draw(){
// 绘制文本
}
}
Window 是依赖于 View 的,是一个抽象类,Window 是依赖于一个抽象,而不是具体的对象。这个时候传入任何 View 的具体对象都是可以的。
好处
提高扩展性,使其不依赖具体的实现,依赖抽象。
依赖倒置原则
让项目拥有变化的能力
概念
依赖倒置原则指代了一种特定的解耦形式,使得高层次的模块不依赖于低层的模块的实现细节,依赖模块被颠倒了。
依赖倒置的关键点:
- 高层模块不应该依赖低层模块,两者应该依赖其抽象
- 抽象不应该依赖细节
- 细节应该依赖抽象
在 Java 语言中,抽象就是指接口或抽象类。两者都是不能直接被实例化的;细节就是实现类,实现接口或者继承抽象类而产生的类就是细节。
高层模块就是调用端,低层模块就是具体的实现类。也就是调用端不要依赖具体的实现类,而是通过依赖抽象的方式。其实就是面向接口编程。
其实和上面里氏替换原则类似
好处
降低耦合性,不依赖具体细节,依赖抽象,提高可扩展性
接口隔离原则
系统有更好的灵活性
概念
客户端不应该依赖它不需要的接口。另一种定义:类间的依赖关系应该建立在最小的接口上。接口隔离原则将非常庞大、臃肿的接口拆分成更小的和更具体的接口,这样客户将会只需要知道他们感兴趣的方法。接口隔离原则的目的是系统解开耦合,从而容易重构、更改和重新部署。
其实就是让一个接口尽可能的小,方法少,使用户使用起来方便。
比如:一个对象实现了多个接口,有个接口是关闭功能,那么当这个对象想要关闭的时候,调用关闭方法就可以了,因为它实现了多个接口,有多个方法,调用的时候就暴露了其他接口函数。这个时候我们仅需要它暴露关闭的接口就可以了,隐藏其他接口信息。
好处
使用起来更加方便灵活
迪米特原则
概念
迪米特原则也称为最少知道原则。一个对象应该对其他对象有最少的了解。通俗地讲,一个类应该对需要耦合或调用的类知道得最少,类的内部如何实现与调用者或者依赖者没有关系,调用者或者依赖者只需要知道它的需要的方法就可以了,其他的可一概不管。类与类之间关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。
迪米特原则还可以解释为:只与直接朋友通信。
也就是说,应该尽可能少的与别的朋友通信,仅与最直接的朋友通信。
两个对象成为朋友的方式有多种:组合、聚合、依赖等等。
好处
降低依赖、使用简单
总结
这六大原则不是相互独立的,而是互相融合,你中有我,我中有你。
单一职责告诉我们要尽量的分离代码,不断的精分代码,不同的模块实现不同的功能。这样不会所有功能都融合在一块,方便阅读、维护代码。
开闭原则、里氏替换原则、依赖倒置原则:本质上都是通过抽象来提高扩展性。不依赖具体的实现而依赖抽象,就会增加很多扩展性,抽象可以有需要不同的实现。
接口隔离原则:和单一职责有类似,就是通过接口细分化,暴露最少的方法。要想有某个功能,只需要实现这个接口就可以了,与其他接口无关。
迪米特原则:尽量依赖更少的类,尽量对外界暴露更少的方法
实现这六大原则主要是通过面向接口编程,面向抽象编程。不依赖具体的实现。每个类都有一个抽象(抽象类、接口)。当高层级模块需要依赖这个类的时候,依赖它的抽象,而不是具体。这个时候就可以灵活的改变其实现了。