单一职责
描述:一个类、方法或模块只负责一项功能。
场景举例:设计账户信息类时,会有账号、密码、电话、地址等基本信息。最简单的就是将所有属性放到同一个类中。在实际中,我们使用购物软件时可能会保存多个地址信息,那么原先的设计就不能满足需求了。这时需要单独一个类保存地址信息。这便体现了单一职责原则。
代码示例:
修改前:
package com.zf.design.principle;
import lombok.Data;
@Data
public class UserInfo {
private String account;
private String password;
private String phone;
private String address;
}
修改后:
package com.zf.design.principle;
import lombok.Data;
@Data
public class AddressInfo {
private String phone;
private String address;
}
package com.zf.design.principle;
import lombok.Data;
import java.util.List;
@Data
public class UserInfo {
private String account;
private String password;
private List<AddressInfo> address;
}
开闭原则
对扩展开放,对修改关闭。通过扩展代码来实现新功能,而不是修改代码。
场景举例:画不同的图形,画圆形、三角形。
代码示例:
修改前:定义绘画类,添加画圆形方法,画三角形方法。如果要再画正方形怎么办呢,需要修改绘画类。如果该类是别人写的可能修改不了,而且修改可能会引入错误,这不是我们想要的。
package com.zf.design.principle;
public class Draw {
public void drawCircle() {
System.out.println("draw circle-------");
}
public void drawRectangle() {
System.out.println("draw rectangle-------");
}
}
修改后:使用抽象和接口定义可扩展的点。定义形状接口Shape及绘画方法,所有形状实现Shape接口。有新的形状则实现Shape接口即可
public interface Shape {
void draw();
}
public class Circle implements Shape{
@Override
public void draw() {
System.out.println("draw circle-------");
}
}
public class Rectangle implements Shape{
@Override
public void draw() {
System.out.println("draw rectangle-------");
}
}
接口隔离原则
类继承最小接口集合,避免实现其他不需要的接口方法。即将接口拆分成最小单元提高灵活性及可复用性。
场景举例:动物有天上飞的,有地上跑的,会吃东西等,如果把这些行为放到同一个接口就会造成冗余。
修改前:对于Dog来说,fly()方法就是多余的
package com.zf.design.principle;
public interface Animal {
void eat();
void run();
void fly();
}
package com.zf.design.principle;
public class Dog implements Animal{
@Override
public void eat() {
}
@Override
public void run() {
}
@Override
public void fly() {
}
}
修改后:Dog只需要实现它需要的接口
public interface Eatable {
void eat();
}
package com.zf.design.principle;
public interface Runnnable {
void run();
}
package com.zf.design.principle;
public interface Flyable {
void fly();
}
package com.zf.design.principle;
public class Dog implements Eatable, Runnnable {
@Override
public void eat() {
}
@Override
public void run() {
}
}
依赖倒置原则
高层模块依赖低层模块翻转过来,高层模块需要什么,低层模块给什么。高层模块依赖抽象而不依赖具体实现。
场景举例:造汽车,汽车依赖依赖底盘,底盘依赖轮子。如果汽车需要的轮子尺寸不同,则需要修改每个类传入轮子尺寸
修改前:
修改后:Car依赖类而不是具体的实现。修改Tire尺寸只需要修改Tire类。接口定义好后可以多人同时开发具体实现细节;单元测试只需要给Car传Framework而不用关心底层的Buttom等。
以上图片来源:https://www.zhihu.com/question/23277575/answer/169698662
迪米特原则
迪米特是个i人,与他人保持最少的联系。不同模块之间尽可能少了解彼此的细节,降低代码的复杂性和耦合度,提高代码的可读性和可维护性。
场景举例:学校由若干班级组成,班级有若干学生。在此场景下学校只与班级发生联系,而不需关心学生的实现细节
代码示例:学校打印所有班级,不需要关心学生信息
package com.zf.design.principle;
import lombok.Data;
@Data
public class Student {
private String name;
private int age;
private String school;
public Student(String name, int age, String school) {
this.name = name;
this.age = age;
this.school = school;
}
public void printStudent(){
System.out.println("我是" + name + ",今年" + age + "岁,就读于" + school);
}
}
package com.zf.design.principle;
import lombok.Data;
import java.util.List;
@Data
public class Class {
private String name;
private List<Student> students;
public Class(String name, List<Student> students) {
this.name = name;
this.students = students;
}
public void printStudents(){
students.forEach((student)->System.out.println(student.getName()));
}
}
package com.zf.design.principle;
import lombok.Data;
import java.util.List;
@Data
public class School {
private String name;
private List<Class> classes;
public School(String name, List<Class> classes) {
this.name = name;
this.classes = classes;
}
public void printClasses(){
classes.forEach(
classes -> System.out.println(classes.getName())
);
}
}
package com.zf.design.principle;
import java.util.ArrayList;
import java.util.Arrays;
public class MainRunner {
public static void main(String[] args) {
// 不需要关心student细节
Class mainClass1 = new Class("一年一班", new ArrayList<>());
Class mainClass2 = new Class("一年二班",new ArrayList<>());
School school = new School("清华中学",Arrays.asList(mainClass2,mainClass1));
school.printClasses();
}
}
里氏替换原则
子类替换父类,尽量避免重写父类方法,尽量遵循父类的约束和行为,不引入新的约束和行为
场景举例:类Ostrich重写了父类fly方法并抛出异常,与父类原有预期不一致。用户不知情的情况下使用了Ostrich的fly方法就会得到与预期不符的结果
// 基类
class Bird {
public void fly() {
System.out.println("Flying high");
}
}
// 派生类,遵守LSP
class Sparrow extends Bird {
@Override
public void fly() {
System.out.println("Sparrow is flying");
}
}
// 派生类,违反LSP(假设Ostrich不能飞)
class Ostrich extends Bird {
@Override
public void fly() {
throw new UnsupportedOperationException("Ostrich cannot fly");
}
}
// 使用示例
public class Main {
public static void letItFly(Bird bird) {
bird.fly();
}
public static void main(String[] args) {
letItFly(new Sparrow()); // 正常工作
// letItFly(new Ostrich()); // 抛出异常,违反LSP
}
}
合成复用原则
能组合,不继承。尽量使用组合/聚合的方式达到复用目的,而不是通过继承。
// 通过组合/聚合来实现复用,而不是继承
class Engine {
public void run() {
System.out.println("Engine is running");
}
}
class Car {
private Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
public void start() {
engine.run();
}
}
// 使用示例
public class Main {
public static void main(String[] args) {
Engine dieselEngine = new Engine() {
@Override
public void run() {
System.out.println("Diesel engine is running");
}
};
Car dieselCar = new Car(dieselEngine);
dieselCar.start(); // 输出: Diesel engine is running
// 可以很容易地替换成其他类型的引擎,而不需要修改Car类
}
}
参考文章: