【设计模式】1、设计模式七大原则
创始人
2025-05-28 10:01:46
0

目录

  • 一、单一职责
  • 二、接口隔离
  • 三、依赖倒置(倒转)
  • 四、里氏替换
  • 五、迪米特法则(Law of Demeter)
  • 六、开闭
  • 七、合成复用

一、单一职责

  • 类(或方法)功能的专一性。一个类(或方法)不应该承担太多功能,一个类(或方法)最好只承担 一种类型 的功能。
public class SingleResponsibility {public static void main(String[] args) {Vehicle vehicle = new Vehicle();vehicle.run("汽车");vehicle.run("火车");vehicle.run("自行车");vehicle.run("飞机"); // 有问题vehicle.run("轮船"); // 有问题}
}class Vehicle {public void run(String vehicleName) {System.out.println(vehicleName + "在公路上行驶");}
}

Vehicle 类既处理陆地上的交通工具,又处理天空中的交通工具;它的作用太广泛了,不单一。


public class SingleResponsibility {public static void main(String[] args) {RoadVehicle roadVehicle = new RoadVehicle();roadVehicle.run("汽车");roadVehicle.run("火车");roadVehicle.run("自行车");SkyVehicle skyVehicle = new SkyVehicle();skyVehicle.run("飞机");WaterVehicle waterVehicle = new WaterVehicle();waterVehicle.run("轮船");}
}class RoadVehicle {public void run(String vehicleName) {System.out.println(vehicleName + "在公路上行驶");}
}class WaterVehicle {public void run(String vehicleName) {System.out.println(vehicleName + "在水中行驶");}
}class SkyVehicle {public void run(String vehicleName) {System.out.println(vehicleName + "在天空中行驶");}
}

在类级别遵循了单一职责原则

当业务功能比较简单的时候也没有必要将其拆分为多个类(如下所示)


public class SingleResponsibility {public static void main(String[] args) {Vehicle vehicle = new Vehicle();vehicle.run("汽车", VehicleType.ROAD);vehicle.run("火车", VehicleType.ROAD);vehicle.run("自行车", VehicleType.ROAD);vehicle.run("飞机", VehicleType.SKY);vehicle.run("轮船", VehicleType.WATER);}
}enum VehicleType {ROAD, WATER, SKY;
}class Vehicle {public void run(String name, VehicleType type) {String sentence = "";if (type == VehicleType.ROAD) {sentence = "在公路上行驶";} else if (type == VehicleType.WATER) {sentence = "在水中行驶";} else if (type == VehicleType.SKY) {sentence = "在天空中行驶";}System.out.println(name + sentence);}// public void roadRun(String name) {}// public void skyRun(String name) {}// public void waterRun(String name) {}
}

Vehicle 类不符合单一职责原则,但其功能简单 。
当功能非常非常简单的时候,不一定必须遵循单一职责原则。(杀鸡别用宰牛刀)

  • 降低了类的功能的复杂度
  • 提高了代码的维护性
  • 代码修改导致连带错误几率降低

二、接口隔离

Clients should not be forced to depend on methods they do not use. 客户端不应该被迫依赖它不使用的方法。
The dependency of one class to another one should depend on the smallest possible interface. 一个类对另一个类的依赖应该建立在最小的接口上。

在这里插入图片描述

public class InterfaceSegregation {public static void main(String[] args) {InOutAbleImpl1 impl1 = new InOutAbleImpl1();InOutAbleImpl2 impl2 = new InOutAbleImpl2();Cat cat = new Cat();cat.use1(impl1);cat.use2(impl1);cat.use3(impl1);Dog dog = new Dog();dog.use1(impl2);dog.use2(impl2);dog.use3(impl2);}
}class Cat {public void use1(InOutAble inOutAble) {inOutAble.openDoor();}public void use2(InOutAble inOutAble) {inOutAble.pushGoods();}public void use3(InOutAble inOutAble) {inOutAble.writeGoodsName();}
}class Dog {public void use1(InOutAble inOutAble) {inOutAble.openDoor();}public void use2(InOutAble inOutAble) {inOutAble.popGoods();}public void use3(InOutAble inOutAble) {inOutAble.writePeopleName();}
}/*** 存取东西的接口*/
interface InOutAble {// 类型1void openDoor(); // 打开门// 类型2void pushGoods(); // 放入货物void writeGoodsName(); // 登记货物名字// 类型3void popGoods(); // 取出货物void writePeopleName(); // 登记取货人的名字
}class InOutAbleImpl1 implements InOutAble {@Overridepublic void openDoor() {System.out.println("InOutAbleImpl1 - openDoor");}@Overridepublic void pushGoods() {System.out.println("InOutAbleImpl1 - pushGoods");}@Overridepublic void writeGoodsName() {System.out.println("InOutAbleImpl1 - writeGoodsName");}@Overridepublic void popGoods() {System.out.println("InOutAbleImpl1 - popGoods");}@Overridepublic void writePeopleName() {System.out.println("InOutAbleImpl1 - writePeopleName");}
}class InOutAbleImpl2 implements InOutAble {@Overridepublic void openDoor() {System.out.println("InOutAbleImpl2 - openDoor");}@Overridepublic void pushGoods() {System.out.println("InOutAbleImpl2 - pushGoods");}@Overridepublic void writeGoodsName() {System.out.println("InOutAbleImpl2 - writeGoodsName");}@Overridepublic void popGoods() {System.out.println("InOutAbleImpl2 - popGoods");}@Overridepublic void writePeopleName() {System.out.println("InOutAbleImpl2 - writePeopleName");}
}

在这里插入图片描述


在这里插入图片描述

public class InterfaceSegregation {public static void main(String[] args) {OpenPushImpl impl1 = new OpenPushImpl();OpenPopImpl impl2 = new OpenPopImpl();Cat cat = new Cat();cat.use1(impl1);cat.use2(impl1);cat.use3(impl1);Dog dog = new Dog();dog.use1(impl2);dog.use2(impl2);dog.use3(impl2);}
}class Cat {public void use1(OpenPushImpl openPushImpl) {openPushImpl.openDoor();}public void use2(OpenPushImpl openPushImpl) {openPushImpl.pushGoods();}public void use3(OpenPushImpl openPushImpl) {openPushImpl.writeGoodsName();}
}class Dog {public void use1(OpenPopImpl openPopImpl) {openPopImpl.openDoor();}public void use2(OpenPopImpl openPopImpl) {openPopImpl.popGoods();}public void use3(OpenPopImpl openPopImpl) {openPopImpl.writePeopleName();}
}interface OpenDoorAble {void openDoor(); // 打开门
}interface PushAble {void pushGoods(); // 放入货物void writeGoodsName(); // 登记货物名字
}interface PopAble {void popGoods(); // 取出货物void writePeopleName(); // 登记取货人的名字
}class OpenPushImpl implements OpenDoorAble, PushAble {@Overridepublic void openDoor() {System.out.println("OpenPushImpl - openDoor");}@Overridepublic void pushGoods() {System.out.println("OpenPushImpl - pushGoods");}@Overridepublic void writeGoodsName() {System.out.println("OpenPushImpl - writeGoodsName");}
}class OpenPopImpl implements OpenDoorAble, PopAble {@Overridepublic void openDoor() {System.out.println("OpenPopImpl - openDoor");}@Overridepublic void popGoods() {System.out.println("OpenPopImpl - popGoods");}@Overridepublic void writePeopleName() {System.out.println("OpenPopImpl - writePeopleName");}
}

三、依赖倒置(倒转)

🍬 ① 高层模块不应该依赖低层模块(类),二者都应该依赖于抽象(接口)
🍬 ② 抽象(接口)不应该依赖细节(实现类),而是细节依赖于抽象
🍬 ③ 依赖倒置的中心思想是:面向接口(抽象)编程
🍬 ④ 依赖倒置设计理念:相对于细节的多变性,抽象的东西要稳定得多。以抽象为基础搭建的架构比以细节为基础搭建的架构要稳定
🍬 ⑤ 使用接口或抽象类的作用是:制定规范(协议);把展现细节的任务交给接口的实现类

接口 ➡️ 抽象
实现类 ➡️ 细节

public class DependencyInversion {public static void main(String[] args) {Person person = new Person();person.sendMessage(new QQMessage("用QQ问候一下小明"));person.sendMessage(new WechatMessage("用微信问候一下小明"));}
}class Person {public void sendMessage(QQMessage qqMessage) {System.out.println(qqMessage.buildMessage());}public void sendMessage(WechatMessage wechatMessage) {System.out.println(wechatMessage.buildMessage());} 
}class QQMessage {private String message;public QQMessage(String message) {this.message = message;}public String buildMessage() {return "QQ Message: " + message;}
}class WechatMessage {private String message;public WechatMessage(String message) {this.message = message;}public String buildMessage() {return "Wechat Message: " + message;}
} 

在这里插入图片描述
在这里插入图片描述

😰 假如版本升级,还想发送 抖音消息 的话:① 需要创建一个 TiktokMessage 类;② 需要在 Person 类中再重载一个 sendMessage(TiktokMessage tiktokMessage) 方法
😰 每次版本升级,增加新的发送消息的方式的时候对代码的改动非常大。假如不止一个用户(不仅仅只有 Person 类),哪改动就更加巨大了


面向接口编程:

在这里插入图片描述

public class DependencyInversion {public static void main(String[] args) {Person person = new Person();person.sendMessage(new QQMessage("用QQ问候一下小明"));person.sendMessage(new WechatMessage("用微信问候一下小明"));}
}class Person {public void sendMessage(IMessage iMessage) {System.out.println(iMessage.buildMessage());}
}interface IMessage {String buildMessage();
}/*** 发送 QQ 消息*/
class QQMessage implements IMessage {private String message;public QQMessage(String message) {this.message = message;}@Overridepublic String buildMessage() {return "QQ Message: " + message;}
}/*** 发送微信消息*/
class WechatMessage implements IMessage {private String message;public WechatMessage(String message) {this.message = message;}public String buildMessage() {return "Wechat Message: " + message;}
}

四、里氏替换

继承优点: 🍬 提高代码的复用性(子类继承父类后可使用父类的非 private 关键字修饰的的成员)

public class Animal {public void eat() {System.out.println("Animal - public void eat()");}protected void drink() {System.out.println("Animal - protected void drink()");}void play() {System.out.println("Animal - void play()");}
}class Dragon extends Animal {public void use() {eat(); // Animal - public void eat()drink(); // Animal - protected void drink()play(); // Animal - void play()}public static void main(String[] args) {Dragon dragon = new Dragon();dragon.use();}
}

继承缺点: 🍬 ① 继承关系过去庞大的话,整个代码结构会很乱
🍬 ② 代码耦合性变高
🍬 ③ 代码稳定性变差(子类可以重写父类的方法。重写之后,运行时究竟调用的是父类的方法还是子类重写的方法很难判断)
🍬 ④ 如果有多处直接使用父类方法的实现,但凡父类方法修改了,所有依赖该父类方法的地方都得考虑兼容性(考虑代码是否会产生 bug)


(1) 子类可以实现父类的抽象方法,【不要覆盖父类的非抽象方法】 (2) 子类可以可以增加自己特有的实现,不要影响父类的非抽象方法【你用父类的可以,但不要改】 (3) 子类方法重载父类方法的时候,方法的形参要比父类方法的形参更宽松(父类方法的形参得是子类方法的形参的父类型)
public class Main {public static void main(String[] args) {Parent parent = new Parent();parent.m2(new ArrayList<>());Son son = new Son();son.m2(new ArrayList<>());/*Parent - m2(ArrayList)Parent - m2(ArrayList)*/}
}class Parent {public void m1() {System.out.println("Parent - m1()");}public void m2(ArrayList list) {System.out.println("Parent - m2(ArrayList)");}
}class Son extends Parent {// 不符合里氏替换原则(子类不应该重写父类的非抽象方法)@Overridepublic void m1() {System.out.println("Son - m1()");}// 子类重载父类的方法(子类重载的方法的参数类型要比父类被重载的方法的参数类型大)// 这样才符合里氏替换原则, 子类增加代码(如新增一个方法)不会影响父类方法的使用public void m2(List list) {System.out.println("Son - m2(List)");}
}
(4) 子类实现父类的抽象方法的时候,方法的返回值应比父类的更加严格

五、迪米特法则(Law of Demeter)

🍬 只与你的直接朋友交谈,不与陌生人交谈【降低类与类之间的耦合】

A ➡️ B ➡️ C
① A 和 B 是直接朋友
② B 和 C 是直接朋友
③ A 和 C 是陌生人
④ 若 A 想使用 C 中的方法,需要通过 B

🍬 直接朋友:
① 当前对象本身
② 当前对象的成员变量
③ 当前对象的成员方法的返回类型
④ 当前对象的成员方法的参数

class Parent {public void m() {// 在方法内部 new 出来的是非直接朋友User user = new User();}
} class User {}

六、开闭

🎄 Open Close Principe:软件对象(类、模块、方法等)应该对扩展开放,对修改关闭
🎄 用抽象构建框架,用实现扩展细节
🎄 开放 服务方 的拓展,关闭 消费方 的修改


public class OpenClosePrincipe {public static void main(String[] args) {// 对消费方的修改关闭// 尽量少修改原先的代码MilkTeaFactory factory = new MilkTeaFactory();factory.makeMilkTea(MilkTeaType.APPLE);factory.makeMilkTea(MilkTeaType.BANANA);}
}interface MilkTeaAble {}class AppleMilkTea implements MilkTeaAble {public AppleMilkTea() {System.out.println("苹果奶茶");}
}class BananaMilkTea implements MilkTeaAble {public BananaMilkTea() {System.out.println("香蕉奶茶");}
}enum MilkTeaType {APPLE, BANANA
}class MilkTeaFactory {public MilkTeaAble makeMilkTea(MilkTeaType type) {switch (type) {case APPLE:return new AppleMilkTea();case BANANA:return new BananaMilkTea();}return null;}
}

public class OpenClosePrincipe {// 修改依赖的类型(消费方代码没有修改)private static MilkTeaAble factory = new WatermelonMilkTeaFactory();public static void main(String[] args) {factory.milkTea();}
}interface MilkTeaAble {void milkTea();
}class AppleMilkTeaFactory implements MilkTeaAble {@Overridepublic void milkTea() {System.out.println("苹果奶茶");}
}class BananaMilkTeaFactory implements MilkTeaAble {@Overridepublic void milkTea() {System.out.println("香蕉奶茶");}
}class WatermelonMilkTeaFactory implements MilkTeaAble {@Overridepublic void milkTea() {System.out.println("西瓜奶茶");}
}

七、合成复用

📖 通过对象 组合、聚合、依赖 达成代码复用,而不是继承

class Animal {public void eat() {System.out.println("Animal - eat()");}public void drink() {System.out.println("Animal - drink()");}public void play() {System.out.println("Animal - play()");}
}/*** 继承(不推荐, 不符合合成复用原则)*/
class People1 extends Animal {public void use() {eat();drink();play();}
}/*** 依赖(推荐)*/
class People2 {public void use(Animal animal) {animal.eat();animal.drink();animal.play();}
}/*** 聚合(推荐)*/
class People3 {private Animal animal;public void setAnimal(Animal animal) {this.animal = animal;}public void use() {animal.eat();animal.drink();animal.play();}
}/*** 组合(推荐)*/
class People4 {private Animal animal = new Animal();public void use() {animal.eat();animal.drink();animal.play();}
}

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
有效的括号 一、题目 给定一个只包括 '(',')','{','}'...
【Ctfer训练计划】——(三... 作者名:Demo不是emo  主页面链接:主页传送门 创作初心ÿ...