• 设计模式——七大设计原则


    设计模式的目的

    设计模式体现了代码的耦合性,内聚性以及可维护性,可扩展性,重用性,灵活性。

    • 代码重用性(形同功能的代码,不用多次编写)
    • 可读性(编程规范性,便于其他程序员的阅读和理解)
    • 可扩展性(当需要增加新的功能时,非常的方便)
    • 可靠性(当我们增加新的功能后,对原来的功能没有影响)
    • 使程序呈现高内聚,低耦合的特性

    一、单一职责原则(Single Responsibility)

    即一个类应该只负责一项职责

    1. package com.taoke.designPattern.principle;
    2. /**
    3. * 单一职责原则
    4. *
    5. * @author taoke
    6. * @date 2022/7/29
    7. */
    8. public class SingleResponsibility {
    9. public static void main(String[] args) {
    10. Vehicle vehicle = new Vehicle();
    11. vehicle.run("飞机");
    12. vehicle.run("摩托车");
    13. vehicle.run("汽车");
    14. }
    15. }
    16. /**
    17. * 交通工具中的run方法中违反 单一职责原则
    18. * 应该将交通方式的不同,分解成不同的类
    19. *
    20. * @author taoke
    21. * @date 2022/7/29
    22. */
    23. class Vehicle {
    24. public void run(String string) {
    25. System.out.println(string + "在公路上运行。。。");
    26. }
    27. }

    单一职责原则注意事项和细节

    • 降低类的复杂度,一个类只负责一项职责
    • 提高类的可读性,可维护性
    • 降低变更引起的风险
    • 通常情况下,应当遵守单一职责原则, 只有逻辑足够简单,才可以在方法级违反单一职责原则

    二、接口隔离原则(Interface Segregation)

     客户端不应该依赖它不需要的接口,即对一个类的依赖应该建立在最小的接口

          类A通过Interface会依赖类B,但是类A只会使用到接口的1,2,3方法;类C通过Interface会依赖类B,但是类C只会使用到接口的4,5方法;那么接口Interface对于类A和类C来说不是最小接口

          根据接口隔离原则,应该将接口Interface拆分为多个接口,类A和类C应该与他们需要的接口产生联系

    1. package com.taoke.designPattern.principle;
    2. /**
    3. * 接口隔离原则
    4. *
    5. * @author taoke
    6. * @date 2022/7/29
    7. */
    8. public class InterfaceSegregation {
    9. interface Interface1 {
    10. void operate1();
    11. void operate2();
    12. void operate3();
    13. }
    14. interface Interface2 {
    15. void operate4();
    16. void operate5();
    17. }
    18. class A implements Interface1 {
    19. @Override
    20. public void operate1() {}
    21. @Override
    22. public void operate2() {}
    23. @Override
    24. public void operate3() {}
    25. }
    26. class C implements Interface2 {
    27. @Override
    28. public void operate4() {}
    29. @Override
    30. public void operate5() {}
    31. }
    32. class B implements Interface1, Interface2 {
    33. @Override
    34. public void operate1() {}
    35. @Override
    36. public void operate2() {}
    37. @Override
    38. public void operate3() {}
    39. @Override
    40. public void operate4() {}
    41. @Override
    42. public void operate5() {}
    43. }
    44. }

    三、依赖倒转原则(Dependence Inversion)

    • 高层模块不应该依赖低层模块,二者都应该依赖其抽象(缓冲层)

    • 抽象不应该依赖细节,细节应该依赖抽象

    • 依赖倒转(倒置)的中心思想是面向接口编程

    • 依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在java中, 抽象指的是接口或抽象类,细节就是具体的实现类

    • 使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成

    依赖关系三种传递方式

    • 接口传递(依赖)
    • 构造方法传递(依赖)
    • setter方式传递(聚合)
    1. package com.taoke.designPattern.principle;
    2. /**
    3. * 依赖倒转原则
    4. * 实现Person接收不同类型消息的功能,引入一个抽象的接口IReceive,表示接收者
    5. * 这样Person类与接口IReceive发生依赖
    6. *
    7. * @author taoke
    8. * @date 2022/7/29
    9. */
    10. public class DependenceInversion {
    11. public static void main(String[] args) {
    12. Person person = new Person();
    13. person.receive(new Email());
    14. person.receive(new Wechat());
    15. }
    16. }
    17. interface IReceive {
    18. String getInfo();
    19. }
    20. class Person {
    21. public void receive(IReceive receive) {
    22. System.out.println(receive.getInfo());
    23. }
    24. }
    25. class Email implements IReceive {
    26. @Override
    27. public String getInfo() {
    28. return "电子邮件信息";
    29. }
    30. }
    31. class Wechat implements IReceive {
    32. @Override
    33. public String getInfo() {
    34. return "微信信息";
    35. }
    36. }

    四、里氏替换原则(Liskov Substitution)

    • 里氏替换原则(Liskov Substitution Principle)在1988年,由麻省理工学院一位姓里的女士提出

    • 如果对每个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。换句话说,所有引用基类的地方必须能透明地使用其子类的对象

    • 在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法

    • 继承实际上让两个类耦合性增强了,给程序带来侵入性。在适当的情况下,可以通过聚合,组合,依赖来解决问题

    • 继承包含这样一层含义:父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然它不强制要求所有的子类必须遵循这些契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏

    1. package com.taoke.designPattern.principle;
    2. /**
    3. * 里氏替换原则
    4. * A类没有重写父类的方法,B类重写了父类的方法
    5. * A类继承父类,使用父类的方法,B类继承父类,并重写了父类的方法,造成错误
    6. *
    7. * @author taoke
    8. * @date 2022/7/29
    9. */
    10. public class LiskovSubstitution {
    11. public static void main(String[] args) {
    12. A a = new A();
    13. System.out.println("2-1=" + a.func1(2, 1));
    14. B b = new B();
    15. System.out.println("2-1=" + b.func1(2, 1));
    16. }
    17. }
    18. class Base {
    19. /**
    20. * 父类定义了一个基础方法
    21. *
    22. * @param num1 数值1
    23. * @param num2 数值2
    24. * @return
    25. */
    26. public int func1(int num1, int num2) {
    27. return num1 - num2;
    28. }
    29. }
    30. class A extends Base {
    31. }
    32. class B extends Base {
    33. /**
    34. * 重写了父类方法,造成原有方法发生改变。
    35. *
    36. * @param num1 数值1
    37. * @param num2 数值2
    38. * @return
    39. */
    40. @Override
    41. public int func1(int num1, int num2) {
    42. return num1 + num2;
    43. }
    44. }

    五、开闭原则 OCP(Open Closed)

    • 开闭原则(Open Closed Principle) 是编程中最基础、最重要的设计原则
    • 一个软件实体,比如类,模块和函数应该对提供方扩展开放,对使用方修改关闭。用抽象构建框架,用实现扩展细节
    • 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化
    • 编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则
    1. package com.taoke.designPattern.principle;
    2. /**
    3. * 开闭原则
    4. *
    5. * @author taoke
    6. * @date 2022/7/29
    7. */
    8. public class OpenClosed {
    9. public static void main(String[] args) {
    10. Use use = new Use();
    11. use.drawShape(new Triangle());
    12. use.drawShape(new Circle());
    13. //只需要让 此类继承 抽象类,子类实现具体方法 OCP原则
    14. use.drawShape(new OtherGraphics());
    15. }
    16. }
    17. class Use {
    18. public void drawShape(Shape shape) {
    19. shape.draw();
    20. }
    21. }
    22. abstract class Shape {
    23. public abstract void draw();
    24. }
    25. class Triangle extends Shape {
    26. @Override
    27. public void draw() {
    28. System.out.println("子类实现具体功能:三角形");
    29. }
    30. }
    31. class Circle extends Shape {
    32. @Override
    33. public void draw() {
    34. System.out.println("子类实现具体功能:圆形");
    35. }
    36. }
    37. class OtherGraphics extends Shape {
    38. @Override
    39. public void draw() {
    40. System.out.println("子类实现具体功能:任何形状");
    41. }
    42. }

    六、迪米特法则(Demeter)

    • 一个对象应该对其他对象保持最少的了解
    • 类与类关系越密切,耦合度越大。
    • 迪米特法则(Demeter Principle)又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供public方法,不对外泄露任何信息
    • 迪米特法则更简单的定义:只与直接的朋友通信
    • 直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖,关联,组合,聚合 等。其中,我们称出现成员变量,方法参数,方法返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变量的形式出现在类的内部

    直接的朋友

    1. class A{
    2. B b;//全局变量 - 直接朋友
    3. public B m1(){} //方法返回值 - 直接朋友
    4. public void m2(B b){}//方法入参 - 直接朋友
    5. public void m3(){
    6. B b1 = new B();// 局部变量 非直接朋友
    7. }
    8. }

    1. package com.taoke.principle;
    2. import java.util.ArrayList;
    3. import java.util.List;
    4. /**
    5. * 迪米特法则
    6. * 学校管理类应该最少知道学院员工的信息
    7. * printAllEmployee 方法中,只提供方法,不把具体实现放在其他类里面。
    8. *
    9. * @author taoke
    10. * @date 2022/7/29
    11. */
    12. public class Demeter {
    13. public static void main(String[] args) {
    14. SchoolManager schoolManager = new SchoolManager();
    15. schoolManager.printAllEmployee(new CollegeManager());
    16. }
    17. }
    18. /**
    19. * 学院员工类
    20. */
    21. class CollegeEmployee {
    22. private String id;
    23. public String getId() {
    24. return id;
    25. }
    26. public void setId(String id) {
    27. this.id = id;
    28. }
    29. }
    30. /**
    31. * 管理学院员工的管理类
    32. */
    33. class CollegeManager {
    34. //返回学院的所有员工
    35. public List getAllEmployee() {
    36. List list = new ArrayList<>();
    37. //这里我们增加了10 个员工到list ,
    38. for (int i = 0; i < 10; i++) {
    39. CollegeEmployee emp = new CollegeEmployee();
    40. emp.setId("学院员工id " + i);
    41. list.add(emp);
    42. }
    43. return list;
    44. }
    45. /**
    46. * 打印学院所有的员工
    47. */
    48. public void printCollegeEmployee() {
    49. List list = this.getAllEmployee();
    50. System.out.println("---学院员工----");
    51. for (CollegeEmployee e : list) {
    52. System.out.println(e.getId());
    53. }
    54. }
    55. }
    56. /**
    57. * 学校员工类
    58. */
    59. class SchoolEmployee {
    60. private String id;
    61. public String getId() {
    62. return id;
    63. }
    64. public void setId(String id) {
    65. this.id = id;
    66. }
    67. }
    68. /**
    69. * 学校管理类
    70. */
    71. class SchoolManager {
    72. //返回学校总部的员工
    73. public List getAllEmployee() {
    74. List list = new ArrayList<>();
    75. //这里我们增加了5个员工到list
    76. for (int i = 0; i < 5; i++) {
    77. SchoolEmployee emp = new SchoolEmployee();
    78. emp.setId("学校总部员工id= " + i);
    79. list.add(emp);
    80. }
    81. return list;
    82. }
    83. /**
    84. * 该方法完成输出学校总部和学院员工信息(id)
    85. *
    86. * @param collegeManager 管理学院员工的管理类
    87. */
    88. void printAllEmployee(CollegeManager collegeManager) {
    89. //只提供方法,不把具体实现放在其他类里面。
    90. collegeManager.printCollegeEmployee();
    91. //获取到学校总部员工
    92. List list = this.getAllEmployee();
    93. System.out.println("------学校总部员工------");
    94. for (SchoolEmployee e : list) {
    95. System.out.println(e.getId());
    96. }
    97. }
    98. }

    七、合成复用原则(Composite Reuse)

    合成复用原则 尽量使用组合/聚合的方式,而不是使用继承。

    1. package com.taoke.principle;
    2. /**
    3. * 合成复用原则
    4. *
    5. * @author taoke
    6. * @date 2022/7/29
    7. */
    8. public class CompositeReuse {
    9. public static void main(String[] args) {
    10. System.out.println("------依赖------");
    11. B b = new B();
    12. b.Operation1(new A());
    13. System.out.println("------聚合------");
    14. b.setA(new A());
    15. b.Operation2();
    16. System.out.println("------组合------");
    17. b.Operation3();
    18. }
    19. static class A {
    20. void Operation1() {
    21. System.out.println("A Operation1");
    22. }
    23. void Operation2() {
    24. System.out.println("A Operation2");
    25. }
    26. void Operation3() {
    27. System.out.println("A Operation3");
    28. }
    29. }
    30. /**
    31. * 如果只是需要用到 A类的方法,尽量不要使用继承。而是使用,依赖,聚合,组合的方式
    32. */
    33. static class B {
    34. /**
    35. * 依赖
    36. *
    37. * @param a 实例a
    38. */
    39. void Operation1(A a) {
    40. a.Operation1();
    41. a.Operation2();
    42. a.Operation3();
    43. }
    44. //==============================================================
    45. A a;
    46. public void setA(A a) {
    47. this.a = a;
    48. }
    49. /**
    50. * 聚合
    51. */
    52. void Operation2() {
    53. a.Operation1();
    54. a.Operation2();
    55. a.Operation3();
    56. }
    57. //==============================================================
    58. A a1 = new A();
    59. /**
    60. * 组合
    61. */
    62. void Operation3() {
    63. a1.Operation1();
    64. a1.Operation2();
    65. a1.Operation3();
    66. }
    67. }
    68. }

    类与类之间的关系

    依赖(Dependence)

    只要是在类中用到了对方,那么他们之间就存在依赖关系。

    • 类的成员属性;
    • 方法的返回类型;
    • 方法接收的参数类型;
    • 方法中使用到。
    1. package com.taoke.relation;
    2. /**
    3. * 依赖关系
    4. * 1、类中用到了对方
    5. * 2、类的成员属性
    6. * 3、方法的返回类型
    7. * 4、方法接收的参数类型
    8. * 5、方法中使用到
    9. *
    10. * @author taoke
    11. * @date 2022/7/29
    12. */
    13. public class Dependence {
    14. /**
    15. * 类的成员属性
    16. */
    17. A a;
    18. public A save(B b) {//方法接收的参数类型
    19. System.out.println();
    20. //方法中使用到
    21. A a = new A();
    22. //方法的返回类型
    23. return a;
    24. }
    25. }
    26. class A {
    27. }
    28. class B {
    29. }

    继承(泛化 Generalization)

    泛化关系实际上就是继承关系,依赖关系的特例

    1. package com.taoke.relation;
    2. /**
    3. * 继承
    4. *
    5. * @author taoke
    6. * @date 2022/7/29
    7. */
    8. public class Generalization extends Base {
    9. @Override
    10. public void get(Object oId) {
    11. }
    12. @Override
    13. public void put(Object oName) {
    14. }
    15. }
    16. abstract class Base {
    17. abstract public void get(Object oId);
    18. abstract public void put(Object oName);
    19. }

    实现(Realization)

    实现关系实际上就是 A类 实现 B接口,依赖关系的特例。

    1. package com.taoke.relation;
    2. /**
    3. * 实现
    4. *
    5. * @author taoke
    6. * @date 2022/7/29
    7. */
    8. public class Implementation implements Interface {
    9. @Override
    10. public void init() {
    11. System.out.println("init");
    12. }
    13. }
    14. interface Interface {
    15. void init();
    16. }

    关联(Association)

    类与类之间的关系,依赖关系的特例。关联具有导航性,即双向关系或单向关系。

    1. package com.taoke.relation;
    2. /**
    3. * 关联
    4. *
    5. * @author taoke
    6. * @date 2022/7/29
    7. */
    8. public class Association {
    9. class Person {
    10. private IDCard idCard;
    11. }
    12. class IDCard {
    13. private Person person;
    14. }
    15. }

    聚合(Aggregation)

    表示的是整体和部分的关系,整体与部分可以分开,关联关系的特例;聚合关系是关联关系的特例,所以他具有关联的导航性与多重性。

    1. package com.taoke.relation;
    2. /**
    3. * 聚合
    4. *
    5. * @author taoke
    6. * @date 2022/7/29
    7. */
    8. public class Aggregation {
    9. class Computer {
    10. private Mouse mouse;
    11. private Keyboard keyboard;
    12. public void setMouse(Mouse mouse) {
    13. this.mouse = mouse;
    14. }
    15. public void setKeyboard(Keyboard keyboard) {
    16. this.keyboard = keyboard;
    17. }
    18. }
    19. class Mouse {
    20. }
    21. class Keyboard {
    22. }
    23. }

    组合(Composite)

    整体与部分的关系,但是整体与部分不可以分开,关联关系的特例。级联删除就是组合关系。

    1. package com.taoke.relation;
    2. /**
    3. * 组合
    4. *
    5. * @author taoke
    6. * @date 2022/7/29
    7. */
    8. public class Composite {
    9. public class Computer {
    10. private CPU cpu = new CPU();
    11. private SSD ssd = new SSD();
    12. }
    13. static class CPU {
    14. }
    15. static class SSD {
    16. }
    17. }

  • 相关阅读:
    Lambda总结
    CTFHub技能树 Web-SQL注入详解
    【PACS系统源码】与医院HIS系统双向数据交换,实现医学影像集成与影像后处理功能
    rabbitmq 交换机相关实例代码
    NLog配置文件详解
    简明SQL截断和偏移指南:掌握 LIMIT 和 OFFSET 实现数据筛选
    odoo关于计算字段store=True时导致的安装/更新时间较长问题的解决方案
    计算机组成原理-输入输出系统(持续更新中)
    C语言数组清零----使用memset函数
    深入浅出计算机组成原理04-穿越功耗墙,我们该从哪些方面提升“性能”?
  • 原文地址:https://blog.csdn.net/Strange_boy/article/details/126049326