07 Estructura de programación orientada a objetos, encapsulación, herencia, polimorfismo, interfaz

  1. Golang admite la programación orientada a objetos, pero es diferente de la tradicional orientada a objetos. No es un lenguaje puramente orientado a objetos. Solo se puede decir que Golang admite funciones de programación orientadas a objetos.
  2. Golang no tiene clases, implementa OOP a través de la estructura struct, que es muy concisa.

Definición de estructura

  1. La sintaxis de la declaración del campo Struct es la misma que la de una variable. Después de crear una estructura, si no se asigna ningún valor al campo, el campo será el valor predeterminado.
  2. La estructura se puede redefinir el tipo, Golang utiliza el nuevo tipo de forma predeterminada.
  3. La estructura es un tipo definido por separado por el usuario y debe tener exactamente los mismos campos (nombre, número, tipo) cuando se convierte con otros tipos.
  4. Cada campo de estructura se puede escribir con una etiqueta, que se puede obtener mediante un mecanismo de reflexión.
  5. Todos los campos de la estructura son continuos en la memoria.

Código de muestra

//声明一个结构体
type Cat struct{
    
    
	Name string
	Age int
	Color string
	Hobby string
}
func test01(){
    
    
	
	//创建结构体并赋值1
	var cat1 Cat
	cat1.Name = "小白"
	cat1.Age = 10
	cat1.Color = "白色"
	cat1.Hobby = "吃 鱼"
	var cat2 Cat
	fmt.Println(cat1,cat2)//{小白 10 白色 吃 鱼} { 0  }
    //创建结构体并赋值2
	c3 := Cat{
    
    "小黑",20,"黑色","shuijiao"}
	 //创建结构体并赋值3
	var c4 *Cat = new(Cat)
	(*c4).Name = "小花" //通过索引访问值 再访问属性
	c4.Age = 10 //也可以直接通过索引访问属性(goLang做了优化)
    //创建结构体并赋值4
	var c5 *Cat  = &Cat{
    
    }
    c5.Name = "c5"
	fmt.Println(cat1,cat2,c3,c4,c5)
	c6 := c3
	c6.Name ="c6"
	//赋值后会直接克隆一个变量给C6,修改c6的值 c3 不受影响
	//{小黑 20 黑色 shuijiao} {c6 20 黑色 shuijiao}
	fmt.Println(c3,c6)
	//创建结构体变量时直接指定字段值
	var c7 = Cat{
    
    
				Name:"小绿",
				Age:40,
				}
	fmt.Println(c7)	

}
type Point struct{
    
    
	x int
	y int
}
type Rect struct{
    
    
	leftUp, rightDown Point
}
type Rect2 struct{
    
    
	leftUp, rightDown *Point
}


func test02(){
    
    

	//结构体所有字段在内存中是连续的
	r1 := Rect{
    
    Point{
    
    1,2} , Point{
    
    3,4}}
	fmt.Printf("r1.rd.x地址= %p r1.rd.y地址= %p r1.lu.x地址= %p r1.lu.y地址= %p  \n",
	&r1.rightDown.x,&r1.rightDown.y,&r1.leftUp.x,&r1.leftUp.y)
	//	fmt.Printf("r1.rd.x地址= %p r1.rd.y地址= %p r1.lu.x地址= %p r1.lu.y地址= %p  \n",
	//&r1.rightDown.x,&r1.rightDown.y,&r1.leftUp.x,&r1.leftUp.y)
	r2 := Rect2{
    
    &Point{
    
    1,2} , &Point{
    
    3,4}}
	fmt.Printf("r2.rd.x地址= %p r2.rd.y地址= %p r2.lu.x地址= %p r2.lu.y地址= %p  \n",
	&r2.rightDown.x,&r2.rightDown.y,&r2.leftUp.x,&r2.leftUp.y)

	//结构体是用户单独定义的类型,和其他类型转换时需要有完全相同的字段(名字、个数、类型)
	
	type A struct{
    
    
		Num int
	}
	type B struct{
    
    
		Num int
	}
	var a A
	var b B
	a = A(b)
	a.Num =10
	fmt.Println(a,b)
	
	//结构体进行type 重新定义,Golang默认是新类型,
	type AA A
	var aa AA
	//var a A = aa 报错需要强转
	var a3 A = A(aa)
	fmt.Println(aa,a3)
	//struct 每个字段 都可以写上一个tag,该tag 可以通过反射机制获取
	//常见的场景就是 序列化和反序列化
	type Monster struct {
    
    
		Name string `json:"name"`
		Age int `json:age`
	}
	m1 := Monster{
    
    "牛魔王",300}
	jsonstr ,err := json.marshal(m1)
	if err != nil {
    
    
		fmt.Println("json 字符串处理错误")
	}
	fmt.Println("json 字符串=",jsonstr)
}

método de estructura

La sintaxis de declaración del método struct indica que la estructura A tiene un método de prueba (a A) para indicar que este método está vinculado al tipo A

func( a A)test(){
    
    } 
func( a A)test2(参数列表)(返回值列表){
    
    
		方法体
	return 返回值
} 

punto importante

  1. Cuando se llama a un método a través de una variable de estructura, el mecanismo de llamada es el mismo que el de una función, excepto que cuando se llama al método de estructura, la variable de estructura también se pasa al método como parámetro.
  2. El método en Golang se especifica en el tipo de datos, por lo que todos los tipos personalizados pueden tener métodos, incluidos struct, int, float32
  3. Si un tipo implementa el método String (), entonces fmt.Println llamará a este método de la variable como resultado de salida por defecto
  4. Cuando se llama a un método a través de una variable, su mecanismo (transferencia de parámetros) es el mismo que el de una función. La diferencia es que la propia variable también se pasa al método como parámetro (si la variable es un tipo de valor, copia de valor se realiza, si es un tipo de referencia, Copia geológica, si se desea modificar el valor de la estructura en el método, se puede usar un puntero para pasar).
  5. El control del alcance de acceso del método es el mismo que el de la función. Solo se puede acceder a la primera letra del nombre del método en el paquete en minúsculas, y se puede acceder a las mayúsculas fuera del paquete.

Código de muestra

//定义结构体
type Person struct{
    
    
	Name string
}
//定义方法
func (p Person) speak(){
    
    
	fmt.Println(p.Name,"是一个好人")
}
func test03(){
    
    
	p := Person{
    
    "zhangsan"}
	p.speak()
}

Modo de fábrica

El patrón de fábrica se utiliza para crear (solo disponible en la estructura de paquete / minúsculas) una instancia mediante un método especificado.
Y si la primera clase de estructura de atributos en minúsculas también se puede obtener mediante un proceso (similar al
código de ejemplo set / get the method java)

func test04(){
    
    
	var stu = mode.NewStudent("tom",12.0)
	fmt.Println(*stu)
	fmt.Println(stu.Name,stu.GetScore())
}
package model

type student struct{
    
    
	Name string
	score float64
}
//通过工厂方法获取 隐藏结构的实例
func NewStudent(n string,s float64) *student{
    
    
	return &student{
    
    Name:n,score:s}
}
//通过方法或去被封装的属性
func( stu *student) GetScore () float64{
    
    
	return stu.score
}

Tres funciones orientadas a objetos

Golang también tiene tres características principales de programación orientada a objetos, pero la implementación es diferente de otros lenguajes de programación orientada a objetos.

Encapsulamiento

Encapsule los campos extraídos juntos
Beneficios: oculte los detalles de implementación y utilice el método de acceso unificado para verificar los datos y garantizar la racionalidad de los datos
.

  1. Ponga en minúscula la primera letra de la estructura y el campo (similar a java private)
  2. Proporcionar funciones de modo de fábrica para la estructura encapsulada, con la primera letra en mayúscula, similar a un constructor
  3. Proporcione el método Set / Get con la primera letra en mayúscula para que la estructura se utilice para la lectura de propiedades y la escritura de
    código de muestra.
func test05(){
    
    
	var per = mode.NewPerson("TOM")
	per.SetAge(50)
	per.SetSal(10000.0)
	fmt.Println(per)
	fmt.Println(per.GetAge(),per.GetSal(),per.Name)

}
package model
import (
	"fmt"
)
type person struct{
    
    
	Name string
	age int
	sal float64
}
// 封装工厂方法 创建结构体实例
func NewPerson(name string) *person{
    
    
	return &person{
    
    
		Name:name,
	}
}
func (p *person)SetAge(age int){
    
    
	if age>0 && age<150{
    
    
		p.age = age 
	} else{
    
    
		fmt.Println("年龄不在合法范围内")
	}

}

func (p *person)SetSal(sal float64){
    
    
	if sal>3000 && sal<30000{
    
    
		p.sal = sal 
	} else{
    
    
		fmt.Println("薪资不在合法范围内")
	}
}
func (p *person)GetAge() int {
    
    
	fmt.Println("====>",p.age)
	return p.age
}

func (p *person)GetSal() float64 {
    
    
	fmt.Println("====>",p.sal)
	return p.sal
}

heredar

La herencia puede resolver la reutilización del código.Cuando varias estructuras tienen los mismos campos y métodos, una estructura se puede abstraer para contener propiedades y métodos públicos. Otras estructuras no necesitan definir estas propiedades y métodos, solo necesitan anidar esta estructura.
Es decir, si una estructura está anidada con otra estructura anónima, entonces esta estructura puede acceder directamente a los métodos y campos de la estructura anónima, logrando así la herencia.
Forma gramatical básica

type Goods struct{
    
    
	Name string
	Price int
}
type Book struct{
    
    
	Goods //嵌套匿名结构体
	Writer string
}

Código de muestra

func test06(){
    
    
	pu := &Pupil{
    
    }
	pu.Student.Name = "TOM"
	pu.Student.Age =12
	pu.Age=13//对继承的字段可以简化访问
	pu.testing();
	pu.Student.ShowInfo()
	pu.ShowInfo()//方法简化访问
	//声明结构体时直接为嵌套结构体赋值
	pu2 := &Pupil{
    
    
		Student{
    
    Name: "Jack",
			Age:19,
			Score:100,
		},
	}
	pu2.ShowInfo()
}
//定义学生类
type Student struct{
    
    
	Name string
	Age int
	Score int
}
//显示学生信息
func (stu *Student)ShowInfo(){
    
    
	fmt.Println(stu)
}
func (stu *Student) SetCore(score int){
    
    
	stu.Score = score
}
//定义小学生类
type Pupil struct{
    
    
	Student//嵌入 学生结构体
}
func (p *Pupil) testing(){
    
    
	fmt.Println("小学生正在考试...")
}

punto importante

  1. La estructura puede utilizar todos los campos y métodos de la estructura anónima, independientemente de si la primera letra es mayúscula o minúscula.
  2. Acceso a la estructura Los métodos y propiedades de la estructura anónima se pueden simplificar, simplificando la lógica de acceso durante el acceso: si la estructura actual tiene propiedades / métodos de acceso, llame directamente a las propiedades / métodos de esta estructura, si la estructura actual no tiene las propiedades / métodos, luego busque el atributo / método en la estructura anónima anidada, llámelo cuando lo encuentre e informe un error si no lo encuentra.
  3. Cuando la estructura y la estructura anidada tienen las mismas propiedades o métodos, el compilador adopta el principio de acceso más cercano. Si desea llamar a las propiedades / métodos en la estructura anónima, debe llamarlos por el nombre de la estructura anónima (no se pueden realizar llamadas simplificadas)
  4. Si una estructura tiene varias estructuras anidadas y varias estructuras anidadas contienen las mismas propiedades / métodos, entonces se debe especificar el nombre de la estructura anónima al llamar. De lo contrario, el compilador informa de un error.
  5. Si una estructura está anidada con una estructura con nombre, entonces las dos son una relación de combinación. En este momento, debe traer el nombre de la estructura al acceder a las propiedades / métodos de la estructura compuesta.
  6. Después de anidar las variables de estructura, puede asignar valores directamente a la estructura anidada al crear variables de estructura.
  7. Si un tipo de datos básico como int está anidado en una estructura, el método de acceso es A.int = 12, si hay varios campos int, se debe especificar el nombre

Herencia múltiple
Si una estructura está anidada con varias estructuras anónimas, entonces puede acceder a todos los campos y métodos de la estructura. Esta es la herencia múltiple de Golang
* Si varias estructuras anidadas contienen los mismos atributos / métodos, entonces el nombre de la estructura anidada debe ser especificado al llamar al método

interfaz

El tipo de interfaz puede definir un conjunto de métodos, pero no necesita ser implementado, y la interfaz no puede contener variables, estos métodos se implementan en el tipo concreto de acuerdo con la situación real.

type 接口名 interface{
    
    
	method1()
	method2()
}

Programa de muestra

type Gun interface{
    
    
	Fire()
}
type Gun51 struct{
    
    
	
}
func(g Gun51) Fire(){
    
    
	fmt.Println("Gun51连续发射7mm子弹...")
}
type GunRPG struct{
    
    
	
}
func(g GunRPG) Fire(){
    
    
	fmt.Println("RPC发射火箭弹...")
}
func test07(){
    
    
	var g51 Gun51
	var rpg GunRPG
	var gun Gun = g51
	gun.Fire();
	gun = rpg
	gun.Fire()
}

punto importante

  1. Todos los métodos de la interfaz no tienen cuerpo de método, lo que refleja la idea de alta cohesión y bajo acoplamiento del programa.
  2. Golang no implementa explícitamente la interfaz, mientras una variable contenga todos los métodos declarados por la interfaz , se considera que la variable implementa la interfaz.
  3. La interfaz en sí no crea una instancia, pero puede apuntar a una instancia que implementa la interfaz.
  4. Un tipo personalizado solo puede asignar una variable del tipo a esta interfaz solo si implementa una determinada interfaz.
  5. Cualquier tipo personalizado puede darse cuenta de que la interfaz no es necesariamente una estructura, y una variable puede realizar múltiples interfaces
  6. La interfaz A puede heredar muchas otras interfaces B y C. En este momento, si la variable implementa la interfaz A, también debe implementar las interfaces B y C al mismo tiempo. Si la interfaz (incluida la interfaz heredada) contiene métodos duplicados, el compilador informa de un error
  7. La interfaz es un tipo de puntero de forma predeterminada, si no está inicializada, la salida predeterminada es nula
  8. La interfaz vacía no tiene métodos, todos los tipos implementan la interfaz vacía, es decir, todos los tipos se pueden asignar a la interfaz vacía

Herencia y comparación de interfaces La
herencia resuelve principalmente la capacidad de reutilización y mantenimiento del código. Enfatice la reutilización común.
La interfaz es principalmente una especificación de diseño, lo que permite que otros tipos cumplan con la especificación. Enfatizar la realización de capacidades comunes.
La interfaz logra el desacoplamiento del código hasta cierto punto

Polimorfismo

Las variables tienen muchas formas y el polimorfismo en Golang se implementa a través de interfaces. Según diferentes implementaciones de la misma interfaz en diferentes variables, la interfaz presenta una variedad de estados diferentes. (Consulte el ejemplo de interfaz) El
polimorfismo generalmente se refleja en los parámetros del método.

Aserción de tipo
¿Cómo asignar una variable de interfaz a una variable de tipo personalizado? Primero debe determinar el tipo de variable, es decir, escriba el código de muestra de aserción

func test08(){
    
    
	var x interface{
    
    }
	var b2 int = 1
	x = b2
	// 将x 转换为int  
	y, ok := x.(int); 
	if ok {
    
    
		fmt.Printf("转换成功 y的类型是%T 值是=%v \n",y,y);
	}else{
    
    
		fmt.Println("转换失败")
	}
	fmt.Println("x.(int)==>",x.(int))
	//fmt.Println("x.(type)==>",x.(type)) 只能在switch 中使用 这里使用报错
	//调用断言方法
	TypeJudge(b2)
}

func TypeJudge(items... interface{
    
    }){
    
    
	for index,x := range items{
    
    
		switch x.(type){
    
    
		case bool :
			fmt.Printf("第%v个参数是 bool 类型,值是 %v \n",index,x)
		case float32:
			fmt.Printf("第%v个参数是 float32 类型,值是 %v \n",index,x)
		case float64:
			fmt.Printf("第%v个参数是 float64 类型,值是 %v \n",index,x)
		case int,int32,int64:
			fmt.Printf("第%v个参数是 int 类型,值是 %v \n",index,x)
		case string:
			fmt.Printf("第%v个参数是 string 类型,值是 %v \n",index,x)
		default:
			fmt.Printf("第%v个参数 类型不确定,值是 %v \n",index,x)

		}
	}
}

Supongo que te gusta

Origin blog.csdn.net/zhangxm_qz/article/details/114681802
Recomendado
Clasificación