Siete principios del diseño de programas

En el proceso de desarrollo del programa, para que el código sea más robusto, revisable y mantenible, los siguientes siete principios se siguen generalmente durante el proceso de diseño:

  • Principio de apertura y cierre
  • Principio único
  • Principio de sustitución
  • Principio de dependencia
  • Principio de aislamiento de interfaz
  • Principio de lo mínimo
  • Principio de reutilización de composición / agregación

1. El principio de apertura y cierre

Definición: Una entidad de software como la función de clase y módulo debe estar abierta para extensión y cerrada para modificación. El énfasis está en el uso de la abstracción para construir el marco y la implementación para ampliar los detalles. Mejorar la capacidad de reutilización y mantenimiento del sistema de software nos ayuda a lograr una arquitectura de sistema estable y flexible. Ejemplos en la vida (sistema de trabajo flexible, debes trabajar al menos ocho horas al día, esto no se puede modificar, pero no hay regulación sobre cuándo venir y cuándo salir). La idea central para materializar el principio de apertura y cierre es enfrentarse a la programación abstracta.

Escenario de código: las aplicaciones web suelen tener filtros. Generalmente hay muchos. Cada uno es una colección de funciones. Generalmente, solo agregamos nuevos filtros para enriquecer nuestras funciones heredando Filter y agregando nuevas clases de implementación. En lugar de modificar el filtro original, agregamos nuevas características. Esto está abierto a extensión y cerrado a modificación.

2. Principio de inversión de la dependencia

Definición: Los módulos de alto nivel no deben depender de módulos de bajo nivel y ambos deben depender de sus abstracciones. La abstracción no debe depender de los detalles; los detalles deben depender de la abstracción. Para la programación de la interfaz, no programe para la implementación. A través de la abstracción incluyendo el uso de interfaces o clases abstractas, la realización de cada categoría o módulo puede ser independiente entre sí sin afectarse entre sí, logrando así el acoplamiento entre módulos. En pocas palabras, el programa debería depender de la interfaz en lugar de la clase de implementación.

Ventajas: Puede reducir el acoplamiento entre clases, mejorar la estabilidad del sistema, mejorar la legibilidad y el mantenimiento del código y reducir el riesgo de modificar programas.

Escenario de código: Geely está estudiando el MOOC en línea y está interesado en cursos de Java y cursos de front-end. Según la programación orientada a la implementación, hay una clase de entidad Geely con dos métodos: 1. El método de aprendizaje de Java; 2. Aprenda el método front-end.

/**
 *  Geely 类 => 低层模块
 */
public class Geely {
    public void studyJavaCourse(){
        System.out.println("Geely 在学习 Java 课程");
    }
 
    public void studyFECourse(){
        System.out.println("Geely 在学习 FE 课程");
    }
}
 
======================
/**
 * 测试类 <=> 应用层 => 高层模块
 */
public class Test {
    public static void main(String[] args) {
        Geely geely = new Geely();
        geely.studyFECourse();
        geely.studyJavaCourse();
    }
}
现在假如我还要学习 Python 课程,那么需要改变的就是在 Geely 类添加关于学习 Python 的方法,如果还要学习其他的课程那么方那么这个 Geely 类是一直在补充变化的,这违背开闭原则。上述的过程也是面向实现编程的过程,扩展性比较差。而且Test测试类的使用需要依赖 Geely 类,Test 想调用学习 哪门课程 的方法,Geely 类就要添加这个方法才能调用,息息相关。
/**
 *  Geely 类
 */
public class Geely {
    public void studyJavaCourse(){
        System.out.println("Geely 在学习 Java 课程");
    }
 
    public void studyFECourse(){
        System.out.println("Geely 在学习 FE 课程");
    }
 
    // 新增关于学习 Python 的方法
    public void studyPythonCourse(){
        System.out.println("Geely 在学习 Python 课程");
    }
}

Resuelva los problemas anteriores presentando clases / interfaces abstractas: La clase Geely siempre está cambiando, ¿cómo hacer que Geely no cambie? Solo cambiando el método de nivel más bajo y el método de llamada de la capa de aplicación, puede aprender cualquier curso que desee aprender .

/**
 * Icourse 接口,里面定义了一个学习课程的方法
 */
public interface Icourse {
    public void studyCourse();
}
 
/**
 * Icourse 接口实现类,具体学习哪门课程
 */
public class FECourse implements Icourse {
    public void studyCourse() {
        System.out.println("Geely 在学习 FE 课程");
    }
}
 
/**
 * Icourse 接口实现类,具体学习哪门课程
 */
public class JavaCourse implements Icourse {
    public void studyCourse() {
        System.out.println("Geely 在学习 Java 课程");
    }
}
 
/**
 *  Geely 类,通过一个自定义方法 studyImoocCourse ,将 Icourse 的实现类传入
 */
public class Geely {
//    public void studyJavaCourse(){
//        System.out.println("Geely 在学习 Java 课程");
//    }
//
//    public void studyFECourse(){
//        System.out.println("Geely 在学习 FE 课程");
//    }
//
//    public void studyPythonCourse(){
//        System.out.println("Geely 在学习 Python 课程");
//    }
 
    private  Icourse icourse;
 
    public Geely() {
    }
 
    // V4 通过 setter 注入
    public void setIcourse(Icourse icourse) {
        this.icourse = icourse;
    }
 
    // 通过构造器注入,可以将原来定义的带参的方法,将其变为无参方法即可
    public Geely(Icourse icourse) {
        this.icourse = icourse;
    }
 
    // V1 带参方法,Test 调用传入即可
    // V2 变为无参,通过构造方法注入
    public void studyImoocCourse(/*Icourse icourse*/){
        icourse.studyCourse();
    }
}
 
/**
 * 测试类
 */
public class Test {
     // V1 面向实现编程
//    public static void main(String[] args) {
//        Geely geely = new Geely();
//        geely.studyFECourse();
//        geely.studyJavaCourse();
//    }
 
     // V2 面向接口编程,通过调用方法传入参数方式
//    public static void main(String[] args) {
//        Geely geely = new Geely();
//        geely.studyImoocCourse(new JavaCourse());
//        geely.studyImoocCourse(new FECourse());
//    }
 
      // V3 面向接口编程,通过构造器注入的方式调用方法,缺点,每新学一个课程就要new一下
//    public static void main(String[] args) {
//        Geely geely = new Geely(new JavaCourse());
//        geely.studyImoocCourse();
//        geely = new Geely(new FECourse());
//        geely.studyImoocCourse();
//    }
 
        // V4 面向接口编程,通过 setter 注入的方式,调用方法
//      public static void main(String[] args) {
//          Geely geely = new Geely();
//          JavaCourse javaCourse = new JavaCourse();
//          geely.setIcourse(javaCourse);
//          geely.studyImoocCourse();
//      }
}

Diagrama de clase

La prueba es el módulo de la capa de aplicación y pertenece al módulo de alto nivel, Geely es la capa inferior de la prueba,

Se puede encontrar que la llamada de Test ya no depende del cambio de Geely para implementar la llamada correspondiente. Al expandir, solo necesita agregar la clase de implementación de la interfaz ICourse y llamar al método en Test para pasar. Hay no es necesario cambiar la clase Geely Cumplir con el principio de apertura y cierre.

3. Principio de responsabilidad única

Definición: No debe haber más de una causa de cambios de clase. Por ejemplo, una determinada clase tiene dos métodos, y cada método es responsable de una función. Si las responsabilidades del método uno deben cambiarse, entonces este archivo de clase debe modificarse. Este cambio puede causar la falla del método dos que se originalmente funcionando normalmente. Para este método donde las dos responsabilidades cambian a menudo, es necesario establecer los archivos java correspondientes respectivamente para ser responsables de las funciones correspondientes, a fin de evitar la influencia mutua cuando una parte cambia temporalmente a la otra sin cambios. En circunstancias normales, tenemos requisitos más bajos para una sola responsabilidad de una clase, pero las interfaces y los métodos intentan garantizar una sola responsabilidad. La clase o método de interfaz escrito de acuerdo con los requisitos de su negocio debe satisfacer mejor las necesidades y no es necesario ampliarlo para que pueda usarse. Si lo hace, escriba otro método, clase e interfaz.

Realización en código Java: una clase / interfaz / método solo es responsable de una responsabilidad.

Ventajas: reducir la complejidad de la clase, mejorar la legibilidad de la clase, mejorar la mantenibilidad del sistema y reducir el riesgo causado por los cambios.

Cuarto, el principio de aislamiento de la interfaz.

Definición: Con múltiples interfaces especializadas en lugar de una única interfaz general, el cliente no debe depender de interfaces que no necesita. La dependencia de una clase de una clase debe establecerse en la interfaz más pequeña. Establezca una interfaz única, no cree una interfaz enorme e inflada. Intente refinar la interfaz tanto como sea posible, con la menor cantidad de métodos posibles en la interfaz. Preste atención al principio de moderación.

El significado del párrafo anterior es que una determinada clase solo puede necesitar un método en una determinada interfaz, pero la interfaz tiene más de n métodos que él no necesita. Si implementa esta interfaz, debe implementar todas las definiciones de la interfaz . Método, esto no es correcto En este momento, el método de la interfaz debe ser independiente de una interfaz pequeña, o se debe crear una nueva interfaz que contenga el método. El principio de moderación significa que no puede haber muy pocos métodos en la interfaz. Si hay muy pocos métodos, se generarán múltiples interfaces, y luego habrá muchas clases de implementación que resultarán en una enorme estructura de todo el sistema. y mantenimiento deficiente del sistema.

Ventajas: Se ajusta a la filosofía de diseño de alta cohesión y bajo acoplamiento que solemos decir, para que la clase tenga buena legibilidad, escalabilidad y mantenibilidad.

Cinco, ley de Dimit

Definición: Un objeto debe mantener el menor conocimiento de otros objetos, también conocido como el principio de menor conocimiento. Intente reducir el acoplamiento entre clases. La realización de alta cohesión y bajo acoplamiento.

Probablemente signifique que si puede resolverlo usted mismo, no pregunte a otros.

Enfatice que solo se comunica con amigos, no con extraños. Permanece misterioso

Acerca del concepto de amigo: la clase que aparece en las variables miembro, la entrada del método y los parámetros de salida se llama clase de relación de amigo, y la clase que aparece dentro del cuerpo del método no pertenece a la clase de amigo, por lo que este tipo de entidad la clase es también lo que queremos evitar.

Entonces, desde la perspectiva de la clase, también se necesita autoprotección, es decir, la clase debe minimizar los métodos no públicos expuestos al exterior, y las variables públicas no estáticas y las variables miembro son privadas y usan permisos de paquete y protegido para protegerse.

Si existe tal escenario, un método se puede colocar en la clase A o en la clase B, entonces, ¿cómo hacerlo? El principio es: si el método se coloca en esta clase sin aumentar la relación entre clases, y no tiene un impacto negativo en esta clase, entonces se puede colocar en esta clase. Cuantas menos referencias a clases externas, mejor.

Ventajas: reducir el acoplamiento entre clases.

Escena de codificación: Mukenet tiene un gran jefe. Un día le dijiste a un TeamLeader, puedes comprobar cuántos cursos hay online hasta ahora. Hay varias categorías relacionadas con él: Boss, TeamLeader, Course. Boss tiene un método para dar instrucciones a TeamLeader y TeamLeader tiene un método para contar cursos en línea.

El código que viola la regla DEMETER es el siguiente

import java.util.ArrayList;
import java.util.List;
/**
 * Boss 类
 * 根据 DEMETER 法则。该类里面为朋友关系的是 TeamLeader 类,
 * 而 commandCheckNumber 方法体内部的 Course 类,
 * 不算是朋友关系,陌生人,孤立他
 * 这里 Boss 完全没有必要知道 Course 这个类,
 * 他只需要知道 TeamLeader 这个类,并对他下指令获取线上课程 Course 即可
 */
public class Boss {
    // 有一个对 TeamLeader 下指令的方法,查询线上课程的数量
    public void commandCheckNumber(TeamLeader teamLeader){
        //
        List<Course> courseList = new ArrayList<Course>();
        for (int i = 0; i < 20; i++) {
            courseList.add(new Course());
        }
        teamLeader.checkNumberOfCourses(courseList);
    }
}
 
 
import java.util.List;
/**
 * TeamLeader 类
 * 该类有统计 线上课程的方法
 */
public class TeamLeader {
    public void checkNumberOfCourses(List<Course> courseList){
        System.out.println("在线课程的数量是:" + courseList.size());
    }
}
 
 
/**
 * 被统计的课程
 */
public class Course {
}
 
 
/**
 * Test 测试类
 */
public class Test {
    public static void main(String[] args) {
        Boss boss = new Boss();
        TeamLeader teamLeader = new TeamLeader();
        boss.commandCheckNumber(teamLeader);
    }
}

El diagrama de estructura de clases es el siguiente:

 

Para obtener el código correcto, elimine el código para crear la colección del curso en la clase Boss y muévalo a TeamLeader. Prueba y Curso permanecen sin cambios. El código y el diagrama de estructura de clases son los siguientes.

/**
 * Boss 类
 * 根据 DEMETER 法则。该类里面为朋友关系的是 TeamLeader 类,
 * 而 commandCheckNumber 方法体内部的 Course 类,
 * 不算是朋友关系,陌生人,孤立他
 * 这里 Boss 完全没有必要知道 Course 这个类,
 * 他只需要知道 TeamLeader 这个类,并对他下指令获取线上课程 Course 即可
 */
public class Boss {
    // 有一个对 TeamLeader 下指令的方法,查询线上课程的数量
    public void commandCheckNumber(TeamLeader teamLeader){
        teamLeader.checkNumberOfCourses();
    }
}
 
 
/**
 * TeamLeader 类
 * 该类有统计 线上课程的方法
 */
public class TeamLeader {
    public void checkNumberOfCourses(){
        //
        List<Course> courseList = new ArrayList<Course>();
        for (int i = 0; i < 20; i++) {
            courseList.add(new Course());
        }
        System.out.println("在线课程的数量是:" + courseList.size());
    }
}

PD: Necesitamos pensar con claridad al escribir código, qué clases son amigas directas y cuáles no. Consulte la regla DEMETER para desarrollar.

Seis, la regla de sustitución de Li

Definición: Si para cada objeto o1 de tipo T1, hay un objeto o2 de tipo T2, de modo que todos los programas P definidos por T1 no cambiarán el comportamiento del programa P cuando todos los objetos o1 sean reemplazados por o2, entonces el Tipo T2 es un subtipo de tipo T1 . A través de esta definición podemos extender una herencia de definición, T1 es la clase padre de T2. El principio de sustitución de Li es la piedra angular de la herencia y la reutilización. Cuando la subclase puede reemplazar a la clase principal y la función del software no se ve afectada, la principal puede reutilizarse y la subclase también puede agregar sus propias funciones nuevas. El principio de sustitución de Li es un complemento al principio de apertura y cierre, y es una especificación de pasos concretos para lograr la abstracción. El significado expresado por el principio de sustitución de Li es oponerse al significado de subclase que prevalece sobre el método de la clase principal.

Extensión de la definición: si una entidad de software se aplica a una clase principal, debe ser aplicable a sus subclases. Todas las referencias a la clase principal deben poder usar de forma transparente sus objetos de subclase. Los objetos de subclase pueden reemplazar los objetos de la clase principal y el programa constante lógica.

Significado extendido:

1. Las subclases pueden ampliar las funciones de la clase padre, pero no pueden cambiar las funciones originales de la clase padre.

     Significado 1: Las subclases pueden implementar los métodos abstractos de la clase principal, pero no pueden anular los métodos no abstractos de la clase principal.

     Significado 2: Las subclases pueden agregar sus propios métodos únicos.

     Significado 3: cuando un método de una subclase sobrecarga un método de una clase principal, las condiciones previas del método (es decir, la entrada / parámetros del método) son más relajadas que los parámetros de entrada del método principal.

     Significado 4: cuando un método de una subclase implementa un método de una clase principal (anulando / sobrecargando o implementando un método abstracto), la condición posterior del método (es decir, el valor de salida / retorno del método) es más estricta o igual a el de la clase padre.

ventaja:

     1. La herencia de restricción se desborda, una manifestación del principio de apertura y cierre.

     2. Fortalecer la solidez del programa y, al mismo tiempo, también puede lograr una muy buena compatibilidad al cambiar para mejorar la capacidad de mantenimiento y la escalabilidad del programa. Reducir los riesgos introducidos cuando cambian los requisitos.

Codificación:

 

7. Reglas múltiples sintéticas (combinación) / agregación

Definición: intente utilizar la composición / agregación de objetos en lugar de la herencia para lograr el propósito de la reutilización del software.

El agregado tiene-A y el combinado contiene-A

Ventajas: Puede ser que el sistema sea más flexible y el acoplamiento entre clases se reduzca El cambio de una clase tiene relativamente poco impacto en otras clases.

Cuándo usar composición / agregación, herencia

Relación agregada tiene-A

La combinación contiene una relación de contención

Heredar la relación padre-hijo es-A

Supongo que te gusta

Origin blog.csdn.net/VABTC/article/details/111044330
Recomendado
Clasificación