• 设计模式-原则


    七大设计原则

    所有原则都为了降低类之间的耦合。

    一、单一职责

    • 降低类复杂度,一个类负责一项职责。
    • 提高类可读性,可维护性。
    • 降低变更引起的风险。
    • 通常情况下,应遵循。只有逻辑够简单,才可在代码级别(通过if...else...)违反;只有类方法数量足够少,才可在方法级别(定义不同方法实现不同操作)保持单一职责

    二、接口隔离

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

    如:接口Interface有1、2、3等方法,B类实现了Interface接口。A类通过接口依赖B类,但A只需要用到接口中1、2两个方法。
    按接口隔离原则应把接口分离:
    1、Interface分成两个接口:Interface1有方法1、2;Interface2有方法3;
    2、B类实现接口Interface1即可;
    3、如果后续某个类需要用到Interface中1、2、3方法,再让B实现Interface2。
    

    三、依赖倒转

    • 底层模块尽量使用抽象类或接口,或两者都有,程序稳定性更好
    • 变量声明类型尽量用抽象类或接口。这样变量引用和实际对象存在一个缓冲层(即接口),利用程序扩展和优化。
    • 继承时遵循里氏替换原则
    //客户使用端
    public class Client {
       public static void main(String[] args){
           OpenAndCloseTv openAndCloseTv = new OpenAndCloseTv();
           openAndCloseTv.open(new Changhong());
           openAndCloseTv.open(new Kangjia());
       }
    }
    
    //开关接口
    interface IOpenAndCloseTv{
        public void open(ITV tv);
    }
    //实现
    class OpenAndCloseTv implements IOpenAndCloseTv{
        @Override
        public void open(ITV tv) {
            tv.play();
        }
    }
    
    //tv类型接口
    interface ITV{
        public void play();
    }
    //定义不同tv类型
    class Changhong implements ITV{
        @Override
        public void play() {
            System.out.println("长虹电视打开。。。");
        }
    }
    class Kangjia implements ITV{
        @Override
        public void play() {
            System.out.println("康佳电视打开了。。。");
        }
    }
    
    结果:
    长虹电视打开。。。
    康佳电视打开了。。。
    

    依赖关系传递三种方式:

    • 接口传递
    //开关接口
    interface IOpenAndCloseTv{
        public void open(ITV tv);
    }
    //实现
    class OpenAndCloseTv implements IOpenAndCloseTv{
        @Override
        public void open(ITV tv) {
            tv.play();
        }
    }
    ---------------------使用
    public static void main(String[] args){
        OpenAndCloseTv openAndCloseTv = new OpenAndCloseTv();
        openAndCloseTv.open(new Changhong());
        openAndCloseTv.open(new Kangjia());
    }
    
    • 构造方法传递
    //开关接口
    interface IOpenAndCloseTv{
        public void open();
    }
    //实现
    class OpenAndCloseTv implements IOpenAndCloseTv{
        private ITV tv; // 成员变量
        public OpenAndCloseTv(ITV tv){
            this.tv = tv;
        }
        @Override
        public void open() {
            tv.play();
        }
    }
    ---------------------使用
    public static void main(String[] args){
        OpenAndCloseTv openAndCloseTv = new OpenAndCloseTv(new Changhong());
        openAndCloseTv.open();
        OpenAndCloseTv openAndCloseTv2 = new OpenAndCloseTv(new Kangjia());
        openAndCloseTv2.open();
    }
    
    • setter方法传递
    //开关接口
    interface IOpenAndCloseTv{
        public void open();
        public void setTv(ITV tv)
    }
    //实现
    class OpenAndCloseTv implements IOpenAndCloseTv{
        private ITV tv; // 成员变量
        
        @Override    
        public void setTv(ITV tv){
            this.tv = tv;
        }
        @Override
        public void open() {
            tv.play();
        }
    }
    ---------------------使用
    public static void main(String[] args){
        OpenAndCloseTv openAndCloseTv = new OpenAndCloseTv();
        openAndCloseTv.setTv(new Changhong();
        openAndCloseTv.open();
    }
    

    四、里氏替换

    一个类被其他类所继承,该类修改时,必须考虑所有子类。父类修改后,所有子类有可能产生故障。
    如何正确使用继承?--》里氏替换原则

    • 使用基类的地方必须能透明使用子类对象。
    • 使用继承时,在子类中尽量不要重写父类方法。
    • 继承实际让两个类耦合性增强,在适当情况下可通过聚合,组合,依赖解决问题,不要通过继承。

    如某类子类需要改写父类方法,应把子类提升和父类一样的档次,把共同方法抽取,让子类和父类继承同一个更基层的基类。

    若要改变父,就不要做父子关系了,做兄弟关系吧。

    public class Liskov {
        public static void main(String[] args){
    
            A a = new A();
            System.out.println("11-3="+a.func1(11,3));
            System.out.println("1-8="+a.func1(1,8));
    
            System.out.println("----------------------------");
            B b = new B();
            System.out.println("11-3="+b.func1(11,3)); // 这里其实已经覆写父类方法了,所以-的话是错误的。
            System.out.println("1-8="+b.func1(1,8));
            System.out.println("11+3+9="+b.func2(11,3));
        }
    }
    
    class A {
        public int func1(int num1,int num2){
            return num1 - num2;
        }
    }
    class B extends A{
        // 重新了A 类方法,可能是无意识
        @Override
        public int func1(int a,int b){
            return a + b;
        }
        public int func2(int a,int b){
            return func1(a,b)+9;
        }
    }
    

    通过里氏替换原则:

    public class Liskov2 {
        public static void main(String[] args){
    
            A a = new A();
            System.out.println("11-3="+ a.func1(11,3));
            System.out.println("1-8="+ a.func1(1,8));
    
            System.out.println("----------------------------");
            B b = new B();
            System.out.println("11+3="+ b.func1(11,3)); // 这里其实已经覆写父类方法了,所以-的话是错误的。
            System.out.println("1+8="+ b.func1(1,8));
            System.out.println("11+3+9="+ b.func2(11,3));
    
            System.out.println("11-3="+ b.func3(11,3)); //实际调用的是A类的方法。
        }
    }
    class base{
        //共有的更基础的方法、成员变量定义到该类
        //如:
        public int funcMult(int num1,int num2){
            return num1*num2;
        }
    }
    class A extends base{
        public int func1(int num1,int num2){
            return num1 - num2;
        }
    }
    class B extends base{
        private A  aobj = new A(); //通过组合方式解决继承问题-step1
    
        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 aobj.func1(a,b); //通过组合方式解决继承问题-step2
        }
    }
    
    ----------结果
    11-3=8
    1-8=-7
    ----------------------------
    11+3=14
    1+8=9
    11+3+9=23
    11-3=8
    

    五、开闭原则

    • 最基础、最重要的设计原则
    • 一个实体如类、模块、函数应【扩展开发(对提供方),修改关闭(对使用方)】。抽象构建框架,实现扩展细节。
    • 软件需变化,尽量通过扩展代码而不是修改已有代码。
    • 编程中遵循其他原则,及设计模式目的就是遵循开闭原则(核心)。

    不遵循OCP原则:

    public class Ocp {
        public static void main(String[] args){
            GraphicEditor graphicEditor = new GraphicEditor();
            graphicEditor.drawShape(new Rectangle());
            graphicEditor.drawShape(new Circle());
            graphicEditor.drawShape(new Triangle());
        }
    }
    
    //这是一个用于绘图的类 [使用方]
    class GraphicEditor {
        //接收Shape对象,调用draw方法
        public void drawShape(Shape s) {
            if (s.m_type == 1){
                drawRectangle();
            }else if (s.m_type == 2){
                drawCircle();
            }else if (s.m_type == 3){ //新增图形,使用方需要进行修改
                drawTriangle();
            }
        }
        public void drawRectangle(){ System.out.println(" 绘制矩形 "); }
        public void drawCircle() { System.out.println(" 绘制圆形 "); }
        //新增图形,使用方需要进行修改
        public void drawTriangle() { System.out.println(" 绘制三角形 "); }
    }
    
    //定义各种图形
    //Shape类,基类,
    abstract class Shape {
        int m_type;
    }
    //矩形
    class Rectangle extends Shape {
        Rectangle() { super.m_type = 1; }
    }
    //圆形
    class Circle extends Shape {
        Circle() { super.m_type = 2; }
    }
    //新增三角形
    class Triangle extends Shape {
        Triangle() { super.m_type = 3; }
    }
    
    ----------结果
     绘制矩形 
     绘制圆形 
     绘制三角形 
    

    遵循OCP原则:

    public class Ocp {
        public static void main(String[] args) {
            //使用看看存在的问题
            GraphicEditor graphicEditor = new GraphicEditor();
            graphicEditor.drawShape(new Rectangle());
            graphicEditor.drawShape(new Circle());
            graphicEditor.drawShape(new Triangle());
            graphicEditor.drawShape(new OtherGraphic());
        }
    }
    
    //这是一个用于绘图的类 [使用方]
    class GraphicEditor {
        //接收Shape对象,调用draw方法
        public void drawShape(Shape s) { s.draw(); //只需要新增图形就可以,不需要修改代码。 }
    }
    
    //Shape类,遵守ocp原则通过创建一个基类,
    abstract class Shape {
        public abstract void draw();//抽象方法
    }
    
    //绘制不同图像时直接扩展,而不是修改。
    //矩形
    class Rectangle extends Shape {
        @Override
        public void draw() {System.out.println(" 绘制矩形 "); }
    }
    //圆形
    class Circle extends Shape {
        @Override
        public void draw() { System.out.println(" 绘制圆形 "); }
    }
    //新增画三角形
    class Triangle extends Shape {
        @Override
        public void draw() { System.out.println(" 绘制三角形 "); }
    }
    //新增一个图形
    class OtherGraphic extends Shape {
        @Override
        public void draw() { System.out.println(" 绘制其它图形 "); }
    }
    
    ----------结果
     绘制矩形 
     绘制圆形 
     绘制三角形 
     绘制其它图形 
    

    六、迪米特法则

    核心是为了降低类之间的耦合。

    • 一个对象对其他对象保持最少了解。
    • 也叫【最少知道原则】,即一个被依赖的类不管多复杂,尽量将逻辑封装类内部,对外只提供public方法,不对外泄露任何信息。
    • 更简单定义:只与【直接朋友(两对象耦合即朋友,出现在成员变量,方法参数,方法返回值中的类为直接的朋友,出现在局部变量中的类不是直接的朋友,称陌生类)】通信。
    • 耦合方式:依赖、关联、组合、聚合等。
    • 陌生类最好不要以局部变量出现在类内部。

    非迪米特法则:

    //客户端
    public class Demeter1 {
        public static void main(String[] args) {
            //创建了一个 SchoolManager 对象
            SchoolManager schoolManager = new SchoolManager();
            //输出学院的员工id 和  学校总部的员工信息
            schoolManager.printAllEmployee(new 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 void setId(String id) { this.id = id; }
        public String getId() { return id; }
    }
    
    //学院员工管理类
    class CollegeManager {
        //返回学院的所有员工
        public List<CollegeEmployee> getAllEmployee() {
            List<CollegeEmployee> list = new ArrayList<CollegeEmployee>();
            for (int i = 0; i < 10; i++) { //这里我们增加了10个员工到 list
                CollegeEmployee emp = new CollegeEmployee();
                emp.setId("学院员工id= " + i);
                list.add(emp);
            }
            return list;
        }
    }
    //学校管理类
    //分析 SchoolManager 类的直接朋友类有哪些 Employee、CollegeManager
    //CollegeEmployee 不是 直接朋友 而是一个陌生类,这样违背了 迪米特法则 
    class SchoolManager {
        //返回学校总部的员工
        public List<Employee> getAllEmployee() {
            List<Employee> list = new ArrayList<Employee>();
            for (int i = 0; i < 5; i++) { //这里我们增加了5个员工到 list
                Employee emp = new Employee();
                emp.setId("学校总部员工id= " + i);
                list.add(emp);
            }
            return list;
        }
    
        //该方法完成输出学校总部和学院员工信息(id)
        void printAllEmployee(CollegeManager sub) {
            //分析问题
            //1. 这里的 CollegeEmployee 不是  SchoolManager的直接朋友
            //2. CollegeEmployee 是以局部变量方式出现在 SchoolManager
            //3. 违反了 迪米特法则 
    
            //获取学院员工
            List<CollegeEmployee> list1 = sub.getAllEmployee();
            System.out.println("------------学院员工------------");
            for (CollegeEmployee e : list1) {
                System.out.println(e.getId());
            }
            //获取学校总部员工
            List<Employee> list2 = this.getAllEmployee();
            System.out.println("------------学校总部员工------------");
            for (Employee e : list2) {
                System.out.println(e.getId());
            }
        }
    }
    -----------结果
    ------------学院员工------------
    学院员工id= 0
    学院员工id= 1
    学院员工id= 2
    学院员工id= 3
    学院员工id= 4
    学院员工id= 5
    学院员工id= 6
    学院员工id= 7
    学院员工id= 8
    学院员工id= 9
    ------------学校总部员工------------
    学校总部员工id= 0
    学校总部员工id= 1
    学校总部员工id= 2
    学校总部员工id= 3
    学校总部员工id= 4
    

    遵循迪米特法则:

    //客户端
    public class Demeter1 {
        public static void main(String[] args) {
            System.out.println("~~~使用迪米特法则的改进~~~");
            //创建了一个 SchoolManager 对象
            SchoolManager schoolManager = new SchoolManager();
            //输出学院的员工id 和  学校总部的员工信息
            schoolManager.printAllEmployee(new 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 void setId(String id) { this.id = id; }
        public String getId() { return id; }
    }
    
    //学院员工管理类
    class CollegeManager {
        //返回学院的所有员工
        public List<CollegeEmployee> getAllEmployee() {
            List<CollegeEmployee> list = new ArrayList<CollegeEmployee>();
            for (int i = 0; i < 10; i++) { //这里我们增加了10个员工到 list
                CollegeEmployee emp = new CollegeEmployee();
                emp.setId("学院员工id= " + i);
                list.add(emp);
            }
            return list;
        }
        //输出学院员工的信息
        public void printEmployee() {
            //获取到学院员工
            List<CollegeEmployee> list1 = getAllEmployee();
            System.out.println("------------学院员工------------");
            for (CollegeEmployee e : list1) {
                System.out.println(e.getId());
            }
        }
    }
    //学校管理类
    //分析 SchoolManager 类的直接朋友类有哪些 Employee、CollegeManager
    //CollegeEmployee 不是 直接朋友 而是一个陌生类,这样违背了 迪米特法则
    class SchoolManager {
        //返回学校总部的员工
        public List<Employee> getAllEmployee() {
            List<Employee> list = new ArrayList<Employee>();
    
            for (int i = 0; i < 5; i++) { //这里我们增加了5个员工到 list
                Employee emp = new Employee();
                emp.setId("学校总部员工id= " + i);
                list.add(emp);
            }
            return list;
        }
    
        //该方法完成输出学校总部和学院员工信息(id)
        void printAllEmployee(CollegeManager sub) {
            //分析问题
            //1. 将输出学院的员工方法,封装到CollegeManager
            sub.printEmployee();
    
            //获取到学校总部员工
            List<Employee> list2 = this.getAllEmployee();
            System.out.println("------------学校总部员工------------");
            for (Employee e : list2) {
                System.out.println(e.getId());
            }
        }
    }
    ----------结果
    ~~~使用迪米特法则的改进~~~
    ------------学院员工------------
    学院员工id= 0
    学院员工id= 1
    学院员工id= 2
    学院员工id= 3
    学院员工id= 4
    学院员工id= 5
    学院员工id= 6
    学院员工id= 7
    学院员工id= 8
    学院员工id= 9
    ------------学校总部员工------------
    学校总部员工id= 0
    学校总部员工id= 1
    学校总部员工id= 2
    学校总部员工id= 3
    学校总部员工id= 4
    

    七、合成复用原则

    • 尽量使用组合/聚合的方式,不是使用继承。

    类之间关系

    所有都可以归为依赖。

    • 依赖:
      • 通过方法参数传递依赖进来。
      • 类中用到了对方。
      • 类成员属性
      • 方法返回类型
      • 方法接收参数
      • 方法中使用
    • 泛化(继承):依赖关系的特例
      • 泛化关系就是继承关系。
    • 实现:依赖关系的特例
      • 类实现了某一接口。
    • 关联:依赖关系的特例。类与类之间的联系。
      • 具有导航性(单向或双向),多重性(一对一、一对多等)
      • 如人和身份证类
    • 聚合:关联关系的特例。
      • 添加类属性,通过方法设置进来。
      • 整体与部分的关系,整体与部分可以分开。即可有可无。
      • 导航性(a聚合b还是b聚合类),多重性(单聚合-聚合一个还是多聚合-聚合多个)
      • 如:人和身份证、台式主机和显示器。
    • 组合:关联关系的特例。
      • 构建属性直接new一个出来。
      • 整体与部分的关系,整体与部分不可分开。
      • 随着当前类创建而创建,销毁而销毁。
      • 如:人和自己的头
      • 级联删除即组合关系。

    设计原则核心思想

    • 找出可能需变化之处独立出来,不要和不需变化的代码混写。
    • 针对接口编程,不是具体实现类编程
    • 为交互对象之间低耦合设计努力
  • 相关阅读:
    python 保存两位小数
    python 临时文件
    Windows 10 右键 在此处打开 CMD
    windows 10 右键菜单注册表位置
    GoLand 设置与配置
    docker 相关指令
    ubuntu下python版本间切换
    gitLab本地推送到远程仓库的命令
    CSS的性能优化
    Web前端:11个让你代码整洁的原则
  • 原文地址:https://www.cnblogs.com/xiaoaiying/p/14163687.html
Copyright © 2020-2023  润新知