Manufactura industrial
Internet industrial de las cosas | Materiales industriales | Mantenimiento y reparación de equipos | Programación industrial |
home  MfgRobots >> Manufactura industrial >  >> Industrial Internet of Things >> Computación en la nube

SOLID:Principios de diseño orientado a objetos

SOLID es un acrónimo mnemotécnico para el diseño de clases en la programación orientada a objetos. Los principios instituyen prácticas que ayudan a desarrollar buenos hábitos de programación y código mantenible.

Al considerar el mantenimiento y la extensibilidad del código a largo plazo, los principios SOLID enriquecen el entorno de desarrollo de código Agile. Contabilizar y optimizar las dependencias del código ayuda a crear un ciclo de vida de desarrollo de software más sencillo y organizado.

¿Qué son los principios SOLID?

SOLID representa un conjunto de principios para el diseño de clases. Robert C. Martin (tío Bob) introdujo la mayoría de los principios de diseño y acuñó el acrónimo.
SÓLIDO significa:

Los principios SOLID representan una colección de mejores prácticas para el diseño de software. Cada idea representa un marco de diseño, lo que conduce a mejores hábitos de programación, diseño de código mejorado y menos errores.

SÓLIDO:5 principios explicados

La mejor manera de entender cómo funcionan los principios SOLID es a través de ejemplos. Todos los principios son complementarios y se aplican a casos de uso individuales. El orden en que se aplican los principios no es importante, y no todos los principios son aplicables en todas las situaciones.

Cada sección a continuación proporciona una descripción general de cada principio SOLID en el lenguaje de programación Python. Las ideas generales de SOLID se aplican a cualquier lenguaje orientado a objetos, como PHP, Java o C#. La generalización de las reglas las hace aplicables a los enfoques de programación modernos, como los microservicios.

Principio de responsabilidad única (PRS)

El principio de responsabilidad única (SRP) establece:“Nunca debe haber más de una razón para que una clase cambie”.

Al cambiar una clase, solo debemos cambiar una sola funcionalidad, lo que implica que cada objeto debe tener un solo trabajo.

Como ejemplo, observe la siguiente clase:

# A class with multiple responsibilities
class Animal:
    # Property constructor
    def __init__(self, name):
        self.name = name

    # Property representation
    def __repr__(self):
        return f'Animal(name="{self.name}")'

    # Database management
    def save(animal):
        print(f'Saved {animal} to the database')

if __name__ == '__main__':
    # Property instantiation
    a = Animal('Cat')
    # Saving property to a database
    Animal.save(a)

Al realizar cambios en el save() método, el cambio ocurre en el Animal clase. Al realizar cambios en las propiedades, las modificaciones también se producen en el Animal clase.

La clase tiene dos razones para cambiar y viola el principio de responsabilidad única. Aunque el código funciona como se esperaba, no respetar el principio de diseño hace que el código sea más difícil de administrar a largo plazo.

Para implementar el principio de responsabilidad única, observe que la clase de ejemplo tiene dos trabajos distintos:

Por lo tanto, la mejor manera de abordar el problema es separar el método de administración de la base de datos en una nueva clase. Por ejemplo:

# A class responsible for property management
class Animal:
    def __init__(self, name):
        self.name = name
    def __repr__(self):
        return f'Animal(name="{self.name}")'

# A class responsible for database management
class AnimalDB:
    def save(self, animal):
        print(f'Saved {animal} to the database')

if __name__ == '__main__':
    # Property instantiation
    a = Animal('Cat')
    # Database instantiation
    db = AnimalDB()
    # Saving property to a database
    db.save(a)

Cambiando el AnimalDB la clase no afecta al Animal clase con el principio de responsabilidad única aplicado. El código es intuitivo y fácil de modificar.

Principio Abierto-Cerrado (OCP)

El principio abierto-cerrado (OCP) establece:“Las entidades de software deben estar abiertas para la extensión pero cerradas para la modificación”.

Agregar funcionalidades y casos de uso al sistema no debería requerir la modificación de entidades existentes. La redacción parece contradictoria:agregar nuevas funcionalidades requiere cambiar el código existente.

La idea es sencilla de entender a través del siguiente ejemplo:

class Animal:
    def __init__(self, name):
        self.name = name
    def __repr__(self):
        return f'Animal(name="{self.name}")'

class Storage:
    def save_to_db(self, animal):
        print(f'Saved {animal} to the database')

El Storage clase guarda la información de un Animal instancia a una base de datos. Agregar nuevas funcionalidades, como guardar en un archivo CSV, requiere agregar código al Storage clase:

class Animal:
    def __init__(self, name):
        self.name = name
    def __repr__(self):
        return f'Animal(name="{self.name}")'

class Storage:
    def save_to_db(self, animal):
        print(f'Saved {animal} to the database')
    def save_to_csv(self,animal):
        printf(f’Saved {animal} to the CSV file’)

El save_to_csv El método modifica un Storage existente clase para agregar la funcionalidad. Este enfoque viola el principio abierto-cerrado al cambiar un elemento existente cuando aparece una nueva funcionalidad.

El código requiere eliminar el Storage de uso general class y crear clases individuales para almacenar en formatos de archivo específicos.

El siguiente código demuestra la aplicación del principio abierto-cerrado:

class DB():
    def save(self, animal):
        print(f'Saved {animal} to the database')

class CSV():
    def save(self, animal):
        print(f'Saved {animal} to a CSV file')

El código cumple con el principio abierto-cerrado. El código completo ahora se ve así:

class Animal:
    def __init__(self, name):
        self.name = name
    def __repr__(self):
        return f'"{self.name}"'

class DB():
    def save(self, animal):
        print(f'Saved {animal} to the database')

class CSV():
    def save(self, animal):
        print(f'Saved {animal} to a CSV file')

if __name__ == '__main__':
    a = Animal('Cat')
    db = DB()
    csv = CSV()
    db.save(a)
    csv.save(a) 

La ampliación con funcionalidades adicionales (como guardar en un archivo XML) no modifica las clases existentes.

Principio de sustitución de Liskov (LSP)

El principio de sustitución de Liskov (LSP) establece:“Las funciones que usan punteros o referencias a clases base deben poder usar objetos de clases derivadas sin saberlo”.

El principio establece que una clase principal puede sustituir a una clase secundaria sin cambios notables en la funcionalidad.

Consulte el ejemplo de escritura de archivos a continuación:

# Parent class
class FileHandling():
    def write_db(self):
        return f'Handling DB'
    def write_csv(self):
        return f'Handling CSV'

# Child classes
class WriteDB(FileHandling):
    def write_db(self):
        return f'Writing to a DB'
    def write_csv(self):
        return f"Error: Can't write to CSV, wrong file type."

class WriteCSV(FileHandling):
    def write_csv(self):
        return f'Writing to a CSV file'
    def write_db(self):
        return f"Error: Can't write to DB, wrong file type."

if __name__ == "__main__":
   # Parent class instantiation and function calls
    db = FileHandling()
    csv = FileHandling()
    print(db.write_db())
    print(db.write_csv())

    # Children classes instantiations and function calls
    db = WriteDB()
    csv = WriteCSV()
    print(db.write_db())
    print(db.write_csv())
    print(csv.write_db())
    print(csv.write_csv())

La clase padre (FileHandling ) consta de dos métodos para escribir en una base de datos y un archivo CSV. La clase maneja ambas funciones y devuelve un mensaje.

Las dos clases secundarias (WriteDB y WriteCSV ) heredan propiedades de la clase padre (FileHandling ). Sin embargo, ambos hijos arrojan un error cuando intentan usar la función de escritura inapropiada, lo que viola el principio de sustitución de Liskov ya que las funciones anuladoras no se corresponden con las funciones principales.

El siguiente código resuelve los problemas:

# Parent class
class FileHandling():
    def write(self):
        return f'Handling file'

# Child classes
class WriteDB(FileHandling):
    def write(self):
        return f'Writing to a DB'

class WriteCSV(FileHandling):
    def write(self):
        return f'Writing to a CSV file'

if __name__ == "__main__":
   # Parent class instantiation and function calls
    db = FileHandling()
    csv = FileHandling()
    print(db.write())
    print(csv.write())

    # Children classes instantiations and function calls
    db = WriteDB()
    csv = WriteCSV()
    print(db.write())
    print(csv.write())

Las clases secundarias corresponden correctamente a la función principal.

Principio de segregación de interfaz (ISP)

El principio de segregación de interfaz (ISP) establece:“Muchas interfaces específicas del cliente son mejores que una interfaz de uso general”.

En otras palabras, las interfaces de interacción más extensas se dividen en otras más pequeñas. El principio garantiza que las clases solo usen los métodos que necesitan, lo que reduce la redundancia general.

El siguiente ejemplo muestra una interfaz de uso general:

class Animal():
    def walk(self):
        pass
    def swim(self):
        pass
    
class Cat(Animal):
    def walk(self):
        print("Struts")
    def fly(self):
        raise Exception("Cats don't swim")
    
class Duck(Animal):
    def walk(self):
        print("Waddles")
    def swim(self):
        print("Floats")

Las clases secundarias heredan del padre Animal clase, que contiene walk y fly métodos. Aunque ambas funciones son aceptables para ciertos animales, algunos animales tienen funcionalidades redundantes.

Para manejar la situación, divida la interfaz en secciones más pequeñas. Por ejemplo:

class Walk():
    def walk(self):
        pass

class Swim(Walk):
    def swim(self):
        pass

class Cat(Walk):
    def walk(self):
        print("Struts")
    
class Duck(Swim):
    def walk(self):
        print("Waddles")
    def swim(self):
        print("Floats")

El Fly la clase hereda del Walk , proporcionando funcionalidad adicional a las clases secundarias apropiadas. El ejemplo satisface el principio de segregación de interfaces.
Agregar otro animal, como un pez, requiere atomizar aún más la interfaz ya que los peces no pueden caminar.

Principio de inversión de dependencia (DIP)

El principio de inversión de dependencia establece:“Depende de abstracciones, no de concreciones”.

El principio tiene como objetivo reducir las conexiones entre clases mediante la adición de una capa de abstracción. Mover dependencias a abstracciones hace que el código sea sólido.

El siguiente ejemplo demuestra la dependencia de clase sin una capa de abstracción:

class LatinConverter:
    def latin(self, name):
        print(f'{name} = "Felis catus"')
        return "Felis catus"

class Converter:
    def start(self):
        converter = LatinConverter()
        converter.latin('Cat')

if __name__ ==  '__main__':
    converter = Converter()
    converter.start()

El ejemplo tiene dos clases:

El principio de inversión de dependencia requiere agregar una capa de interfaz de abstracción entre las dos clases.

Una solución de ejemplo es similar a la siguiente:

from abc import ABC

class NameConverter(ABC):
    def convert(self,name):
        pass

class LatinConverter(NameConverter):
    def convert(self, name):
        print('Converting using Latin API')
        print(f'{name} = "Felis catus"')
        return "Felis catus"

class Converter:
    def __init__(self, converter: NameConverter):
        self.converter = converter
    def start(self):
        self.converter.convert('Cat')

if __name__ ==  '__main__':
    latin = LatinConverter()
    converter = Converter(latin)
    converter.start()

El Converter la clase ahora depende del NameConverter interfaz en lugar de en el LatinConverter directamente. Las actualizaciones futuras permitirán definir conversiones de nombres utilizando un idioma y una API diferentes mediante el NameConverter interfaz.

¿Por qué se necesitan principios SOLID?

Los principios SOLID ayudan a luchar contra los problemas de patrones de diseño. El objetivo general de los principios SOLID es reducir las dependencias del código, y agregar una nueva función o cambiar una parte del código no rompe toda la compilación.

Como resultado de aplicar los principios SOLID al diseño orientado a objetos, el código se vuelve más fácil de entender, administrar, mantener y cambiar. Dado que las reglas son más adecuadas para proyectos grandes, la aplicación de los principios SOLID aumenta la velocidad y la eficiencia del ciclo de vida de desarrollo general.

¿Siguen siendo relevantes los principios SOLID?

Aunque los principios SOLID tienen más de 20 años, todavía proporcionan una buena base para el diseño de arquitectura de software. SOLID proporciona principios de diseño sólidos aplicables a los programas y entornos modernos, no solo a la programación orientada a objetos.

Los principios SOLID se aplican en situaciones donde el código es escrito y modificado por personas, organizado en módulos y contiene elementos internos o externos.

Conclusión

Los principios SOLID ayudan a proporcionar un buen marco y una guía para el diseño de arquitectura de software. Los ejemplos de esta guía muestran que incluso un lenguaje de escritura dinámica como Python se beneficia al aplicar los principios al diseño de código.

A continuación, lea acerca de los 9 principios de DevOps que ayudarán a su equipo a aprovechar al máximo DevOps.


Computación en la nube

  1. Palabra clave estática de C#
  2. Clase anidada de C#
  3. Plantillas de clase de C++
  4. Clase anónima de Java
  5. Clase Java ObjectOutputStream
  6. Genéricos de Java
  7. Clase de archivo Java
  8. 5 principios de diseño para aplicar interconexiones robustas para aplicaciones con uso intensivo de datos
  9. C# - Herencia
  10. C# - Polimorfismo
  11. Principios de fricción y diseño de rodamientos