【C ++】 Patrón de diseño

Seis principios

Principio de responsabilidad única

  • Las responsabilidades de una clase deben ser únicas. Un método sólo hace una cosa. Las responsabilidades deben estar claramente divididas. Cada cambio debe realizarse en la unidad más pequeña de método o clase.
  • Sugerencias de uso: dos funciones completamente diferentes no deben colocarse en una clase, una clase debe ser un conjunto de funciones y encapsulación de datos altamente relacionados.
  • Caso de uso: la clase de chat de Internet (❌) debe dividirse en clase de comunicación de red + clase de chat

Principio abierto cerrado

  • Abierto a extensiones, cerrado a modificaciones (solo se agregarán nuevas funciones, no se modificará el contenido original)
  • Sugerencias de uso: Para realizar cambios en entidades de software, es mejor utilizar extensiones en lugar de modificaciones.
  • Caso de uso: Venta de horas extras: precio del producto: sin modificar el precio original del producto, pero agregando un nuevo precio promocional

Principio de sustitución de Liskov

  • Dondequiera que pueda aparecer una clase principal, puede aparecer una subclase y no habrá errores ni excepciones al reemplazarla con una subclase.
  • Al heredar una clase, asegúrese de reescribir todos los métodos de la clase principal y preste especial atención a los métodos protegidos de la clase principal. Las subclases deben intentar no exponer sus propios métodos públicos a llamadas externas.
  • Sugerencias de uso: las subclases pueden implementar completamente los métodos de la clase principal y las subclases pueden tener su propia personalidad. Al anular o implementar los métodos de la clase principal, los parámetros de entrada se pueden ampliar y la salida también se puede reducir.
  • Caso de uso: Clase de corredor: puede correr, subclase de corredor de larga distancia: puede correr y es bueno en carreras de larga distancia, subclase de velocista: puede correr y es bueno en carreras de velocidad

Principio de inversión de dependencia

  • Los módulos de alto nivel no deben depender de los módulos de bajo nivel. Ambos deben depender de sus abstracciones. La lógica atómica inseparable es el patrón de bajo nivel y el conjunto de la lógica atómica es el módulo de alto nivel.
  • Las dependencias entre módulos se producen a través de abstracciones (interfaces) y las clases concretas no pueden depender directamente entre sí.
  • Sugerencias de uso: cada clase debe tener una clase abstracta en la medida de lo posible y ninguna clase debe derivarse de una clase concreta. Intente no anular los métodos de la clase base. Utilizado junto con el principio de sustitución de Richter.
  • Caso de uso: conductor de Mercedes-Benz – sólo puede conducir Mercedes-Benz, clase de conductor: qué coche conducir: conductor: conductor – depende de la abstracción

Ley de Demeter Ley de los menos conocidos

  • Minimiza las interacciones entre objetos, reduciendo así el acoplamiento entre clases. Un objeto debe tener el menor conocimiento sobre otros objetos, lo que plantea requisitos claros para un bajo acoplamiento de clases:
    solo comunicación directa con amigos y también hay tensión entre amigos. Lo que es tuyo es tuyo (si un método se coloca en esta clase y no aumenta la relación entre clases ni tiene un impacto negativo en esta clase, entonces colócalo en esta clase)
  • Caso de uso: el maestro le pide al monitor que diga los nombres, el maestro le da al monitor una lista, el monitor verifica los nombres y devuelve los resultados. El profesor sólo interactúa con el monitor, y los alumnos sólo interactúan con el monitor

Principio de aislamiento de interfaz

  • El cliente no debe depender de interfaces que no necesita y las dependencias entre clases deben establecerse en la interfaz más pequeña.
  • Sugerencias de uso: intente mantener el diseño de la interfaz lo más simple posible, pero no exponga las interfaces sin sentido al mundo exterior.
  • Caso de uso: para cambiar la contraseña, no se debe proporcionar la interfaz de información del usuario, pero la interfaz de cambio de contraseña debe usarse sola.

Resumen: Para comprender los seis principios principales de diseño en su conjunto, se pueden resumir brevemente en una oración. Utilice la abstracción para construir el marco y utilice la implementación para ampliar los detalles. Para cada principio de diseño, hay una nota correspondiente.

Patrón singleton

El patrón singleton, como sugiere el nombre, solo puede tener una instancia, por lo que debemos asegurarnos de que: esta clase no se pueda copiar y esta clase no se pueda crear públicamente.

El modo Singleton se puede dividir en modo perezoso y modo hambriento.

Modo diferido: para un proceso, solo lo creamos cuando se usa por primera vez. La ventaja es que ahorra tiempo de carga del proceso. La desventaja es que se crea la primera vez que se utiliza.

Modo hombre hambriento: cuando el código se convierte en un proceso, se crea antes de la función principal y se crea cuando no es necesario. La ventaja es que se puede utilizar directamente y el recurso se crea temprano, lo que afecta la velocidad de carga del proceso.

Modo singleton de hombre hambriento:

La construcción, la construcción de copias y la asignación están privatizadas y no pueden construirse explícitamente.
El puntero del objeto singleton estático se crea durante la inicialización del programa.
Proporcione un objeto estático para obtener el objeto singleton. El objeto singleton se puede obtener en cualquier ubicación.
Su ciclo de vida es el mismo que el ciclo de vida del programa. Esto también se aplica a situaciones de subprocesos múltiples y no hay ningún problema de seguridad de subprocesos.

/* 饿汉单例模式 以空间换时间 */
class Singleton{
    
    
public:
    static Singleton* getInstance() {
    
     return _eton; }
    int getData() {
    
     return _data; }
private:
    Singleton(int data = 99) : _data(data){
    
    }
    ~Singleton(){
    
    };
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    
private:
    static Singleton* _eton;
    int _data;
};
Singleton* Singleton::_eton =new Singleton;

Patrón singleton perezoso:

La construcción, la construcción de copias y la asignación están privatizadas y no pueden construirse explícitamente.
El puntero del objeto singleton estático se crea durante la inicialización del programa.
Proporcione un objeto estático para obtener el objeto singleton. El objeto singleton se puede obtener en cualquier ubicación.
Su ciclo de vida es el mismo que el ciclo de vida del programa. El modo singleton diferido tendrá problemas de seguridad de subprocesos , que se pueden resolver de dos maneras.

Método 1: Bloquear + verificar dos veces (C++98)

class Singleton
{
    
    
public:
	//3、提供一个全局访问点获取单例对象
	static Singleton* GetInstance()
	{
    
    
		//双检查
		if (_inst == nullptr)
		{
    
    
			_mtx.lock();
			if (_inst == nullptr)
			{
    
    
				_inst = new Singleton;
			}
			_mtx.unlock();
		}
		return _inst;
	}
private:
	//1、将构造函数设置为私有,并防拷贝
	Singleton()
	{
    
    }
	Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
	//2、提供一个指向单例对象的static指针
	static Singleton* _inst;
	static mutex _mtx; //互斥锁
};

//在程序入口之前先将static指针初始化为空
Singleton* Singleton::_inst = nullptr;
mutex Singleton::_mtx; //初始化互斥锁

Método 2: construir directamente objetos singleton en funciones estáticas después de C++ 11

/* 懒汉单例模式 懒加载 -- 延时加载思想 -- 一个对象用的时候再实例化 */
// 这里介绍<Effective C++> 作者提出的一种更加优雅简便的单例模式 Meyers Singleton int C++
// C++11后是线程安全的

class Singleton{
    
    
public:
    static Singleton& getInstance() {
    
    
        static Singleton _eton;
        return _eton;
    }
    int getData() {
    
     return _data; }
private:
    Singleton(int data = 99) : _data(data){
    
    }
    ~Singleton() {
    
    };
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

    int _data;
};

Patrón de fábrica

El patrón Factory es un patrón de diseño creacional que proporciona la mejor manera de crear objetos. En el patrón de fábrica, cuando creamos objetos, no exponemos la lógica de creación a la capa superior, sino que usamos una estructura común para señalar el objeto recién creado, logrando así la separación de creación y uso.

①Modo de fábrica simple

Patrón de fábrica simple: la implementación del patrón de fábrica simple requiere una instancia de una clase de producto específica creada por un objeto de fábrica mediante la determinación de tipo. Supongamos que hay una fábrica que puede producir frutas. Cuando el cliente necesita un producto, le dice claramente a la fábrica qué fruta producir. La fábrica necesita recibir la información de categoría proporcionada por el usuario. Cuando se agrega un nuevo producto, la fábrica internamente adopta el método de producción de agregar el nuevo producto.

class Fruit{
    
    
public:
    virtual void name() = 0;
private:   
};

class Apple : public Fruit{
    
    
public:
    void name() override{
    
    
        std::cout << "I'm a apple" << std::endl;
    }
};

class Banana : public Fruit{
    
    
public:
    void name() override {
    
    
        std::cout << "I'm a banana" << std::endl;
    }
};

class FruitFactory {
    
    
public:
    static std::shared_ptr<Fruit> create(const std::string &name) {
    
    
        if (name == "苹果") {
    
    
            return std::make_shared<Apple>();
        } else {
    
    
            return std::make_shared<Banana>();
        }
    }
};

int main() {
    
    
    std::shared_ptr<Fruit> fruit = FruitFactory::create("苹果");
    fruit->name();
    fruit = FruitFactory::create("香蕉");
    fruit->name();
    return 0;
}

La estructura de este modelo y la forma de gestionar los objetos del producto son muy simples, pero su escalabilidad es muy pobre. Cuando necesitamos agregar un nuevo producto, necesitamos modificar la clase de fábrica para agregar un nuevo tipo de lógica de creación de producto, que viola el principio de apertura y cierre.

②Patrón de método de fábrica

  Modo de método de fábrica: en el modo de fábrica simple, se agregan múltiples fábricas y múltiples productos, y cada producto corresponde a una fábrica. Supongamos que hay dos productos, A y B, y se abren dos fábricas: la fábrica A es la principal responsable de producir el producto A y la fábrica B es la principal responsable de producir el producto B. Los usuarios solo necesitan saber el nombre de fábrica del producto, pero No es necesario conocer información específica del producto. La fábrica no necesita recibir la categoría de producto del cliente y solo ser responsable de producir los productos.

  El patrón del método de fábrica se diferencia del patrón de fábrica simple en que el patrón del método de fábrica coloca el proceso de creación de objetos en la subclase. De esta manera, el objeto principal de fábrica es el mismo que el objeto principal del producto, puede ser una clase o interfaz abstracta, solo define las especificaciones u operaciones correspondientes y no implica detalles específicos de creación o implementación.

/* 工厂方法模式 */
class Fruit{
    
    
public:
    virtual void name() = 0;
private:   
};

class Apple : public Fruit{
    
    
public:
    void name() override{
    
    
        std::cout << "I'm a apple" << std::endl;
    }
};

class Banana : public Fruit{
    
    
public:
    void name() override {
    
    
        std::cout << "I'm a banana" << std::endl;
    }
};
class FruitFactory {
    
    
public:
    virtual std::shared_ptr<Fruit> createFruit() = 0;
};

class AppleFactory : public FruitFactory {
    
    
public:
    virtual std::shared_ptr<Fruit> createFruit() override {
    
    
        return std::make_shared<Apple>();
    }
};

class BananaFactory : public FruitFactory {
    
    
public:
    virtual std::shared_ptr<Fruit> createFruit() override {
    
    
        return std::make_shared<Banana>();
    }
};

int main() {
    
    
    std::shared_ptr<FruitFactory> ff(new AppleFactory());
    std::shared_ptr<Fruit> fruit1 = ff->createFruit();
    fruit1->name();
    ff.reset(new BananaFactory());
    std::shared_ptr<Fruit> fruit2 = ff->createFruit();
    fruit2->name();
    return 0;
}

Cada vez que se agrega o elimina un producto en el patrón del método de fábrica, se deben agregar una clase de producto específica y una clase de fábrica, lo que duplica el número de clases en el sistema y aumenta el acoplamiento del sistema hasta cierto punto.

③Patrón abstracto de fábrica

Patrón de fábrica abstracto: el patrón de método de fábrica resuelve el problema de las responsabilidades demasiado pesadas de las clases de fábrica en el patrón de fábrica simple mediante la introducción de una estructura jerárquica de fábrica. Sin embargo, dado que cada fábrica en el patrón de método de fábrica solo produce un tipo de producto, puede conducir a una gran cantidad de productos en el sistema. La clase de fábrica inevitablemente aumentará la sobrecarga del sistema. En este momento, podemos considerar formar algunos productos relacionados en una familia de productos (una familia de productos con funciones interrelacionadas en diferentes estructuras jerárquicas de productos). Dado que una fábrica produce producción unificada, esta es una fábrica abstracta: la idea básica del patrón.

#include <iostream>
#include <memory>

/* 简单工厂模式 */
class Fruit{
    
    
public:
    virtual void name() = 0;
private:   
};

class Apple : public Fruit{
    
    
public:
    void name() override{
    
    
        std::cout << "I'm a apple" << std::endl;
    }
};

class Banana : public Fruit{
    
    
public:
    void name() override {
    
    
        std::cout << "I'm a banana" << std::endl;
    }
};

class Animal {
    
    
    public:
        virtual void name() = 0;
};

class Lamp : public Animal {
    
    
    public:
        virtual void name() override {
    
     
            std::cout << "I'm a Lamp" << std::endl;
        }
};

class Dog : public Animal {
    
    
    public:
        virtual void name() override {
    
    
            std::cout << "I'm  a dog" << std::endl;
        }
};

class Factory {
    
    
    public: 
        virtual std::shared_ptr<Fruit> getFruit(const std::string& name) = 0;
        virtual std::shared_ptr<Animal> getAnimal(const std::string& name) = 0;
};

class FruitFactory : public Factory {
    
    
    public:
    virtual std::shared_ptr<Fruit> getFruit(const std::string& name) override{
    
    
        if (name == "苹果") {
    
    
            return std::make_shared<Apple>();
        } else {
    
    
            return std::make_shared<Banana>();
        }
    }
    virtual std::shared_ptr<Animal> getAnimal(const std::string& name) override{
    
    
        return std::shared_ptr<Animal>();
    }
};

class AnimalFactory : public Factory {
    
    
    public:
    virtual std::shared_ptr<Fruit> getFruit(const std::string& name) override {
    
    
        return std::shared_ptr<Fruit>();
    }
    virtual std::shared_ptr<Animal> getAnimal(const std::string& name) override {
    
    
        if (name == "山羊") {
    
    
            return std::make_shared<Lamp>();
        } else {
    
    
            return std::make_shared<Dog>();
        }
    }
};

class FactoryProducer {
    
    
    public: 
        static std::shared_ptr<Factory> create(const std::string &name) {
    
    
            if (name == "水果") {
    
    
                return std::make_shared<FruitFactory>();
            } else {
    
    
                return std::make_shared<AnimalFactory>();
            }
        }
};

int main() {
    
    
    std::shared_ptr<Factory> ff = FactoryProducer::create("水果");
    std::shared_ptr<Fruit> fruit = ff->getFruit("苹果");
    fruit->name();
    fruit = ff->getFruit("香蕉");
    fruit->name();
    ff = FactoryProducer::create("动物");
    std::shared_ptr<Animal> animal = ff->getAnimal("山羊");
    animal->name();
    animal = ff->getAnimal("小狗");
    animal->name();
    return 0;
}

El patrón de fábrica abstracto es adecuado para el patrón de diseño derivado de la producción de múltiples productos en serie de fábrica. Agregar nuevas estructuras a nivel de producto es complejo, requiere modificaciones importantes en el sistema original e incluso requiere la modificación del código de capa abstracta, lo que viola la apertura. y principio de cierre.

patrón constructor

El patrón constructor es un patrón de diseño orientado a la creación que utiliza múltiples objetos simples para construir un objeto complejo paso a paso. Puede separar la construcción de un objeto complejo de su representación, proporcionando la mejor manera de crear objetos. Se utiliza principalmente para resolver el problema de la construcción de objetos demasiado complejos.

1. Rol de constructor: proporciona una interfaz abstracta para estandarizar la construcción de cada componente del objeto del producto. En general, esta interfaz es independiente de la lógica empresarial de la aplicación. Quien crea directamente los objetos de producto en el patrón es el rol de Concrete Builder. La clase constructora concreta debe implementar los métodos requeridos por esta interfaz: uno es el método de construcción y el otro es el método de devolución de resultados. En este momento, los empleados de la tienda de fideos de arroz preparan comidas preparadas específicas de acuerdo con los requisitos del cajero, que incluyen fideos de arroz, platos fríos y bebidas adecuados.

2. Función Concrete Builder: esta función la desempeñan clases estrechamente relacionadas con la aplicación y crean instancias de producto cuando la aplicación las llama. Las principales tareas completadas por este rol incluyen: implementar la interfaz proporcionada por el rol de Constructor y completar el proceso de creación de instancias de productos paso a paso. Una vez completado el proceso de construcción, proporcione ejemplos del producto. Son empleados específicos que realizan un determinado paquete.

3. Rol de director: las clases que asumen este rol invocan roles de constructor concretos para crear objetos de producto. El director no tiene un conocimiento específico de la clase de producto, es el objeto constructor específico el que realmente tiene un conocimiento específico de la clase de producto. Es el cajero. Él sabe qué menú preparado quiero y le dirá al personal de la tienda de fideos de arroz que está dentro que prepare el menú preparado.

4. Rol del producto: Los productos son objetos complejos en construcción. El rol de mentor es el rol que trata con el cliente. El rol de director divide las solicitudes del cliente para crear un producto en solicitudes de compilación para partes individuales y luego delega estas solicitudes a roles de constructor específicos. La función específica del constructor realiza trabajos de construcción específicos, pero el cliente no los conoce. Es el menú final, todo está preparado y servido.

El patrón de construcción se basa principalmente en cuatro implementaciones principales:

  • clase de producto abstracto
  • Clase de producto específica: una clase de objeto de producto específica
  • Clase Abstract Builder: interfaz abstracta para cada componente necesario para crear un objeto de producto
  • Clase de constructor de productos específicos: implementa interfaces abstractas y construye varios componentes
  • Director Clase Director: unifica el proceso de construcción, lo proporciona a la persona que llama y obtiene productos a través del director.
#include <iostream>
#include <string>
#include <memory>

/* 通过MacBook的构造理解建造者模式*/

class Computer{
    
    
    public:
        Computer(){
    
    };
        void setBoard(const std::string &board) {
    
     _board = board; }
        void setDisplay(const std::string &display) {
    
     _display = display; }
        virtual void setOs() = 0;
        void showParamaters() {
    
    
            std::string param = "Computer Paramaters: \n";
            param += "\tBoard: " + _board + "\n";
            param += "\tDispaly: " + _display + "\n";
            param += "\tOs: " + _os + "\n";
            std::cout << param << std::endl;
        }
    protected:
        std::string _board;
        std::string _display;
        std::string _os;
};
class MacBook : public Computer{
    
    
    public:
        virtual void setOs() override {
    
    
            _os = "Mac OS x12";
        }
};

class Builder {
    
    
    public:
        virtual void buildBoard(const std::string &board) = 0;
        virtual void buildDisplay(const std::string &display) = 0;
        virtual void buildOs() = 0;
        virtual std::shared_ptr<Computer> build() = 0;
};

class MacBookBuilder : public Builder{
    
    
    public:
        MacBookBuilder() : _computer(new MacBook()) {
    
    }
        void buildBoard(const std::string& board) {
    
    
            _computer->setBoard(board);
        }
        void buildDisplay(const std::string& display) {
    
    
            _computer->setDisplay(display);
        }
        void buildOs() {
    
    
            _computer->setOs();
        }
        std::shared_ptr<Computer> build() {
    
    
            return _computer;
        }
    private:
        std::shared_ptr<Computer> _computer;
};

class Director {
    
    
    public:
        Director(Builder* builder) : _builder(builder) {
    
    }
        void construct(const std::string& board, const std::string& display) {
    
    
            _builder->buildBoard(board);
            _builder->buildDisplay(display);
            _builder->buildOs();
        }
    private:
        std::shared_ptr<Builder> _builder;
};


int main() {
    
    
    Builder * builder = new MacBookBuilder();
    std::unique_ptr<Director> director(new Director(builder));
    director->construct("华硕主板", "三星显示器");
    std::shared_ptr<Computer> computer = builder->build();
    computer->showParamaters();
    return 0;
}

modo proxy

El modo proxy se refiere al proxy que controla el acceso a otros objetos, es decir, el objeto proxy controla la referencia al objeto original. En algunos casos, un objeto no es adecuado o no se puede acceder directamente por referencia, y un objeto proxy puede desempeñar un papel intermediario entre el cliente y el objeto de destino. La estructura del patrón de proxy incluye un objeto que es el objeto de destino real que

usted desea acceder (clase objetivo), uno es un objeto proxy. El objeto de destino y el objeto proxy implementan la misma interfaz: primero acceda a la clase de proxy y luego acceda al objeto de destino a través de la clase de proxy. El modo proxy generalmente se divide en proxy estático y proxy dinámico.

  • Proxy estático significa que la relación entre la clase de proxy y la clase de proxy se ha determinado en el momento de la compilación. En otras palabras, se ha determinado en el momento de la compilación qué clase de proxy desea representar el agente.
  • Proxy dinámico significa que la clase de proxy se genera dinámicamente en tiempo de ejecución y se vincula a la clase de proxy. Esto significa que la clase de proxy no se puede determinar hasta el tiempo de ejecución.

Tomemos como ejemplo el alquiler de una casa: un inquilino alquila una casa y la alquila al propietario a través de una agencia de vivienda, lo que se logra utilizando el modelo de agencia.

#include <iostream>

/* 代理模式 */
class RentHouse {
    
    
    public:
        virtual void rentHouse() = 0;
};


class Landlord : public RentHouse {
    
    
    public:
        void rentHouse() {
    
    
            std::cout << "房子租出去了" << std::endl;
        }
};

class Intermediary : public RentHouse {
    
    
    public:
        void rentHouse() {
    
    
            std::cout << "发布招租启事" << std::endl;
            std::cout << "带人看房" << std::endl;
            _landload.rentHouse();
            std::cout << "负责租后维修" << std::endl;
        }
    private:
        Landlord _landload;
};

int main() {
    
    
    Intermediary intermediary;
    intermediary.rentHouse();
    return 0;
}

Guess you like

Origin blog.csdn.net/Tianzhenchuan/article/details/133531016