单一职责原则 基本介绍
对类来说的,即一个类应该只负责一项职责。如类A负责两个不同职责:职责1,职责2。 当职责1需求变更而改变A时,可能造成职责2执行错误, 所以需要将类A的粒度分解为A1, A2
应用实例
以水果类为例
方案一
在方式1中的eat方法中,违反了单一职责原则
解决方案非常简单,根据水果的方法不同,分解成不同的类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class SingleResposibility01 { public static void main (String[] args) { Fruit fruit = new Fruit(); fruit.eat("苹果" ); fruit.eat("葡萄" ); fruit.eat("菠萝" ); } } class Fruit { public void eat (String fruit) { System.out.println("正在红色的" + fruit); } }
方案二
遵守单一职责原则
但是这样做的改动很多大,即将类的分解同时更改客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class SingleResponsibility02 { public static void main (String[] args) { RedFriut redFriut = new RedFriut(); redFriut.eat("苹果" ); GreenFruit greenFruit = new GreenFruit(); greenFruit.eat("葡萄" ); YellowFruit yellowFruit = new YellowFruit(); yellowFruit.eat("菠萝" ); } } class RedFriut { public void eat (String fruit) { System.out.println("正在红色的" + fruit); } } class GreenFruit { public void eat (String fruit) { System.out.println("正在绿色的" + fruit); } } class YellowFruit { public void eat (String fruit) { System.out.println("正在黄色的" + fruit); } }
方案三
这种修改方法没有对原来的类做大的修改,只是增加方法
这里虽然没有在类这个级别上遵守单一职责原则,但在方法级别上仍然是遵守单一职责原则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class SingleResposibility03 { public static void main (String[] args) { MyFruit fruit = new MyFruit(); fruit.eatRedFruit("苹果" ); fruit.eatGreenFruit("葡萄" ); fruit.eatYellowFruit("菠萝" ); } } class MyFruit { public void eatRedFruit (String Fruit) { System.out.println("正在吃红色的" + Fruit); } public void eatGreenFruit (String Fruit) { System.out.println("正在吃绿色的" + Fruit); } public void eatYellowFruit (String Fruit) { System.out.println("正在吃黄色的" + Fruit); } }
单一职责原则注意事项和细节
降低类的复杂度,一个类只负责一项职责。
提高类的可读性,可维护性
降低变更引起的风险
通常情况下, 我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违反单一职责原则;只有类中方法数量足够少,可以在方法级别保持单一职责原则
接口隔离原则 应用实例
类A通过接口Interface1依赖类C, 类B通过接口Interface1依赖类D, 请编写代码完成此应用实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 interface interface1 { public void method1 () ; public void method2 () ; public void method3 () ; public void method4 () ; public void method5 () ; } class C implements interface1 { @Override public void method1 () { System.out.println("C 实现了接口1的方法1" ); } @Override public void method2 () { System.out.println("C 实现了接口1的方法2" ); } @Override public void method3 () { System.out.println("C 实现了接口1的方法3" ); } @Override public void method4 () { System.out.println("C 实现了接口1的方法4" ); } @Override public void method5 () { System.out.println("C 实现了接口1的方法5" ); } } class D implements interface1 { @Override public void method1 () { System.out.println("D 实现了接口1的方法1" ); } @Override public void method2 () { System.out.println("D 实现了接口1的方法2" ); } @Override public void method3 () { System.out.println("D 实现了接口1的方法3" ); } @Override public void method4 () { System.out.println("D 实现了接口1的方法4" ); } @Override public void method5 () { System.out.println("D 实现了接口1的方法5" ); } } class A { public void depend1 (interface1 i) { i.method1(); } public void depend2 (interface1 i) { i.method2(); } public void depend3 (interface1 i) { i.method3(); } } class B { public void depend1 (interface1 i) { i.method1(); } public void depend4 (interface1 i) { i.method4(); } public void depend5 (interface1 i) { i.method5(); } }
应传统方法的问题和使用接口隔离原则改进
类A通过接口Interface1依赖类B,类C通过接口Interface1依赖类D,如果接口 Interface1对于类A和类B来说不是最小接口,那么类C和类D必须去实现他们不 需要的方法
将接口Interface1拆分为独立的几个接口,类A和类B分别与他们需要的接口建立 依赖关系。也就是采用接口隔离原则
接口Interface1中出现的方法,根据实际情况拆分为三个接口
代码实现1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 public class Segregation { public static void main (String[] args) { A a = new A(); C c = new C(); a.depend1(c); a.depend2(c); a.depend3(c); B b = new B(); D d = new D(); b.depend1(d); b.depend4(d); b.depend5(d); } } interface interface1 { public void method1 () ; } interface interface2 { public void method2 () ; public void method3 () ; } interface interface3 { public void method4 () ; public void method5 () ; } class C implements interface1 ,interface2 { @Override public void method1 () { System.out.println("C 实现了接口1的方法1" ); } @Override public void method2 () { System.out.println("C 实现了接口2的方法2" ); } @Override public void method3 () { System.out.println("C 实现了接口2的方法3" ); } } class D implements interface1 ,interface3 { @Override public void method1 () { System.out.println("D 实现了接口1的方法1" ); } @Override public void method4 () { System.out.println("D 实现了接口3的方法4" ); } @Override public void method5 () { System.out.println("D 实现了接口3的方法5" ); } } class A { public void depend1 (interface1 i) { i.method1(); } public void depend2 (interface2 i) { i.method2(); } public void depend3 (interface2 i) { i.method3(); } } class B { public void depend1 (interface1 i) { i.method1(); } public void depend4 (interface3 i) { i.method4(); } public void depend5 (interface3 i) { i.method5(); } }
依赖倒转原则 基本介绍
依赖倒转原则(Dependence Inversion Principle)是指: 1) 高层模块不应该依赖低层模块,二者都应该依赖其抽象 2) 抽象不应该依赖细节,细节应该依赖抽象 3) 依赖倒转(倒置)的中心思想是面向接口编程 4) 依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在java中,抽象指的是接口或抽象类,细节就是具体的实现类 5) 使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成
应用实例
接口传递
构造方法传递
setter方法传递
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 public class DInversion { Food food; public static void main (String[] args) { Fruit fruit = new Fruit(); Operation operation = new Operation(); operation.operate(fruit); Operation operation2 = new Operation(new Vegetable()); operation2.operate(); Operation operation3 = new Operation(); operation3.setFood(fruit); operation3.operate(); } } class Operation { private Food food; public Operation () { } public Operation (Food food) { this .food = food; } public void setFood (Food food) { this .food = food; } public void operate (Food food) { food.eat(); } public void operate () { food.eat(); } } interface Food { public void eat () ; } class Vegetable implements Food { @Override public void eat () { System.out.println("吃蔬菜..." ); } } class Fruit implements Food { @Override public void eat () { System.out.println("吃水果.." ); } }
依赖转换原则的注意事项和细节
1) 低层模块尽量都要有抽象类或接口,或者两者都有,程序稳定性更好. 2) 变量的声明类型尽量是抽象类或接口, 这样我们的变量引用和实际对象间,就存在一个缓冲层,利于程序扩展和优化 3) 继承时遵循里氏替换原则
里氏替换原则 OO中的继承性的思考和说明
1) 继承包含这样一层含义:父类中凡是已经实现好的方法, 实际上是在设定规范和契约,虽然它不强制要求所有的子类必须遵循这些契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏。 2) 继承在给程序设计带来便利的同时,也带来了弊端。比如使用继承会给程序带来侵入性,程序的可移植性降低,增加对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能产生故障 3) 问题提出:在编程中,如何正确的使用继承? => 里氏替换原则
基本介绍
1) 里氏替换原则 (Liskov Substitution Principle) 在1988年,由麻省理工学院的以为姓里的女士提出的。 2) 如果对每个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型T2类型T1的子类型。换句话说,所有引用基类的地方必须能透明地使用其子类的对象。 3) 在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法 4) 里氏替换原则告诉我们,继承实际上让两个类耦合性增强了, 在适当的情况下 ,可以通过 聚合,组合,依赖 来解决问题。
一个程序引出问题思考 解决方法
1) 我们发现原来运行正常的相减功能发生了错误。原因就是类B无意中重写了父类的方法,造成原有功能出现错误。在实际编程中,我们常常会通过重写父类的方法完成新的功能,这样写起来虽然简单,但整个继承体系的复用性会比较差。特别是运行多态比较频繁的时候 2) 通用的做法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖,聚合,组合等关系代替
开闭原则 基本介绍
1) 开闭原则(Open Closed Principle) 是编程中最基础、最重要的设计原则 2) 一个软件实体如类,模块和函数应该对扩展开放(对提供方), 对修改关闭(对使用方)。 用抽象构建框架,用实现扩展细节。 ( 这里的使用放是指调用 ) 3) 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。 4) 编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则
看下面一段代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public class Test { public static void main (String[] args) { GraphicEditor graphicEditor = new GraphicEditor(); graphicEditor.drawCircle(new Circle()); graphicEditor.drawRectangle(new Rectangle()); } } class GraphicEditor { public void drawShape (Shape s) { if (s.m_type == 1 ) drawRectangle(s); else if (s.m_type == 2 ) drawCircle(s); } public void drawRectangle (Shape r) { System.out.println("矩形" ); } public void drawCircle (Shape r) { System.out.println("圆形" ); } } class Shape { int m_type; } class Rectangle extends Shape { Rectangle() { super .m_type = 1 ; } } class Circle extends Shape { Circle() { super .m_type = 2 ; } }
优缺点
1) 优点是比较好理解,简单易操作。 2) 缺点是违反了设计模式的ocp原则,即对扩展开放(提供方),对修改关闭(使用方)。即当我们给类增加新功能的时候,尽量不修改代码,或者尽可能少修改代码. 3) 比如我们这时要新增加一个图形种类 三角形,我们需要做如下修改, 修改的地方较多
代码改进改进 改进的思路分析 思路: 把创建Shape类做成抽象类,并提供一个抽象的draw方法,让子类去实现即可,这样我们有新的图形种类时,只需要让新的图形类继承Shape,并实现draw方法即可, 使用方的代码就不需要修 -> 满足了开闭原则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 public class OCP { public static void main (String[] args) { GraphicEditor graphicEditor = new GraphicEditor(); graphicEditor.drawShape(new Rectangle()); graphicEditor.drawShape(new Triangle()); } } class GraphicEditor { public void drawShape (Shape s) { s.draw(); } } abstract class Shape { int m_type; public abstract void draw () ; } class Rectangle extends Shape { Rectangle() { super .m_type = 1 ; } @Override public void draw () { System.out.println("画矩形" ); } } class Circle extends Shape { Circle() { super .m_type = 2 ; } @Override public void draw () { System.out.println("画圆" ); } } class Triangle extends Shape { public Triangle () { super .m_type = 3 ; } @Override public void draw () { System.out.println("画三角形" ); } }
迪米特法则 基本介绍
1) 一个对象应该对其他对象保持最少的了解 2) 类与类关系越密切,耦合度越大 3) 迪米特法则(Demeter Principle)又叫最少知道原则 ,即一个类对自己依赖的类知道的越少越好 。也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的public 方法,不对外泄露任何信息 4) 迪米特法则还有个更简单的定义:*只与直接的朋友通信 * 5) 直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖,关联,组合,聚合等。其中,我们称出现成员变量,方法参数,方法返回值 中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变量的形式出现在类的内部
应用实例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 public class Demeter { public static void main (String[] args) { new PoisonousAirMaking().make(new GeneralAirMaking()); } } class GeneralAirMaking { private ArrayList<GeneralAir> list = new ArrayList<>(); public ArrayList<GeneralAir> retrieve () { list.add(new GeneralAir("氧气" )); list.add(new GeneralAir("二氧化碳" )); list.add(new GeneralAir("氮气" )); return list; } } class PoisonousAirMaking { private ArrayList<PoisonousAir> list = new ArrayList<>(); public ArrayList<PoisonousAir> retrieve () { list.add(new PoisonousAir("一氧化碳" )); list.add(new PoisonousAir("氨气" )); list.add(new PoisonousAir("二氧化硫" )); return list; } public void make (GeneralAirMaking sub) { retrieve(); for (PoisonousAir paAir:list) { System.out.println("有毒气体--" +paAir.getType()); } System.out.println("---" ); ArrayList<GeneralAir> list2 = sub.retrieve(); for (GeneralAir gAir: list2) { System.out.println("普通气体--" + gAir.getType()); } } } class GeneralAir { private String type; public GeneralAir (String type) { super (); this .type = type; } public String getType () { return type; } public void setType (String type) { this .type = type; } } class PoisonousAir { private String type; public PoisonousAir (String type) { super (); this .type = type; } public String getType () { return type; } public void setType (String type) { this .type = type; } }
应用实例改进 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 public class Demeter { public static void main (String[] args) { new PoisonousAirMaking().make(new GeneralAirMaking()); } } class GeneralAirMaking { private ArrayList<GeneralAir> list = new ArrayList<>(); public ArrayList<GeneralAir> retrieve () { list.add(new GeneralAir("氧气" )); list.add(new GeneralAir("二氧化碳" )); list.add(new GeneralAir("氮气" )); return list; } public void make () { retrieve(); for (GeneralAir gAir: list) { System.out.println("普通气体--" + gAir.getType()); } } } class PoisonousAirMaking { private ArrayList<PoisonousAir> list = new ArrayList<>(); public ArrayList<PoisonousAir> retrieve () { list.add(new PoisonousAir("一氧化碳" )); list.add(new PoisonousAir("氨气" )); list.add(new PoisonousAir("二氧化硫" )); return list; } public void make (GeneralAirMaking sub) { retrieve(); for (PoisonousAir paAir:list) { System.out.println("有毒气体--" +paAir.getType()); } System.out.println("---" ); sub.make(); } } class GeneralAir { private String type; public GeneralAir (String type) { super (); this .type = type; } public String getType () { return type; } public void setType (String type) { this .type = type; } } class PoisonousAir { private String type; public PoisonousAir (String type) { super (); this .type = type; } public String getType () { return type; } public void setType (String type) { this .type = type; } }
迪米特法则注意事项和细节
1) 迪米特法则的核心是*降低类之间的耦合 * 2) 但是注意:由于每个类都减少了不必要的依赖,因此迪米特法则只是要求降低类间(对象间)耦合关系, 并不是要求完全没有依赖关系
合成复用原则(Composite Reuse Principle) 基本介绍
原则是尽量使用合成/聚合的方式,而不是使用继承