一、单一职责原则
基本介绍
对类来说,一个类应当只负责一项职责,如果类A负责两个不同的职责,如职责1,职责2。当职责1需求变更而改变类A时,可能会导致职责2执行报错,所以需要将A的粒度分解为A1,A2。
示例
/**
* @author songle
* @create 2021-05-16 10:06
* @descreption 单一职责原则示例
*/
public class SingleResponse {
public static void main(String[] args) {
Vichel vichel = new Vichel();
vichel.run("摩托车");
vichel.run("轮船");
vichel.run("飞机"); // 违反单一职责原则
// 解决办法一
RoadVichel roadVichel = new RoadVichel();
roadVichel.run("摩托车");
WaterVichel waterVichel = new WaterVichel();
waterVichel.run("轮船");
AirVichel airVichel = new AirVichel();
airVichel.run("飞机");
// 解决方案二
Traffic traffic = new Traffic();
traffic.run("摩托车");
traffic.runWater("轮船");
traffic.runAir("飞机");
}
}
class Vichel{
public void run(String vichel) {
System.out.println(vichel + "在公路上跑************");
}
}
// 解决方法一:从类上实现粒度分解 缺点:改动较大
class RoadVichel{
public void run(String vichel) {
System.out.println(vichel + "在公路上跑************");
}
}
class WaterVichel{
public void run(String vichel) {
System.out.println(vichel + "在水上跑************");
}
}
class AirVichel{
public void run(String vichel) {
System.out.println(vichel + "在天上飞************");
}
}
// 解决方法二 在类上没有遵守单一职责原则,在方法上遵守单一职业原则
class Traffic{
public void run(String vichel) {
System.out.println(vichel + "在公路上跑************");
}
public void runWater(String vichel) {
System.out.println(vichel + "在水上跑************");
}
public void runAir(String vichel) {
System.out.println(vichel + "在天上飞************");
}
}
总结
降低类的复杂度,一个类只负责一项职责。
提高类的可读性,可维护性。
降低变更引起的风险线
通常情况下,我们应该遵守单一职责原则,只有逻辑足够简单,才可以在代码级别违反单一职责原则;只有类中方法较少,才可以在方法级别保持单一职责原则。
二、接口隔离原则
基本介绍
客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上。
举个例子:当类A通过接口interface1依赖类B,类C通过接口interface1依赖类D,如果接口interface1对于类A和类C都不是最小接口,那么类B和类D都必须要去实现他们不需要的方法。
按照接口隔离原则应该这样处理:将接口Interface1拆分为几个独立的接口,类A与类C分别与他们需要的接口建立依赖关系。
示例
/**
* @author songle
* @create 2021-05-16 11:02
* @descreption 接口隔离原则
*/
public class Segregation {
public static void main(String[] args) {
ExampleA a = new ExampleA();
a.depend1(new ExampleB());
a.depend2(new ExampleB());
a.depend3(new ExampleB());
ExampleC c = new ExampleC();
c.depend1(new ExampleD());
c.depend4(new ExampleD());
c.depend5(new ExampleD());
}
}
interface Interface1 {
void operation1();
void operation2();
void operation3();
void operation4();
void operation5();
}
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方法");
}
}
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方法");
}
}
class A { // 类A通过接口interface1依赖使用B类,但是只会用到123方法
public void depend1(Interface1 interface1) {
interface1.operation1();
}
public void depend2(Interface1 interface1) {
interface1.operation2();
}
public void depend3(Interface1 interface1) {
interface1.operation3();
}
}
class C { // 类C通过接口interface1依赖使用D类,但是只会用到145方法
public void depend1(Interface1 interface1) {
interface1.operation1();
}
public void depend4(Interface1 interface1) {
interface1.operation4();
}
public void depend5(Interface1 interface1) {
interface1.operation5();
}
}
// 解决方法,使用接口隔离原则,将接口interface1进行拆分
interface Interface2 {
void operation1();
}
interface Interface3 {
void operation2();
void operation3();
}
interface Interface4 {
void operation4();
void operation5();
}
class ExampleB implements Interface2,Interface3 {
@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方法");
}
}
class ExampleD implements Interface2,Interface4 {
@Override
public void operation1() {
System.out.println("类B实现了operation1方法");
}
@Override
public void operation4() {
System.out.println("类B实现了operation4方法");
}
@Override
public void operation5() {
System.out.println("类B实现了operation5方法");
}
}
class ExampleA {
public void depend1(Interface2 interface2) {
interface2.operation1();
}
public void depend2(Interface3 interface3) {
interface3.operation2();
}
public void depend3(Interface3 interface3) {
interface3.operation3();
}
}
class ExampleC {
public void depend1(Interface2 interface2) {
interface2.operation1();
}
public void depend4(Interface4 interface4) {
interface4.operation4();
}
public void depend5(Interface4 interface4) {
interface4.operation5();
}
}
三、依赖倒转原则
基本介绍
依赖倒转原则是指:
1)高层模块不应该依赖低层模块,二者都应该依赖其抽象。
2)抽象不应该依赖细节,细节应该依赖抽象。
3)依赖倒转的中心思想是面向接口编程
4)依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多
。以抽象为基础搭成的架构比以细节为基础搭成的架构要稳定的多。在java中,抽象指的是接口或者抽象类,细节指的是具体的实现。
5)使用接口或者抽象类的目的是为了制定规范,而不涉及任何具体的操作,把展现细节的操作交给他们的实现类去完成。
示例
/**
* @author songle
* @create 2021-05-16 15:02
* @descreption 依赖倒转原则
*/
public class DependInversion {
public static void main(String[] args) {
Person person = new Person();
person.recieve(new Email());
Person2 person2 = new Person2();
person2.recieve(new Email2());
person2.recieve(new Weixin());
}
}
class Email {
public String getInfo() {
return "接收电子邮件信息:hello,world";
}
}
// 不适用依赖倒转原则的分析:
// 1、简单,比较容易想到
// 2、如果要获取的是微信消息,QQ消息,短息消息等,需要新增类,同时person中也需要新增相应的接收方法
// 3、解决思路:引入一个接口IReciever,让person和IReciever发生依赖
class Person {
public void recieve(Email email) {
System.out.println(email.getInfo());
}
}
// 依赖倒转原则优化
class Person2 {
public void recieve(IReciever iReciever) {
System.out.println(iReciever.getInfo());
}
}
interface IReciever {
String getInfo();
}
class Email2 implements IReciever{
@Override
public String getInfo() {
return "接收电子邮件信息:hello,world";
}
}
class Weixin implements IReciever{
@Override
public String getInfo() {
return "接收微信:hello,world";
}
}
总结
低层模块都要有接口或者抽象类,或者两者都有,程序的稳定性更好
变量的声明尽量是抽象类或者接口,这样我们的变量引用和实际对象之间就存在一个缓冲层,有利于程序扩展和优化
继承时要遵循里氏替换原则
四、里氏替换原则
面向对象中的继承性的思考和说明
1)继承包含这样的一层含义:父类中凡是已经实现的方法,实际上是在设定一些规范和契约,虽然它不强制要求所有的子类都必须要遵守这些规范和契约,但是如果子类对这些已经实现的方法进行修改,就会对整个继承的体系产生破坏。
2)继承在给程序设计带来便利的同时,也带来了一些弊端。比如使用继承会给程序带来侵入性,程序的可移植性降低,增加了对象间的耦合性,如果一个类被其他的类所继承,那么当这个类需要修改时,必须考虑到它的所有子类,并且父类修改之后可能会对子类的功能产生影响。
3)这个时候,就需要使用到里氏替换原则去规避这些问题。
基本介绍
1)如果对每个类型为T1的对象O1,都有类型为T2的对象O2,使得以T1定义的所有程序P在所有的对象O1都替换成O2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。换句话说,所有引用基类的地方都必须能透明的使用其子类的对象。
2)在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法。
3)里氏替换原则告诉我们,继承实际上是让两个类之间的耦合性增强了,在适当的情况下可以通过聚合,组合,依赖的方式来解决问题。
示例
/**
* @author songle
* @create 2021-05-16 15:50
* @descreption 里氏替换原则
*/
public class Liskvo {
public static void main(String[] args) {
Func1 func1 = new Func1();
int i = func1.func1(10, 8);
System.out.println("正确答案" + (i + 9));
Func2 func2 = new Func2();
int i1 = func2.func2(10, 8);
System.out.println("错误答案" + i1);
Func3 func3 = new Func3();
int i2 = func3.func1(10, 8);
System.out.println("正确答案" + (i2 + 9));
Func4 func4 = new Func4();
int i3 = func4.func2(10, 8);
System.out.println("正确答案" + i3);
int i4 = func4.func3(10,8);
System.out.println("正确答案" + (i4 + 9));
}
}
class Func1 {
public int func1(int a,int b) {
return a - b;
}
}
class Func2 extends Func1 {
public int func1(int a,int b) {
return a + b;
}
public int func2(int a,int b) {
return func1(a,b) + 9;
}
}
// 解决方法,使用里氏替换原则
// 创建一个更加基础的类
class Base {
}
class Func3 extends Base {
public int func1(int a,int b) {
return a - b;
}
}
class Func4 extends Base {
private Func1 func1 = new Func1();
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.func1.func1(a,b);
}
}
五、开闭原则
基本介绍
1)开闭原则是变成最基础。最重要的设计原则
2)一个软件实体如类,函数和模块应该对扩展开放,对修改关闭。用抽象构建框架,用实现扩展细节。
3)当软件需求发生变化时,尽量通过扩展软件实体的方法来实现变化,而不是通过修改已有的代码来实现变化。
4)编程中遵循其他原则,以及使用设计模式的目的就是遵循开闭原则。
示例
/**
* @author songle
* @create 2021-05-16 19:49
* @descreption 开闭原则
*/
public class Ocp {
public static void main(String[] args) {
// 传统写法
GraphicEdit graphicEdit = new GraphicEdit();
graphicEdit.draw(new Rectangle());
graphicEdit.draw(new Circle());
// 开闭原则写法
OcpGraphicEdit ocpGraphicEdit = new OcpGraphicEdit();
ocpGraphicEdit.draw(new OcpRectangle());
ocpGraphicEdit.draw(new OcpCircle());
ocpGraphicEdit.draw(new OcpTriangle());
}
}
class Shape {
int shape_type;
}
// 矩形
class Rectangle extends Shape {
Rectangle () {
super.shape_type = 1;
}
}
// 圆形
class Circle extends Shape {
Circle () {
super.shape_type = 2;
}
}
class GraphicEdit {
public void draw(Shape s) {
if (s.shape_type == 1) {
drawRectangle();
}else if (s.shape_type == 2) {
drawCircle();
}
}
public void drawRectangle() {
System.out.println("绘制矩形");
}
public void drawCircle() {
System.out.println("绘制圆形");
}
}
// 原代码扩展的时候会修改原来的代码,违背开闭原则,使用抽象类优化代码,新增绘制三角形,无需改变原来的代码即可实现
abstract class OcpShape {
public abstract void drawShape();
}
// 矩形
class OcpRectangle extends OcpShape {
@Override
public void drawShape() {
System.out.println("绘制矩形");
}
}
// 圆形
class OcpCircle extends OcpShape {
@Override
public void drawShape() {
System.out.println("绘制圆形");
}
}
// 三角形
class OcpTriangle extends OcpShape {
@Override
public void drawShape() {
System.out.println("绘制三角形");
}
}
class OcpGraphicEdit {
public void draw(OcpShape s) {
s.drawShape();
}
}
六、迪米特法则
基本介绍
1)一个对象应该对其它对象保持最少的了解。
2)类与类关系越密切,耦合度越大。
3)迪米特法则又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类,不管多复杂,都尽量在逻辑封装在类的内部。对外除了public方法,不暴露任何信息。
4)迪米特法则还有个更直接的定义:只与直接的朋友通信。
5)直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式有很多种:依赖、关联、组合、聚合等。其中,我们称出现成员变量,方法参数,方法返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变量的形式出现在类的内部。
示例
/**
* @author songle
* @create 2021-05-16 22:23
* @descreption 迪米特法则
*/
public class Demeter {
public static void main(String[] args) {
SchoolManager schoolManager = new SchoolManager();
schoolManager.printAllEmployee(new CollegeManager());
}
}
@Data
class SchoolEmployee {
private Integer id;
}
@Data
class CollegeEmployee {
private Integer id;
}
class SchoolManager {
// 返回学校员工的总数
public List getAllShcoolEmployee() {
List schoolEmployeeList = new ArrayList<>();
for (int i = 0; i < 5; i++) {
SchoolEmployee schoolEmployee = new SchoolEmployee();
schoolEmployee.setId(i);
schoolEmployeeList.add(schoolEmployee);
}
return schoolEmployeeList;
}
void printAllEmployee(CollegeManager collegeManager) {
// 获取到学院员工
// 这里的CollegeEmployee是以局部变量的方式出现在SchoolManager类中的,属于陌生类,不是直接的朋友,违反了迪米特法则
// List collegeEmployeeList = collegeManager.getAllCollegeEmployee();
// System.out.println("****学院员工********");
// for (CollegeEmployee collegeEmployee:collegeEmployeeList) {
// System.out.println(collegeEmployee.getId());
// }
// 优化后
collegeManager.printCollegeEmployee();
List allShcoolEmployee = getAllShcoolEmployee();
System.out.println("****学校总部员工********");
for (SchoolEmployee schoolEmployee:allShcoolEmployee) {
System.out.println(schoolEmployee.getId());
}
}
}
class CollegeManager {
// 返回学院员工的总数
public List getAllCollegeEmployee() {
List collegeEmployeeList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
CollegeEmployee collegeEmployee = new CollegeEmployee();
collegeEmployee.setId(i);
collegeEmployeeList.add(collegeEmployee);
}
return collegeEmployeeList;
}
void printCollegeEmployee() {
// 获取到学院员工
List collegeEmployeeList = this.getAllCollegeEmployee();
System.out.println("****学院员工********");
for (CollegeEmployee collegeEmployee:collegeEmployeeList) {
System.out.println(collegeEmployee.getId());
}
}
}