La "herencia bidireccional" de Golang hace que la programación sea más elegante

1. Antecedentes

Cuando el autor desarrolló un sistema distribuido, el algoritmo balsa se implementó en el lado de la administración para evitar un solo punto de falla de todo el sistema. La descripción general de todo el sistema es la siguiente:

  1. El estado de todos los nodos en el lado de la administración es consistente, por lo que es más apropiado usar peer para definir el nodo del lado de la administración; 
  2. Se elige un líder entre todos los nodos del lado de la administración. Todas las decisiones relacionadas con la administración del sistema son emitidas por el líder. El par sincroniza la decisión del líder para que el estado de todos los pares sea consistente. Cuando el par donde se encuentra el líder es anormal, reiniciar El líder electo puede continuar tomando decisiones sobre la base del líder anterior;
  3. Un punto a tener en cuenta: la decisión del líder debe proponerse en balsa. Más de la mitad de los pares deben aprobar lo suficiente para ser aplicado por el compañero. Por lo tanto, desde el inicio de la decisión del líder hasta la confirmación del éxito de la decisión. haciendo la ejecución por todo el sistema, debe haber una serie de pasos. Proceso, nosotros
  4. Esto se describe simplemente como un proceso asincrónico;

Este artículo no analiza el contenido relacionado con la balsa, pero utiliza balsa para introducir el concepto de compañero y líder. De acuerdo con la descripción anterior, ¿cómo se debe definir la clase utilizando el método de programación de objetos de fase?

2. Análisis

El método más convencional debería ser: el líder es un par con más atributos y conexiones, por lo que el líder debe heredar del self peer, luego el código (C ++) se define de la siguiente manera:

// 定义peer类
class Peer {
public:
    Peer(){}
    ~Peer(){}
public:
    void PeerMethod(void) {}
private:
    int peerVariables;
}
// 定义leader类,继承自peer
class Leader : public Peer {
public:
    Leader(){}
    ~Leader(){}
public:
    void LeaderMethod(void) {}
private:
    int leaderVariables;
}

A continuación, veamos si este método de definición encontrará problemas en el proceso de implementación. Analicémoslo desde las siguientes tres perspectivas:

  1. Vista de líder: el objeto es construido por la clase líder, y las conexiones y atributos sirven para la toma de decisiones, dado que el auto-par se hereda, el estado actual del sistema se obtiene automáticamente al heredar el par;
  2. Vista de pares: el objeto es una estructura de tipo par, y la conexión y los atributos están relacionados con el estado del sistema de sincronización, y no hay necesidad de lidiar con problemas de toma de decisiones, por lo que puede hacer lo que quiera;
  3.  Punto de vista del líder entre pares: Los dos puntos de vista anteriores aún no se comprenden bien, entonces, ¿qué es un líder entre pares? ¡Es el líder entre pares!

Con respecto al compañero líder, muchos de mis compañeros definitivamente no podrán quedarse quietos, ¿no es una tontería? ¿No es lo mismo que el líder? Esto comienza en el escenario real. Antes de que se elija al líder, todos los nodos siguen siendo pares. En este momento, el objeto de servicio lo construye el par; cuando un par es elegido con éxito como líder, se proporciona el objeto de servicio. debe ser construido por un líder Recuerde que el líder también es un par, por lo que los objetos construidos por el líder tienen capacidades tanto de pares como de líder.

Entonces, la pregunta es, ¿qué tipo de objeto se debe usar para brindar servicios? A partir de la relación de herencia mencionada anteriormente, es relativamente más razonable usar objetos de tipo par, y los objetos de subclases se pueden asignar a objetos de clases de clase. Sin embargo, cuando el par necesita cambiar al estado de líder, independientemente de si es C ++ o JAVA, se deben agregar declaraciones de conversión más o menos obligatorias, el objeto del par se asigna al objeto líder y luego el objeto líder se utiliza para ejecución Algunas operaciones. Como se muestra en el siguiente código:

{
    Leader *leader = (Leader*)peer;
    leader->LeaderMethod();
}

Cuando el autor no tocó golang antes, sentí que el código anterior no podía ser más normal. Después de definir la llamada "herencia bidireccional", sentí que el código anterior no era lo suficientemente elegante. La llamada herencia bidireccional significa que dos tipos se heredan entre sí, lo cual es impensable en C ++ o JAVA. Una clase A es una subclase de la clase B y una subclase de la clase B. Éticamente, no tiene sentido. también es imposible de lograr. Pero esto se puede hacer en golang, como se muestra en el siguiente código:

// 定义Peer
type Peer struct {
    *Leader
    peerVariables int
}
// 定义Leader
type Leader struct {
    *Peer
    leaderVariables int
}

¿Cómo interpretar estas dos categorías?

  1. Peer: Básicamente es lo mismo que el Peer mencionado anteriormente, la diferencia es que Peer. Leader está vacío, significa Peer normal, si no está vacío, significa Líder;
  2. Líder: exactamente igual que el Líder mencionado anteriormente;

Solo este pequeño cambio hará que la lógica sea más suave y el código más elegante. Como el objeto del servicio es del tipo Peer, no importa si es el cambio de identidad o el juicio de identidad, se vuelve muy natural, como se muestra en el siguiente código:

// 成功选举为Leader
{
    peer.Leader = &Leader{Peer: peer}
}
// 需要切换身份处理时
{
    if nil != peer.Leader {
        peer.LeaderMethod()
    }
}

Si el lector no tiene ningún sentimiento sobre el código anterior y piensa que no es diferente de C ++ / JAVA, o el lector es un gran dios y no aprecia en absoluto la habilidad del autor, o no ha obtenido la del autor. punto. ¡Solo este pequeño cambio ha actualizado mucho el código y la lógica del autor!

para resumir

De hecho, desde la perspectiva de la herencia, no debería haber una conversación bidireccional, de lo contrario no es el concepto de herencia.El autor no ha tomado prestado el mecanismo de herencia de golang para simplificar la programación y la lógica. En este punto, tenemos que mencionar la esencia de la herencia. El siguiente código C se utiliza para describir la esencia de la herencia:

// 定义结构体A
struct A {
    int a;
}
// 定义结构体B,并且继承A
struct B {
    struct A a;
    int b;
}

La llamada herencia es en realidad que el compilador coloca todas las variables miembro de la clase en la subclase. Al acceder a los miembros de la clase (función miembro o variable miembro) en la subclase, puede usar el operador punto para hacer referencia a ella. , en lugar de utilizar el lenguaje C. Necesita Baa para acceder. Por supuesto, el lenguaje del cara a objeto ha extendido muchas funciones en la herencia, lo que está más allá del alcance de esta discusión y no se describirá demasiado.

Echemos un vistazo al método de herencia de golang:

// 定义类型A
type A struct {
    a int
}
// 定义类型B,并且继承A
type B struct {
    A
    b int
}

Este método de herencia de golang es el mismo que C ++ / JAVA. El nuevo objeto de subclase construye una clase al mismo tiempo, porque sizeof (B) = sizeof (A) + B variables miembro son siempre grandes (la tabla de función virtual se ignora aquí) , La subcategoría contiene todo el contenido de la subcategoría. Pero golang tiene otro método de herencia como se muestra en el siguiente código:

type B struct {
    *A
    b int
}

De esta forma, el nuevo B debe ser el nuevo A, que es similar al anterior, la diferencia radica en si la memoria es uno o dos. Es esta
diferencia la que les da a los desarrolladores más espacio para jugar, y el caso mencionado en este artículo se aprovecha de esto. Algunas personas pueden decir que esto también se puede lograr en lenguaje C, como se muestra en el siguiente código:

struct B {
    struct A* a;
    int b;
}

Esto es cierto, aunque es un poco engorroso referirse a miembros de A, como Ba-> a, el efecto logrado por golang es el mismo. El autor suele estar de acuerdo con las ideas de estos lectores, pero lo que quiero decir es: Aunque el método de realización final de dos conceptos diferentes es el mismo, cada concepto tiene su lugar de aplicación, lo que puede hacer de este concepto. El contexto es más fácil de entender. entender y más claro. Ba y Ba-> a, aunque el efecto es el mismo, el significado expresado es diferente, el significado del primero a es un atributo de B, y el significado del segundo a es B y un atributo llamado Atributos.

Finalmente, volvamos al caso del líder y el par. De hecho, no es una verdadera herencia bidireccional. Si el líder hereda del par, es una verdadera herencia.
El puntero del líder en el par debe ser un atributo del par, es decir, peer.leader. El autor usa el método de herencia de golang para
realizar la ilusión de una herencia bidireccional.

Supongo que te gusta

Origin blog.csdn.net/weixin_42663840/article/details/97695651
Recomendado
Clasificación