Principios de diseño: principio de sustitución de Richter

X Principio de sustitución de Liskov: heredar la clase principal en lugar de cambiar la clase principal

significado

El principio de sustitución de Liskov es una definición especial de subtipos.
Si una entidad de software es aplicable a una clase principal, debe ser aplicable a sus subclases. Todos los lugares que hacen referencia a la clase principal deben poder usar sus subclases de forma transparente. Objetos, objetos de subclase puede reemplazar los objetos de la clase principal, mientras que la lógica del programa permanece sin cambios

  • Las subclases pueden implementar métodos abstractos de la clase principal, pero no pueden anular (anular) métodos no abstractos de la clase principal.
  • Cuando un método de una subclase anula un método de una clase principal, las condiciones previas del método (es decir, los parámetros de entrada del método) son más flexibles (de mayor alcance) que los parámetros de entrada del método de la clase principal.
  • Cuando un método de una subclase implementa un método de una clase principal (anula/sobrecarga o implementa un método abstracto), las condiciones posteriores del método (es decir, el valor de retorno del método) son más estrictas o iguales a las de la clase principal.
  • Las subclases pueden tener sus propios métodos o atributos únicos, es decir, las subclases pueden ampliar las funciones de la clase principal, pero no pueden cambiar las funciones originales de la clase principal.

El principio de sustitución de Liskov explica principalmente algunos principios sobre la herencia, es decir, cuándo se debe utilizar la herencia, cuándo no se debe utilizar la herencia y los principios subyacentes. La sustitución de Liskov es originalmente la base para la reutilización de la herencia. Refleja la relación entre las clases base y las subclases. Es una especificación de los pasos específicos para lograr la abstracción. Es un complemento del principio de apertura y cierre y una forma importante de realizar la apertura. y principio de cierre uno

Idea principal

Después de reemplazar un objeto de clase principal con su objeto de subclase, el programa no generará una excepción.

ventaja

  • La herencia de restricciones está muy extendida: el principio de sustitución de Liskov es una de las formas importantes de realizar el principio de apertura y cierre y es una manifestación del principio de apertura y cierre.
  • Supera las deficiencias de la mala reutilización causada por la anulación de clases principales en la herencia.
  • Es una garantía de corrección de la acción. Es decir, la extensión de la clase no introducirá nuevos errores en el sistema existente, reduciendo la posibilidad de errores de código.
  • Fortalecer la robustez del programa, y ​​al mismo tiempo lograr muy buena compatibilidad al cambiar, mejorando la mantenibilidad y escalabilidad del programa. Reducir los riesgos introducidos cuando los requisitos cambian

Caso

el primer punto

Un problema que enfrentaremos en la "herencia" es que el método definido por nuestra clase principal no obliga a sus subclases a cumplir completamente con las reglas de implementación del método. Una subclase puede modificar cualquier método que herede de la clase principal. El principio de sustitución de Liskov requiere un estricto cumplimiento de sus ideas centrales

public class Father {
    
    

    public void getNum(int a, int b) {
    
    
        System.out.println("父类getNum()方法计算出的结果为:" + (a + b));
    }
}

public class Son extends Father {
    
    

    @Override
    public void getNum(int a, int b) {
    
    
        System.out.println("子类getNum()方法计算出的结果为:" + (a - b));
    }
}

public class Test {
    
    
    public static void main(String[] args) {
    
    
        test01();
    }

    public static void test01(){
    
    
        Father father = new Father();
        father.getNum(5, 10);
        father = new Son();
        father.getNum(5, 10);
    }
}

Insertar descripción de la imagen aquí

La intención original de la clase principal era definir un método para sumar dos números, pero después de heredar el método, la subclase lo cambió a resta y lo logró. Si la subclase opera de esta manera, dañará todo el sistema de herencia. Cuando desee reemplazar el lugar donde se usa la clase principal con su subclase, encontrará que la función normal original ahora tiene problemas.

Para resolver este problema, el primer punto del principio de sustitución de Liskov es:

Las subclases pueden implementar métodos abstractos en la clase principal, pero no pueden anular (anular) métodos no abstractos en la clase principal.

Segundo punto

Cuando no tenemos más remedio que agregar un método con el mismo nombre que el método de la clase principal en la subclase, debemos cumplir con los siguientes puntos.

Cuando un método de una subclase anula un método de una clase principal, las condiciones previas del método (es decir, los parámetros de entrada del método) son más flexibles (de mayor alcance) que los parámetros de entrada del método de la clase principal.

Uno de los propósitos directos de esto es evitar confusión en las llamadas a métodos después de reemplazar el objeto de la clase principal con un objeto de subclase.

// 父类
public void method(ArrayList arrayList) {
    
    
    System.out.println("父类方法执行了!");
}
// 子类
@Override
public void method(ArrayList arrayList) {
    
    
    System.out.println("子类方法执行了!");
}

public static void test02(){
    
    
    ArrayList list = new ArrayList();
    Father father = new Father();
    Son son = new Son();
    System.out.print("使用父类对象调用的结果:");
    father.method(list);
    System.out.print("将父类对象替换成子类对象后的调用结果:");
    son.method(list);
}

Insertar descripción de la imagen aquí

Si intercambiamos los parámetros del método de la clase principal y los parámetros del método de la subclase

// 父类
public void method(List<String> arrayList) {
    
    
    System.out.println("父类方法执行了!");
}
// 子类
public void method(ArrayList<String> arrayList) {
    
    
    System.out.println("子类方法执行了!");
}

Insertar descripción de la imagen aquí

Nuestra intención original era esperar que el método original aún se ejecutara después de reemplazar el objeto, pero el resultado cambió. Esto es inconsistente con el principio de sustitución de Liskov. Por lo tanto, siempre debemos tener en cuenta que el rango de parámetros de los métodos de la subclase es mayor que el de la clase principal, para no provocar errores innecesarios.

El tercer punto

Cuando necesitamos anular o implementar métodos de clase principal, debemos cumplir con los siguientes puntos.

Cuando un método de una subclase implementa un método de una clase principal (anula/sobrecarga o implementa un método abstracto), las condiciones posteriores del método (es decir, el valor de retorno del método) son más estrictas o iguales a las de la clase principal.

// 父类
public List<String> getList() {
    
    
    return new ArrayList<>();
}

// 子类
@Override
public ArrayList<String> getList() {
    
    
    return new ArrayList<>();
}

Si intentamos ampliar, anular o implementar el valor de retorno del método de la clase principal en la subclase, el código informará un error e incluso el compilador básico no podrá pasarlo.

cuarto punto

Las subclases pueden tener sus propios métodos o propiedades únicos.

Esta oración es más fácil de entender: muestra que las subclases no solo pueden heredar cosas de la clase principal, sino que también pueden extenderse y escribir sus propias cosas.

A través de la descripción anterior, creo que todos tienen un concepto básico del principio de sustitución de Liskov: nos dice a qué cuestiones debemos prestar atención y qué reglas debemos seguir en la herencia.

Sin embargo, en el desarrollo real, todavía violamos este principio muchas veces. Aunque no hay un problema particularmente grande en la superficie, hacerlo aumentará en gran medida la tasa de error del código.

Supongo que te gusta

Origin blog.csdn.net/qq_42700109/article/details/132861006
Recomendado
Clasificación