设计模式---六大原则
背景:听说设计模式是进入bat的必经之路。
first、何谓设计模式:
设计模式(design pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。
设计模式的好处&学习目的:
1、为了代码可重用行、让代码更易被他人理解、保证代码的可靠性、使代码编写真正实现工程化;
2、设计模式便于我们维护项目,增强系统的健壮性和可扩展性;
3、设计模式还可以锻炼码农的设计思维、升华代码质量等。
六大指导原则:
程序设计模式有六大基本指导原则,但规则毕竟都是人定的,so我们要灵活遵守、灵活运用这六大原则。
一、开-闭原则:
1、开闭原则顾名思义就是对修改关闭,对扩展开放;
2、开闭原则是六大原则的核心,即我们之后做的任何改变都不需要修改原有的代码,只需要加入一些新的实现即可达到目的;
3、开闭原则也是任何一个系统设计期望达到的理想境界。
二、单一职责原则:
单一职责即每个类都只负责单一的功能,莫要太贪心,除此之外尽量把一个类的功能完善到极致,可以用final来修饰的程度。下面就有一个例子类scientific 从一个文件中读取两个数,并返回两者之和,如此设计可以很明显的看到它们之间的职责问题存在太多耦合了。如图:
上图中显示的scientific 类并没有错,但是我们要是读取文件的地址改变了呢?要是结果改为两者之差、两者之积或两者之商或者是两者之模呢?然后,我们需要复制许多份相同的代码,想想这种设计就不合理;这时我们就要考虑下“单一职责原则”,分离出一个类readfile 来读取数据,再分离出一个类designscientific 对读取到的数据进行处理(加减乘除等)。
1 import java.io.bufferedreader; 2 import java.io.filereader; 3 public class readfile { 4 5 private int numone; 6 private int numtwo; 7 8 public readfile(string path) throws exception { 9 bufferedreader br = new bufferedreader(new filereader(path)); 10 numone = integer.valueof(br.readline()); 11 numtwo = integer.valueof(br.readline()); 12 system.out.println("numone is: "+numone+" && "+"numtwo is: "+numtwo); 13 } 14 15 public int getnumone() { 16 return numone; 17 } 18 19 public int getnumtwo() { 20 return numtwo; 21 } 22 }
如此,将一个类拆为两个,既不会有那么多重复的代码,也多了跟多结果的选择性,也恰恰体现了单一职责原则是我们设计模式最应该遵守的原则之一。
三、里氏替换原则:
里氏替换原则具体指的是一个子类可以在替换掉其父类后正常工作,也就是自类不应该重写父类的方法,其主要作用是规范继承是子类的一些书写规则,主要目的是保持父类方法不被覆盖。
1、子类可以实现父类的抽象方法,但不能覆盖父类的抽象方法(要不重新写给类算了,干嘛要继承);
2、子类中可以增加自己特有的方法;
3、当子类覆盖或实现父类方法时,方法的前置条件(方法的形参)要比其父类方法的输入参数更宽松(如:fun(arraylist, list) vs fun(list, list) );
4、当子类的方法实现父类的抽象方法时,方法的后置条件(方法的返回值)要比其父类更严格。
四、接口隔离原则:
接口隔离原则也叫做接口最小化原则,指的是一个接口拥有的行为应该尽可能的最小。
1、如果设计的时候没有考虑到接口隔离原则,就会出现一个类实现了一个接口但实现类中只有个别的方法实现了其他方法都是空的状况,导致强制实现了不得不实现而又本不该实现的方法,而最终也一直没有调用过此方法,造成资源浪费;
2、比如我们设计一个手机mobile的接口时,就要考虑手机哪些属性时必须的,要让该接口尽量最小最细化,即只要是手机就必须要具备的属性。
没有遵守接口隔离原则的mobile接口设计:
1 public interface mobile { 2 public void call(); //手机可以打电话 3 public void sendmessage(); //手机可以发短信 4 public void wechat(); //手机可以上微信wechat? 5 }
上述mobile接口很明显不是一个手机必须具备的功能属性,那么什么的mobile接口就不是最小接口,因为非智能手机就只可以打电话和发短信,而上微信就是智能手机的专属特性,如此就会有多余的实现方法,可以做如下修改,充分考虑接口隔离原则。
1 public interface smartphone extends mobile{ 2 public void wechat(); //智能手机的接口就可以加入这个方法了 3 }
五、依赖倒置原则:
这个原则描述的是高层模块不该依赖于低层模块,两个模块都应该依赖依赖于抽象,抽象不应该依赖于细节,细节应该依赖于抽象。因为实现都是易变的,只有抽象是稳定的,所以当我们依赖于抽象是,实现的变化就不会影响客户端的调用;就比如上面“单一职责”中的计算器的例子,计算器其实是依赖于数据读取类的,这样设计还是有一些缺陷,因为若是当数据不是在文件里,而是在数据库当中呢?这时候为了不影响现有的代码,只能将readerfile类整个大改,或是新增一个dbreader类,把程序代码中所有使用到readerfile读取到地方替换成dbreader,这样做勉强可以接受,但是仍然达不到可复制化的标准;若是数据读取有的是从数据库、有点是从xml文件、有的是从网络或是从键盘输入读取,这时就有充分考虑依赖倒置原则。
1 public interface reader {
2 //依赖倒置抽象出的一个抽象接口 3 public int geta(); 4 public int getb(); 5 }
依赖倒置原则让我们抽象出一个抽象类或者接口,来表述数据读取行为,然后让上面所有的读取方式所实现的类都实现该接口,客户端方面只使用我们定义好的接口,当我们的实现变化时,我们只需设置下不同的实际类型就ok了,这样设计对于系统的扩展行将会是一个巨大的提升;这样设计的话,计算器就依赖于一个很稳定的抽象接口,之后不论是从哪里读取数据,两个获取数据的方法都不会改变;无论是dbreader、xmlreader、netreader或是outputstreamreader之类的都可以实现reader这个抽象接口,不管是从哪里读取数据都ok,因为我们不需要关心这个,只要可以从reader接口中获得a和b的值就ok了;同时,依赖于抽象也体现了java语言的动态特性。
六、迪米特原则:
迪米特原则也称最小知道原则,就是一个类尽量不应该知道(包括)其他类太多的东西,不要和其他类有太多的交集。该原则的制定的终极目的就是解耦,即将细节全部高内聚于类的内部,其他的类只需知道这个类主要提供的功能就ok了,减少不必要的依赖,高聚合低耦合。