一.单一职责原则
单一职责原则对类来说,即一个类应该只负责一项职责。
主要根据不同的角度划分职责,比如,
1、从类的组成,划分为属性操作和行为操作两种。
2、从数据库操作的不同作用,划分为数据库的连接操作和增删改查基本操作。
原例:
class Vehicle{
public void run(String vehicle) {
System.out. println(vehicle + "在公路上跑");} }
public class singleResponsibility {
public static void main(String[] args) {
Vehicle vehicle = new Vehicle();
vehicle.run("摩托车");
vehicle.run("汽车");
vehicle.run("飞机");}
}
原例的分析:
1.在方式1的run方法中,违反了单一职责原则。
2.解决方案:根据交通工具运行方法不同,分解成不同类。
改进:
class Vehicle2 {
public void run(String vehicle) { // 不变
System.out.println(vehicle + "在公路上跑");
}
public void runAir(String vehicle) { // 增加
System.out.println(vehicle + "在天空中飞");
}
public void runWater(String vehicle) { // 增加
System.out.println(vehicle + "在水里游");
}
}
public class Main {
public static void main(String[] args) {
Vehicle2 Vehicle = new Vehicle2();
Vehicle.run("摩托车");
Vehicle.run("汽车");
Vehicle.runAir("飞机");
Vehicle.runWater("轮船");
}
}
改进的分析:
1.这种修改方法没有对原来的类做大的修改,只是增加方法;
2.增加的部分不影响原有部分,降低了变更引起的风险;
3.这里在类这个级别上没有遵守单一职责原则,但是在方法级别上,仍然是遵守单一职责。
二、开放-封闭原则(面向对象设计的最终目标)
一个软件实体如类,模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实现扩展细节。
原例:
class Rectangle {
void draw() {
System.out.println("矩形");
}
}
class Circle {
void draw() {
System.out.println("圆形");
}
}
class Triangle { // 新增三角形——肯定要改
void draw() {
System.out.println("三角形");
}
}
class GraphicDraw {
void drawgraph(int type) {
if (type == 1) {
Rectangle rec = new Rectangle();
rec.draw();
} else if (type == 2) {
Circle c = new Circle();
c.draw();
} else if (type == 3) { // 新增绘制三角形
Triangle t = new Triangle();
t.draw();
}
}
}
public class Main {
public static void main(String[] args) {
GraphicDraw graphicdraw = new GraphicDraw();
graphicdraw.drawgraph(2); // 客户端肯定要改
}
}
原例的分析:
1.优点是比较好理解,简单易操作。
2.缺点是违反了开闭原则(对扩展(提供方)开放,对修(使用方)关闭):需要给类增加新功能的时候,尽量不要修改代码,或者尽可能少修改代码。
3.本例,需要新增一个图形种类时,修改的地方比较多。
改进:
abstract class Shape {
public abstract void draw(); // 抽象方法
}
class GraphicDraw { // 新增绘制图形不需修改此类
void drawgraph(Shape s) {
s.draw();
}
}
class Rectangle extends Shape {
public void draw() {
System.out.println("矩形");
}
}
class Circle extends Shape {
public void draw() {
System.out.println("圆形");
}
}
class Triangle extends Shape { // 新增三角形
public void draw() {
System.out.println("三角形");
}
}
public class Main {
public static void main(String[] args) {
GraphicDraw graphicdraw = new GraphicDraw();
graphicdraw.drawgraph(new Circle());
graphicdraw.drawgraph(new Rectangle());
graphicdraw.drawgraph(new Triangle());
}
}
改进分析:
1.Rectangle和Circle有共同的draw方法,创建抽象Shape类做成基类,并提供抽象的draw方法,让子类去实现,当需要新的图形时,只需要让新的图形继承Shape,并实现draw方法。
2.画图类GraphicDraw不出现具体的类,用抽象类Shape。这样使用方的代码就不需要修改,满足开闭原则。
三.里氏代换原则
子类必须实现父类的抽象方法,但不得重写(覆盖)父类的非抽象(已实现)方法。
原例:
class A {
public int func1(int num1, int num2) {
return num1 - num2;
}
}
class B extends A {
public int func1(int a, int b) {
return a + b;
}
public int func2(int a, int b) {
return func1(a, b) + 9;
}
}
public class Main {
public static void main(String[] args) {
A a = new A();
System.out.println("11-3=" + a.func1(11, 3));
System.out.println("1-8=" + a.func1(1, 8));
System.out.println("-------");
B b = new B();
System.out.println("11-3=" + b.func1(11, 3));
// 这里本意是求出11-3
System.out.println("1-8=" + b.func1(1, 8));
System.out.println("11+3+9=" + b.func2(11, 3));
}
}
原例分析:
原来运行正常的相减功能发生了错误。原因就是类B重写了父类的方法func1,造成原有功能出现错误。
改进:
//创建-个更加基础的基类
class Base {
//把更加基础的方法和成员写到Base类
}
//A类
class A extends Base {
//返回两个数的差
public int func1(int num1, int num2) {
return num1 - num2;
}
}
class B extends Base {
A a = new A();
public int func1(int a, int b) {
return a + b;
}
public int func2(int a, int b) {
return func1(a, b) + 9;
}
public int func3(int a, int b) {
return this.a.func1(a, b);
}
}
public class Main {
public static void main(String[] args) {
A a = new A();
System.out.println("11-3=" + a.func1(11, 3));
System.out.println("1-8=" + a.func1(1, 8));
System.out.println("-------");
B b = new B();
// 因为B类不再继承A类,因此调用者,不会再func1是求减法
// 调用完成的功能就会很明确
System.out.println("11+3=" + b.func1(11, 3));// 这里本意是求出11+3
System.out.println("1+8=" + b.func1(1, 8));// 1+8
System.out.println("11+3+9=" + b.func2(11, 3));
// 使用组合仍然可以使用到A类相关方法
System.out.println("11-3=" + b.func3(11, 3));// 这里本意是求出11-3
}
}
四.依赖倒转原则
1)高层模块不应该依赖低层模块,二者都应该依赖其抽象。
2)抽象不应该依赖细节,细节应该依赖抽象。
3)依赖倒转(倒置)的中心思想是面向接口编程。
原例:
public class Main {
public static void main(String[] args) {
Person person = new Person();
person.receive(new Email());
}
}
class Email {
public String getInfo() {
return "电子邮件信息: hello world ";
}
}
/*
* Person 类 接受消息,将Email类 作为参数 产生了依赖 如果 参数发生变化,即接受的是微信 或短信 整个方法需要改动
*/
class Person {
public void receive(Email email) {
System.out.println(email.getInfo());
}
}
原例分析:
1.简单,比较容易想到
2.如果我们获取的对象是微信,短信等,则新增类,同时Perons也要增加相应的接收方法。
3.解决:引入一个抽象的接口IReceiver,表示接收者,这样Person类与接口IReceiver发生依赖因为Emai1, WeiXin等属于接收的范围,它们各自实现IReceiver接口,这样就符合依赖倒转原则。
改进:
/*定义接受的接口 */
interface IReciver {
public String getInfo();
}
class Email implements IReciver {
public String getInfo() {
return "电子邮件信息: hello world ";
}
}
class WenXin implements IReciver {
public String getInfo() {
return "微信信息: hello WenXin ";
}
}
/* Person 类 接受消息 将IReciver 接口 作为参数 产生了依赖 */
class Person {
public void receive(IReciver reciver) {
System.out.println(reciver.getInfo());
}
}
public class Main {
public static void main(String[] args) {
Person person = new Person();
person.receive(new Email());
//传入不同实现类 实现不同的接受
person.receive(new WenXin());
}
}
五.合成/聚合复用原则
尽量使用合成/聚合,不要使用类继承达到复用的目的。
如果有类A和类B,类B有方法opertion1()、 opertion2() 和opertion3(),类A需要用类B的方法opertion2() ,如何做?
法1(依赖):
class B{
opertion (A a) {
a. opertion2() ; }
}
法2(聚合):
class B{
A a;
void setA (A a){
a. opertion2() ;} }
法3(组合):
class B{
A a=new A( );
}
六.接口隔离原则
使用多个专门的接口比使用单一的总接口要好。换而言之,
接口尽量细化,接口中的方法尽量少。
场景:
1.类B实现接口Interface1 ,类A通过接口Interface1依赖(使 用)类B,但是只会用到1,2,3方法。
2.类D实现接口Interface1 ,类C通过接口Interface1依赖(使 用)类D,但是只会用到1,4,5方法。
原例:
interface Interface1 {
void operation1();
void operation2();
void operation3();
void operation4();
void operation5();
}
class B implements Interface1 {
public void operation1() {System. out.println("B实现了operation1"); }
public void operation2() {System. out.println("B实现了operation2"); }
public void operation3() {System. out.println("B实现了operation3"); }
public void operation4() {System. out.println("B实现了operation4" ); }
public void operation5() {System. out.println("B实现了operation5"); } }
//类D实现接口Interface1
class D implements Interface1 {
public void operation1() {System. out.println("D实现了operation1"); }
public void operation2() {System. out.println("D实现了operation2"); }
public void operation3() {System. out.println("D实现了operation3"); }
public void operation4() {System. out.println("D实现了operation4" ); }
public void operation5() {System. out.println("D实现了operation5"); } }
//类A通过接口Interface1依赖(使用) 类B,但是只会用到1,2,3方法
class A{
public void depend1(Interface1 i) {
i.operation1();
}
public void depend2(Interface1 i) {
i.operation2();
}
public void depend3(Interface1 i) {
i.operation3();}
}
//类C通过接口Interface1依赖(使用) 类D,但是只会用到1,4,5方法
class C{
public void depend1(Interface1 i) {
i.operation1();
}
public void depend4(Interface1 i) {
i.operation4();
}
public void depend5(Interface1 i) {
i.operation5();}
}
public class Main {
public static void main(String[] args) {
A a=new A();
C c=new C();
//类A 通过接口 依赖类B
a.depend1(new B());
a.depend2(new B());
a.depend3(new B());
//类C 通过接口 依赖类D
c.depend1(new D());
c.depend4(new D());
c.depend5(new D());
} }
原例分析:
类A通过接口Interface1依赖类B,类C通过接口Interface1依赖类D,而接口 Interface1对于类A和类C来说不是最小接口,类B和类D必须去实现它们不需要的方法。
改进:
interface Interface1 {
void operation1();
}
interface Interface2 {
void operation2();
void operation3();
}
interface Interface3 {
void operation4();
void operation5();
}
//类B 实现接口Interface1 ,Interface2的所有方法
class B implements Interface1,Interface2 {
public void operation1() {
System.out.println("B 实现 operation1");
}
public void operation2() {
System.out.println("B 实现 operation2");
}
public void operation3() {
System.out.println("B 实现 operation3");
} }
//类A 通过接口 Interface1,Interface2 依赖 (使用)类B 只会用到方法1,2,3
class A {
public void depend1(Interface1 i) {
i.operation1();
}
public void depend2(Interface2 i) {
i.operation2();
}
public void depend3(Interface2 i) {
i.operation3();
}
}
//类D实现接口Interface1,Interface3 的所有方法
class D implements Interface1, Interface3 {
public void operation1() {
System.out.println("D 实现 operation1");
}
public void operation4() {
System.out.println("D 实现 operation4");
}
public void operation5() {
System.out.println("D 实现 operation5");
}
}
//类C 通过接口 Interface1,Interface3 依赖 (使用)类D 只会用到方法1,4,5
class C {
public void depend1(Interface1 i) {
i.operation1();
}
public void depend4(Interface3 i) {
i.operation4();
}
public void depend5(Interface3 i) {
i.operation5();
} }
public class Main {
public static void main(String[] args) {
A a=new A();
C c=new C();
//类A 通过接口 依赖类B
a.depend1(new B());
a.depend2(new B());
a.depend3(new B());
//类C 通过接口 依赖类D
c.depend1(new D());
c.depend4(new D());
c.depend5(new D());
} }
改进分析:
接口Interface1拆分为独立的几个接口,类A和类C分别与它们需要的接口建立依赖关系。也就是采用接口隔离原则。接口Interface1 中出现的方法,根据实际情况拆分为三个接口。
七.迪米特法则(最少原则)
对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的public方法,不泄露任何信息。即至于直接朋友通信。
原例:
import java.util.ArrayList;
import java.util.List;
//学校总部员工类
class Employee {
private String id;
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
//学院的员工类
class CollegeEmployee{
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
//管理学院员工的管理类
class CollegeManager{
public List<CollegeEmployee> getAllEmployee(){
List<CollegeEmployee> list = new ArrayList();
for(int i = 0; i < 10; i++) {
CollegeEmployee emp = new CollegeEmployee();
emp.setId("学院员工 id = " + i);
list.add(emp);
}
return list;
}
}
//学校管理类
//分析SchoolMangager 类的直接朋友有哪些Employee,CollegeManager
//CollegeEmployee 不是直接朋友而是一个陌生类,这样违背了迪米特法则
class SchoolManager{
public List<Employee> getAllEmployee(){
List<Employee> list = new ArrayList();
for(int i = 0; i < 5; i++) {
Employee emp = new Employee();
emp.setId("学校总部的员工id = " + i);
list.add(emp);
}
return list;
}
//该方法完成输出学校总部和学院员工信息(id)
void printAllEmployee(CollegeManager sub){
//获取到学院员工
List<CollegeEmployee> list1 = sub.getAllEmployee();
System.out.println("-------学院员工---------");
for (CollegeEmployee employee : list1) {
System.out.println(employee.getId());
}
//获取到学校总部的员工
List<Employee> list2 = this.getAllEmployee();
System.out.println("-------学校总部员工---------");
for (Employee employee : list2) {
System.out.println(employee.getId());
}
}
}
public class Main {
public static void main(String[] args) {
//创建了一个SchoolManager对象
SchoolManager schoolManager = new SchoolManager();
//输出学院的员工id 和 学校总部的员工信息
schoolManager.printAllEmployee(new CollegeManager());
}
}
原例分析:
- 这里的CollegeEmployee不是 SchoolManager的直接朋友
- CollegeEmployee 是以局部变量方式出现在SchoolManager
- 违反了迪米特法则
改进:
import java.util.ArrayList;
import java.util.List;
//学校总部员工类
class Employee {
private String id;
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
//学院的员工类
class CollegeEmployee{
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
//管理学院员工的管理类
class CollegeManager{
public List<CollegeEmployee> getAllEmployee(){
List<CollegeEmployee> list = new ArrayList();
for(int i = 0; i < 10; i++) {
CollegeEmployee emp = new CollegeEmployee();
emp.setId("学院员工 id = " + i);
list.add(emp);
}
return list;
}
void printEmployee(){
List<CollegeEmployee> list1 = getAllEmployee();
System.out.println("-------学院员工---------");
for (CollegeEmployee employee : list1) {
System.out.println(employee.getId());
}
}
}
//学校管理类
//分析SchoolMangager 类的直接朋友有哪些Employee,CollegeManager
//CollegeEmployee 不是直接朋友而是一个陌生类,这样违背了迪米特法则
class SchoolManager{
public List<Employee> getAllEmployee(){
List<Employee> list = new ArrayList();
for(int i = 0; i < 5; i++) {
Employee emp = new Employee();
emp.setId("学校总部的员工id = " + i);
list.add(emp);
}
return list;
}
//该方法完成输出学校总部和学院员工信息(id)
void printAllEmployee(CollegeManager sub){
//分析问题
//1. 将输出学院的员工方法,封装到CollegeManager
sub.printEmployee();
//获取到学校总部的员工
List<Employee> list2 = this.getAllEmployee();
System.out.println("-------学校总部员工---------");
for (Employee employee : list2) {
System.out.println(employee.getId());
}
}
}
public class Main {
public static void main(String[] args) {
//创建了一个SchoolManager对象
SchoolManager schoolManager = new SchoolManager();
//输出学院的员工id 和 学校总部的员工信息
schoolManager.printAllEmployee(new CollegeManager());
}
}
由于每个类都减少了不必要的依赖,因此迪米特法则
只是要求降低类间(对象间)耦合关系, 并不是要求完
全没有依赖关系。