设计模式它是一种代码编程长期发展的经验和解决某种问题的通用的思想,而且中所周知的一套代码方法和理念。也是咱们编写程序的基石。我平常写代码就比如建造房子同样,首先咱们须要搭建好架子。而后根据这个架子慢慢的将整个大厦建起来。一样的,咱们在编写程序的时候,也须要遵循必定的原则和框架,这样咱们写出来的程序才更见健壮,开发起来也会更加省时间,提升代码的可读性,拓展性,重用性,灵活性等,减小开发成本。设计模式原则其实就是程序员在编程时应当遵照的原则,也是设计模式的基础,即设计模式为何要这样设计的缘由。java
设计模式六大原则:程序员
(一)、单一职责原则编程
(二)、接口隔离原则设计模式
(三)、依赖倒置原则框架
(四)、里氏替换原则ide
(五)、开闭原则函数
(六)、迪米特法则工具
下面我将用说明+代码的方式,尽可能地将6大原则说明白。测试
1、单一职责原则this
对于一个类/接口/方式而言之负责一个职责或职能。好比说A类负责两个不一样的职责,职责1和职责2,当职责1发生需求变动而修改时,有可能会形成职责2执行错误,这是后须要将A类拆分为A1和A2两个。这样作的有点:1.下降了类的复杂性。2.提升了类的可读性,由于一个类只负责一个职责,看起来比较有目的性。3.提升系统的可维护性,下降了当需求变动时修改程序带来的风险。可是,若是一位的最求单一职责原则,有时候可能会形成类爆炸的问题,因此使用时须要谨慎的看待这一点,不过,接口和方法必需要遵照这一原则。
应用实例
咱们已交通工具为例,每种交通工具的出行方式都有可能不同,好比飞机是在天上飞的,汽车在公路上跑,轮船在水上航行。因此咱们能够用如下方式来实现:
方式1:
public class Test { public static void main(String[] args) { Vehicle vehicle = new Vehicle(); vehicle.run("汽车"); vehicle.run("飞机"); vehicle.run("轮船"); } } class Vehicle { public void run(String vehicle) { System.out.println(vehicle + " 在公路上跑"); } }
//运行结果
汽车 在公路上跑
飞机 在公路上跑
轮船 在公路上跑
方式1中run方法既负责在公路上跑,也负责在天上飞,也能够在水上航行,很显然违反了单一职责原则。解决的方法:将每种出行方式单独作成一个类,每一个类中都有各自的运行方式,因此咱们引出了方式2
方式2:
public class SingleResponsible2 { public static void main(String[] args) { RoadVehicle roadVehicle = new RoadVehicle(); roadVehicle.run("汽车"); AirVehicle airVehicle = new AirVehicle(); airVehicle.run("飞机"); WaterVehicle waterVehicle = new WaterVehicle(); waterVehicle.run("邮轮"); } } class RoadVehicle{ public void run(String vehicle) { System.out.println(vehicle + " 在公路上跑"); } } class AirVehicle{ public void run(String vehicle) { System.out.println(vehicle + " 在天上飞 "); } } class WaterVehicle{ public void run(String vehicle) { System.out.println(vehicle + " 在水上航行 "); } }
方式2遵循了单一职责原则,每一个类的run方法都只负责各自的类型。可是,咱们能够看到这样作须要很大的改动,对于客户端也须要改动很大。所以,咱们能够考虑第三种方式,改动Vehicle类型。
方式3:
public class SimgleResponsible3 {
public static void main(String[] args) {
Vehicle vehicle = new Vehicle();
vehicle.airRun("飞机");
vehicle.roadRun("汽车");
vehicle.waterRun("邮轮");
}
}
class Vehicle{
public void roadRun(String vehicle) {
System.out.println(vehicle + "在路上跑");
}
public void airRun(String vehicle) {
System.out.println(vehicle + " 在天空中飞");
}
public void waterRun(String vehicle) {
System.out.println(vehicle + " 水上航行 ");
}
}
这种方式原来的类修改的地方比较少,只是在原来的基础上添加的不一样的方法。这种方式虽然在类型不遵循单一职责原则,可是在方式是遵循了这个原则的。
2、接口隔离原则
客户端不该该依赖它不须要的接口;一个类对另外一个类的依赖应该创建在最小的接口上(Clients should not be forced to depend upon interfaces that they don’t use.)。
类间的依赖关系应该创建在最小的接口上(The dependency of one class to another one should depend on the smallest possible interface)。
问题:类A经过接口I依赖类B,类C经过接口I依赖类D,若是接口I对于类A和类B来讲不是最小接口,则类B和类D必须去实现他们不须要的方法。接口I中有op1,op2,op3,op4和op5这5个方法,类B和类D都实现了接口I,可是类A只须要类B的op1,op2和op3这3个方法,类C只须要类D的op1,op4和op5这3个方法。
在没有遵循接口隔离原则,是这样设计的:
public interface Interface1 { void op1(); void op2(); void op3(); void op4(); void op5(); }
public class A { private Interface1 interface1; public A(Interface1 interface1) { this.interface1 = interface1; } public void depend1() { interface1.op1(); } public void depend2() { interface1.op2(); } public void depend3() { interface1.op3(); } } public class B implements Interface1 { @Override public void op1() { System.out.println("op1"); } @Override public void op2() { System.out.println("op2"); } @Override public void op3() { System.out.println("op3"); } @Override public void op4() { } @Override public void op5() { } } public class C { public C(Interface1 interface1) { this.interface1 = interface1; } private Interface1 interface1; public void depend1() { interface1.op1(); } public void depend4() { interface1.op4(); } public void depend5() { interface1.op5(); } } public class D implements Interface1 { @Override public void op1() { System.out.println("op1"); } @Override public void op2() { System.out.println("op2"); } @Override public void op3() { System.out.println("op3"); } @Override public void op4() { System.out.println("op4"); } @Override public void op5() { System.out.println("op5"); } }
public class Test { public static void main(String[] args) { Interface1 b = new B(); Interface1 d = new D(); A a = new A(b); C c = new C(d); a.depend1(); a.depend2(); a.depend3(); c.depend1(); c.depend4(); c.depend5(); } }
从上面的代码中能够看到,A类经过接口1依赖的B类,可是A类只用到了B的1,2和3的方法,4和5是无用的。一样的,C类只用到了D类的1,4和5的方法。这样就违背了接口隔离的原则。那么接下来就使用接口隔离的原则改进上面代码;
分析:A类和C类都使用到了op1,所以咱们能够将op1单独抽奖成一个接口1,而后把A类依赖的op2和op3抽象成接口2,C类依赖的op4和op5抽象成接口3,而后B类实现接口1和接口2,D类实现接口1和接口3,A类依赖B类,C类依赖D类,这样就能够将不用的接口隔离开,代码以下:
public interface Interface1 { void op1(); } public interface Interface2 { void op2(); void op3(); } public interface Interface3 { void op4(); void op5(); } public class A { public void depend1(Interface1 interface1) { interface1.op1(); } public void depend2(Interface2 interface2) { interface2.op2(); } public void depend3(Interface2 interface2) { interface2.op3(); } } public class B implements Interface1,Interface2 { @Override public void op1() { System.out.println("B 类实现了 Interface1"); } @Override public void op2() { System.out.println("B 类实现了Interface2"); } @Override public void op3() { System.out.println("B 类实现了Interface2"); } } public class C { public void depend1(Interface1 interface1) { interface1.op1(); } public void depend4(Interface3 interface3) { interface3.op4(); } public void depend5(Interface3 interface3) { interface3.op5(); } } public class D implements Interface1,Interface3 { @Override public void op1() { System.out.println("D 类实现了 Interface1"); } @Override public void op4() { System.out.println("D 类实现了Interface3"); } @Override public void op5() { System.out.println("D 类实现了Interface3"); } } public class Test { public static void main(String[] args) { A a = new A(); a.depend1(new B()); a.depend2(new B()); a.depend3(new B()); C c = new C(); D d = new D(); c.depend1(d); c.depend4(d); c.depend5(d); } }
接口隔离使用注意事项:使用过程咱们须要注意把控接口 的细粒度,过分的最求接口隔离,会致使接口数量爆炸,系统中接口泛滥,不利于系统的维护。接口也不能太大,违背了接口隔离的原则,灵活性比较差。适度的控制接口的细粒度,不只让接口变得更加灵活,并且可以下降系统的耦合度等
3、依赖倒置原则
高层模块不该该依赖底层模块,二者都应该依赖抽象,抽象不该依赖具体,具体应该要依赖于抽象,其核心思想就是面向接口编程。
依赖关系传递的三种方式:
一、接口传递;二、构造方法传递;三、setter方式传递
应用案例:
有一天小王的领导要小王对项目中的商品按照价格从高到低排序,小王接到任务后当即开始写代码了,并且很快就写出来:
public class Sort { public void doSort() { System.out.println("sort by price"); } }
public class Item { public void sort() { new Sort().doSort(); } }
public class Client { public static void main(String[] args) { Item item = new Item(); item.sort(); } }
小王通过测试后,可以运行无误,因而提交了代码。可是,随着需求的变动,领导要求小王添加按照销量排序商品。小王接到任务后又开始敲代码了:
public class SaleSort { public void doSort() { System.out.println("sort by sale amount"); } }
public class Item { public void sort(int type) { if (type == 0) { new Sort().doSort(); }else if (type == 1) { new SaleSort().doSort(); } } }
public class Client { public static void main(String[] args) { Item item = new Item(); item.sort(1); } }
小王经过加班加点后,终于实现了领导要求了,不只能够按照价格排序,也能够按照销量来排序。忽然有一天小王的领导跟他说,如今要添加多一种排序方式,小王听到后心里是崩溃的,内心都有删库跑路的想法了。
问题分析:小王的代码中,每次要添加新的排序方式的时候,都须要修改商品类,而且也须要修改客户端,因此修改量很大,在修改的过程当中很容易出现问题,影响其余的功能。这种设计方式就是违反了依赖倒置的原则,高层模块依赖低层模块。
代码改进:
public interface ISort { void doSort(); }
public class PriceSort implements ISort{ @Override public void doSort() { System.out.println("sort by price"); } }
public class SaleSort implements ISort{ @Override public void doSort() { System.out.println("sort by sale amount"); } }
public class Item { private ISort sort; public Item(ISort sort) { this.sort = sort; } public void sort() { sort.doSort(); } public void setSort(ISort sort) { this.sort = sort; } }
public class Client { public static void main(String[] args) { ISort sort = new PriceSort(); Item item = new Item(sort); item.sort(); sort = new SaleSort(); item.setSort(sort); item.sort(); } }
代码分析:重构了代码以后,商品来不在依赖具体的类,而是依赖了ISort接口,如今无论领导须要添加什么样的排序方式,只须要新增一个ISort的实现类就能够了,而后在客户端只须要简单的修改一点能够实现新增排序方式,对原来的代码几乎不用修改。重构后的代码变得更具灵活性,可拓展性。
4、里氏替换原则
定义1:若是对每个类型为 T1的对象 o1,都有类型为 T2 的对象o2,使得以 T1定义的全部程序 P 在全部的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型。
定义2:全部引用基类的地方必须能透明地使用其子类的对象。
继承包含这样一层含义:父类中凡是已经实现好的方法(相对于抽象方法而言),其实是在设定一系列的规范和契约,虽然它不强制要求全部的子类必须听从这些契约,可是若是子类对这些非抽象方法任意修改,就会对整个继承体系形成破坏。而里氏替换原则就是表达了这一层含义。
继承做为面向对象三大特性之一,在给程序设计带来巨大便利的同时,也带来了弊端。好比使用继承会给程序带来侵入性,程序的可移植性下降,增长了对象间的耦合性,若是一个类被其余的类所继承,则当这个类须要修改时,必须考虑到全部的子类,而且父类修改后,全部涉及到子类的功能都有可能会产生故障。
应用案例:
public class A { public int func1(int a,int b) { return a+b; } }
public class B extends A { //这里无心识的重写了父类的方法 public int func1(int a,int b) { return a-b; } public int func2(int a,int b) { return a+b+10; } }
public class Client { public static void main(String[] args) { A a = new A(); System.out.println("10+20=" + a.func1(10,20)); B b = new B(); System.out.println("10+20=" + b.func1(10,20));//这里本意是想10+20 System.out.println("1 + 2 + 10=" + b.func2(1,2)); } }
咱们发现本来运行正常的相减功能发生了错误。缘由就是类B在给方法起名时无心中重写了父类的方法,形成全部运行相减功能的代码所有调用了类B重写后的方法,形成本来运行正常的功能出现了错误。在本例中,引用基类A完成的功能,换成子类B以后,发生了异常。在实际编程中,咱们经常会经过重写父类的方法来完成新的功能,这样写起来虽然简单,可是整个继承体系的可复用性会比较差,特别是运用多态比较频繁时,程序运行出错的概率很是大。若是非要重写父类的方法,比较通用的作法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖、聚合,组合等关系代替。
5、开闭原则
定义:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。
开闭原则是面向对象设计中最基础的设计原则,它指导咱们如何创建稳定灵活的系统。开闭原则多是设计模式六项原则中定义最模糊的一个了,它只告诉咱们对扩展开放,对修改关闭,但是到底如何才能作到对扩展开放,对修改关闭,并无明确的告诉咱们。之前,若是有人告诉我“你进行设计的时候必定要遵照开闭原则”,我会觉的他什么都没说,但貌似又什么都说了。由于开闭原则真的太虚了。
应用实例
public class Shape { int type; } public class Circle extends Shape { Circle() { super.type = 1; } } public class Rectangle extends Shape { Rectangle() { super.type = 2; } } public class Triangle extends Shape { Triangle() { super.type = 3; } } public class GraphicEditor { public void drawShape(Shape shape) { if (shape.type == 1) { drawCircle(); }else if (shape.type == 2) { drawRectangle(); }else { drawTriangle(); } } private void drawRectangle() { System.out.println("画矩形"); } private void drawCircle() { System.out.println("画圆形"); } private void drawTriangle() { System.out.println("画三角形"); } } public class Client { public static void main(String[] args) { GraphicEditor graphicEditor = new GraphicEditor(); graphicEditor.drawShape(new Circle()); graphicEditor.drawShape(new Triangle()); graphicEditor.drawShape(new Rectangle()); } }
上面的示例中优势是比较容易理解,代码简单。可是,违背了开闭原则,即对扩展开发,对修改关闭,也就是当咱们在给类添加新的功能的时候,不要改动现有的代码,或者尽可能少许改动。上面的代码中,若是咱们要添加一个画椭圆形的功能,那么须要修改的地方比较多,在修改的过程当中有可能会影响到已有的功能。所以,咱们须要对现有的代码进行修改,使之符合ocp原则:
思路:将Shape类作成抽象类,并提供一个抽象的方法draw,让子类去实现就能够了,这样咱们在新增新的画图工具是,只须要实现Shape接口,客户端的代码就不须要修改,这样就达到了ocp原则。
public interface Shape { void draw(); } public class GraphicEditor { public void drawShape(Shape shape) { shape.draw(); } } public class Rectangle implements Shape { @Override public void draw() { System.out.println("画矩形"); } } public class Circle implements Shape { @Override public void draw() { System.out.println("画圆形"); } } public class Triangle implements Shape { @Override public void draw() { System.out.println("画三角形"); } } public class Client { public static void main(String[] args) { GraphicEditor graphicEditor = new GraphicEditor(); graphicEditor.drawShape(new Circle()); graphicEditor.drawShape(new Triangle()); graphicEditor.drawShape(new Rectangle()); } }
6、迪米特法则
迪米特法则的定义是:只与你的直接朋友交谈,不跟“陌生人”说话(Talk only to your immediate friends and not to strangers)。其含义是:若是两个软件实体无须直接通讯,那么就不该当发生直接的相互调用,能够经过第三方转发该调用。其目的是下降类之间的耦合度,提升模块的相对独立性。迪米特法则中的“朋友”是指:当前对象自己、当前对象的成员对象、当前对象所建立的对象、当前对象的方法参数等,这些对象同当前对象存在关联、聚合或组合关系,能够直接访问这些对象的方法。
注意事项: