七大原则
- 单一职责原则
- 接口隔离原则
- 依赖倒转原则(DI)
- 里氏替换原则
- 开闭原则
- 迪米特法则
- 合成复用原则
单一职责
一轮上一个类只应该负责一个职责, 但可以有多个方法.
好处:
- 降低复杂度
- 降低变更引起的风险
- 提高可读性, 可维护性
- 通常在代码足够简单时在类级别遵循单一职责, 复杂的情况下为较少代码, 在方法级别上遵循单一职责原则.
/**
* 单一职责原则1
* 不遵循单一职责原则
*/
public class SingleResponsibility_1 {
public static void main(String[] args) {
Vehicle vehicle = new Vehicle();
vehicle.run("摩托车");
vehicle.run("汽车");
// 轮船和飞机不能在公路上跑, 违反了单一职责原则
// 解决办法: 分解成不同的交通工具
vehicle.run("轮船");
vehicle.run("飞机");
}
}
// 交通工具类
class Vehicle {
public void run(String vehicle) {
System.out.println(vehicle + " 在公路上运行...");
}
}
/**
* 单一职责原则2
* 1. 遵循单一职责原则
* 2. 花销很大, 有一种交通工具就要创建一个类, 同时要修改客户端
*/
public class SingleResponsibility_2 {
public static void main(String[] args) {
RoadVehicle roadVehicle = new RoadVehicle();
roadVehicle.run("摩托车");
roadVehicle.run("汽车");
AirVehicle airVehicle = new AirVehicle();
airVehicle.run("飞机");
SeaVehicle seaVehicle = new SeaVehicle();
seaVehicle.run("轮船");
}
}
// 路上跑的交通工具
class RoadVehicle {
public void run(String vehicle) {
System.out.println(vehicle + " 在公路上跑...");
}
}
// 天上飞的交通工具
class AirVehicle {
public void run(String vehicle) {
System.out.println(vehicle + " 在天空上飞...");
}
}
// 海里的交通工具
class SeaVehicle {
public void run(String vehicle) {
System.out.println(vehicle + " 在海里游...");
}
}
/**
* 单一职责原则3
* 1. 修改较小
* 2. 虽然没有在类级别上遵循单一职责原则, 但是在方法级别上遵循单一职责原则
*/
public class SingleResponsibility_3 {
public static void main(String[] args) {
Vehicle3 vehicle = new Vehicle3();
vehicle.runRoad("摩托车");
vehicle.runRoad("汽车");
vehicle.runAir("飞机");
vehicle.runSea("轮船");
}
}
// 交通工具
class Vehicle3 {
public void runRoad(String vehicle) {
System.out.println(vehicle + " 在公路上跑...");
}
public void runAir(String vehicle) {
System.out.println(vehicle + " 在天上飞...");
}
public void runSea(String vehicle) {
System.out.println(vehicle + " 在海里游泳...");
}
}
接口隔离原则
一个类不应该依赖它不需要的接口, 即一个类对另一个类的依赖应该建立在最小的接口上.
package com.mine.segregation;
/**
* 接口隔离原则1
* A类通过Interface1接口实现类B, 只会用到1,2,3三个方法
* C类通过Interface1接口实现类D, 只会用到1,4,5三个方法
* 1. 违反接口隔离原则
*/
public class InterfaceSegregation_1 {
}
interface Interface1 {
void operation1();
void operation2();
void operation3();
void operation4();
void operation5();
}
/**
* 通过接口Inteface1依赖B, 但是只会用到1,2,3三个方法
*/
class A {
public void depend1(Interface1 interface1) {
interface1.operation1();
}
public void depend2(Interface1 interface1) {
interface1.operation2();
}
public void depend3(Interface1 interface1) {
interface1.operation3();
}
}
class B implements Interface1 {
@Override
public void operation1() {
System.out.println("B 实现了operation1");
}
@Override
public void operation2() {
System.out.println("B 实现了operation2");
}
@Override
public void operation3() {
System.out.println("B 实现了operation3");
}
@Override
public void operation4() {
System.out.println("B 实现了operation4");
}
@Override
public void operation5() {
System.out.println("B 实现了operation5");
}
}
/**
* 通过接口Inteface1依赖D, 但是只会用到1,4,5三个方法
*/
class C {
public void depend1(Interface1 interface1) {
interface1.operation1();
}
public void depend4(Interface1 interface1) {
interface1.operation4();
}
public void depend5(Interface1 interface1) {
interface1.operation5();
}
}
class D implements Interface1 {
@Override
public void operation1() {
System.out.println("D 实现了operation1");
}
@Override
public void operation2() {
System.out.println("D 实现了operation2");
}
@Override
public void operation3() {
System.out.println("D 实现了operation3");
}
@Override
public void operation4() {
System.out.println("D 实现了operation4");
}
@Override
public void operation5() {
System.out.println("D 实现了operation5");
}
}
package com.mine.segregation;
/**
* 接口隔离原则2
* AA类通过InterfaceB接口实现类BB, 只会用到1,2,3三个方法
* CC类通过InterfaceD接口实现类DD, 只会用到1,4,5三个方法
*/
public class InterfaceSegregation_2 {
public static void main(String[] args) {
AA aa = new AA();
final BB bb = new BB();
aa.depend1(bb);
aa.depend2(bb);
aa.depend3(bb);
CC cc = new CC();
final DD dd = new DD();
cc.depend1(dd);
cc.depend4(dd);
cc.depend5(dd);
}
}
interface Interface11 {
void operation1();
}
interface InterfaceB extends Interface11 {
void operation1();
void operation2();
void operation3();
}
interface InterfaceD extends Interface11 {
void operation1();
void operation4();
void operation5();
}
/**
* 通过接口IntefaceB依赖BB, 但是只会用到1,2,3三个方法
*/
class AA {
public void depend1(InterfaceB interfaceB) {
interfaceB.operation1();
}
public void depend2(InterfaceB interfaceB) {
interfaceB.operation2();
}
public void depend3(InterfaceB interfaceB) {
interfaceB.operation3();
}
}
class BB implements InterfaceB {
@Override
public void operation1() {
System.out.println("BB 实现了operation1");
}
@Override
public void operation2() {
System.out.println("BB 实现了operation2");
}
@Override
public void operation3() {
System.out.println("BB 实现了operation3");
}
}
/**
* 通过接口IntefaceD依赖DD, 但是只会用到1,4,5三个方法
*/
class CC {
public void depend1(InterfaceD interfaceD) {
interfaceD.operation1();
}
public void depend4(InterfaceD interfaceD) {
interfaceD.operation4();
}
public void depend5(InterfaceD interfaceD) {
interfaceD.operation5();
}
}
class DD implements InterfaceD {
@Override
public void operation1() {
System.out.println("DD 实现了operation1");
}
@Override
public void operation4() {
System.out.println("DD 实现了operation4");
}
@Override
public void operation5() {
System.out.println("DD 实现了operation5");
}
}
依赖倒转原则(DI)
- 高层模块不应该依赖低层模块. 二者都应该依赖其抽象
- 抽象不应该依赖细节, 细节应该依赖抽象
- 依赖倒转的中心思想是面向接口编程
java中抽象指的是接口或者抽象类, 细节指的是具体的实现类, 依赖倒转的目的是制定好规范, 提高稳定性
依赖的三种方式:
通过接口依赖
通过构造器依赖
通过setter方法依赖
package com.mine.principle.dependencyInversion;
/**
* 依赖倒转原则1
* 需求: 用户接收消息
* 1. 不遵循依赖倒转原则
* 2. 如果要接收短信或者微信等消息, 要新增加类, 同时person也要新增加方法
*
* 解决方案: 创建一个接口, email, 微信, 短信等依赖这个接口
*/
public class DependencyInversion_1 {
public static void main(String[] args) {
Person person = new Person();
person.receive(new Email());
}
}
class Person {
public void receive(Email email) {
System.out.println(email.getInfo());
}
}
class Email {
public String getInfo() {
return "电子邮件信息: hello world!";
}
}
package com.mine.principle.dependencyInversion;
/**
* 依赖倒转原则1
* 需求: 用户接收消息
*/
public class DependencyInversion_2 {
public static void main(String[] args) {
Person2 person2 = new Person2();
person2.receive(new Email2());
person2.receive(new Weixin2());
}
}
class Person2 {
public void receive(Message message) {
System.out.println(message.getInfo());
}
}
abstract class Message {
abstract String getInfo();
}
class Email2 extends Message {
@Override
public String getInfo() {
return "电子邮件信息: hello world!";
}
}
class Weixin2 extends Message {
@Override
public String getInfo() {
return "微信信息: hello weixin!";
}
}
里氏替换原则
继承带来便利的同时也带来了很多弊端, 类之间的耦合, 父类做修改, 必须考虑所有子类是否有问题. 如何正确的使用继承=>里氏替换原则
- 子类尽量不去重写父类的方法.
- 继承实际上增加了两个类的耦合, 可以通过聚合, 组合, 依赖来解决问题.
package com.mine.principle.liskov;
/**
* 里氏替换原则
* 1. 不遵循历史替换原则
*/
public class Liskov_1 {
public static void main(String[] args) {
B b = new B();
System.out.println(b.func1(2, 1));
System.out.println(b.func2(2, 1));
}
}
class A {
public int func1(int a, int b) {
return a - b;
}
}
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;
}
}
package com.mine.principle.liskov;
/**
* 里氏替换原则
* 1. 不遵循历史替换原则
*/
public class Liskov_2 {
public static void main(String[] args) {
BB bb = new BB();
System.out.println(bb.func1(2, 1));
System.out.println(bb.func2(2, 1));
}
}
class Base {
}
class AA extends Base {
public int func1(int a, int b) {
return a - b;
}
}
class BB extends Base {
// 组合方式
AA aa = new AA();
public int func1(int a, int b) {
return a + b;
}
// 这里使用AA的func1方法
public int func2(int a, int b) {
return func3(a, b) + 9;
}
public int func3(int a, int b) {
return aa.func1(a, b);
}
}
开闭原则
- 一个软件实体如类, 模块, 方法应该对拓展开放(提供方), 修改关闭(使用方). 用抽象构建框架, 用实现拓展细节.
- 一个软件功能变化时, 应该用拓展类来实现变化, 而不是修改原有代码.
- 编程中遵循其他原则或者使用设计模式, 最终都是为了实现开闭原则.
package com.mine.principle.openClose;
/**
* 开闭原则
* 1. 不遵循开闭原则
* 2. 比如现在增加一个画三角形的方法, 修改的地方比较多
*/
public class OpenClose_1 {
public static void main(String[] args) {
final GraphicEditor graphicEditor = new GraphicEditor();
graphicEditor.drawShape(new Rectangle());
graphicEditor.drawShape(new Circle());
graphicEditor.drawTriangle(new Triangle());
}
}
// 画图工具类(使用方)
class GraphicEditor {
public void drawShape(Shape s) {
if (s.m_type == 1) {
drawRectangle(s);
} else if (s.m_type == 2) {
drawCircle(s);
} else if (s.m_type == 3) {
// 3
drawTriangle(s);
}
}
public void drawRectangle(Shape s) {
System.out.println("矩形");
}
public void drawCircle(Shape s) {
System.out.println("圆形");
}
// 2
public void drawTriangle(Shape s) {
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 新增画三角形
class Triangle extends Shape {
Triangle() {
super.m_type = 3;
}
}
package com.mine.principle.openClose;
/**
* 开闭原则
* 遵循开闭原则
*/
public class OpenClose_2 {
public static void main(String[] args) {
final GraphicEditor graphicEditor = new GraphicEditor();
graphicEditor.drawShape(new Rectangle());
graphicEditor.drawShape(new Circle());
graphicEditor.drawShape(new Triangle());
}
}
// 画图工具类(使用方)
class GraphicEditor {
public void drawShape(Shape s) {
s.draw();
}
}
// 一个基类 改成抽象类
abstract class Shape {
abstract void draw();
}
class Rectangle extends Shape {
@Override
void draw() {
System.out.println("矩形");
}
}
class Circle extends Shape {
@Override
void draw() {
System.out.println("圆形");
}
}
// 1 新增画三角形
class Triangle extends Shape {
@Override
void draw() {
System.out.println("三角形");
}
}
迪米特法则
- 一个对象应该对另外一个对象保持最少的了解.
- 类与类之间越密切, 耦合度就越高
- 迪米特法则又叫最少知道法则, 即一个类对自己依赖的类知道的越少越好.
- 只与直接朋友通讯, 直接朋友包括方法参数, 返回值, 成员变量, 方法中的局部变量不是直接朋友.
package com.mine.principle.demeter;
import java.util.ArrayList;
import java.util.List;
/**
* 迪米特法则
* 1. 不遵循迪米特法则
*/
public class Demeter_1 {
public static void main(String[] args) {
SchoolManager manager = new SchoolManager();
CollegeManager collegeManager = new CollegeManager();
manager.printAllEmployee(collegeManager);
}
}
// 学校总部员工
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 employee = new CollegeEmployee();
employee.setId("学院员工id=> " + i);
list.add(employee);
}
return list;
}
}
// 学校管理类
class SchoolManager {
// 学校总部的员工
public List<Employee> getAllEmployee() {
List<Employee> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
Employee employee = new Employee();
employee.setId("学校员工id=> " + i);
list.add(employee);
}
return list;
}
// 打印总部和学院的所有员工
void printAllEmployee(CollegeManager collegeManager) {
System.out.println("============分公司员工============");
// CollegeEmployee是局部变量, 不是CollegeManager的直接朋友, 不遵循迪米特法则
List<CollegeEmployee> list1 = collegeManager.getAllEmployee();
list1.forEach(System.out::println);
System.out.println("============总部员工============");
List<Employee> list2 = getAllEmployee();
list2.forEach(System.out::println);
}
}
package com.mine.principle.demeter;
import java.util.ArrayList;
import java.util.List;
/**
* 迪米特法则
* 1. 遵循迪米特法则
*/
public class Demeter_2 {
public static void main(String[] args) {
SchoolManager2 manager = new SchoolManager2();
CollegeManager2 collegeManager = new CollegeManager2();
manager.printAllEmployee(collegeManager);
}
}
// 学校总部员工
class Employee2 {
private String id;
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
// 学院员工
class CollegeEmployee2 {
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
// 管理学院员工的类
class CollegeManager2 {
public List<CollegeEmployee2> getAllEmployee() {
List<CollegeEmployee2> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
CollegeEmployee2 employee = new CollegeEmployee2();
employee.setId("学院员工id=> " + i);
list.add(employee);
}
return list;
}
void printCollegeEmployee() {
System.out.println("============分公司员工============");
// CollegeEmployee是局部变量, 不是CollegeManager的直接朋友, 不遵循迪米特法则
List<CollegeEmployee2> list = getAllEmployee();
list.forEach(System.out::println);
}
}
// 学校管理类
class SchoolManager2 {
// 学校总部的员工
public List<Employee2> getAllEmployee() {
List<Employee2> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
Employee2 employee = new Employee2();
employee.setId("学校员工id=> " + i);
list.add(employee);
}
return list;
}
// 打印总部和学院的所有员工
void printAllEmployee(CollegeManager2 collegeManager) {
System.out.println("============分公司员工============");
// CollegeEmployee是局部变量, 不是CollegeManager的直接朋友, 不遵循迪米特法则
collegeManager.printCollegeEmployee();
System.out.println("============总部员工============");
List<Employee2> list2 = getAllEmployee();
list2.forEach(System.out::println);
}
}
合成复用原则
- 尽量使用合成/聚合的方式, 而不是继承
- 依赖: 方法参数引入依赖的对象
- 组合: 成员变量移入依赖的对象, 构造器引入依赖
- 聚合: 成员变量移入依赖的对象, setter方法引入依赖