前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Golang之旅36-方法和工厂模式

Golang之旅36-方法和工厂模式

作者头像
皮大大
发布2021-03-02 16:04:08
1930
发布2021-03-02 16:04:08
举报
文章被收录于专栏:机器学习/数据可视化

方法和工厂模式

方法介绍

在创建了一个结构体之后,结构体可能还具有一些行为,比如Person结构体还有说话、跑步、学习等,此时通过方法才能完成。

Golang中的方法是作用在指定数据类型上的,是自定义类型。

代码语言:javascript
复制
type A struct{
  Num int
}

func (a A) test(){  // A结构体有种方法叫做test()
  fmt.Println(a.Num)
}
代码语言:javascript
复制
package main
import "fmt"

type Person struct{
  Name string
}

func (p Person) test(){   // 定义方法test()
  p.Name = "jack"
  fmt.Println("test() name=", p.Name)
}

func main(){
  var p Person  // 创建结构体实例
  p.Name = "xiaoming"
  p.test()  // 调用方法
  fmt.Println("main() p.Name=", p.Name)   // 输出是xiaoming
}
  • 方法test和结构体Person进行绑定;只能通过Person类型的变量来进行调用
  • p这个名字是任意指定的,一般是结构体名字的小写
方法声明
代码语言:javascript
复制
func (receiver type) methodName (参数列表)(返回值列表){
  方法体
  return 返回值
}
  • 参数列表:表示方法输入
  • receiver type:type是结构体,该方法和type结构体进行绑定
  • receiver :是type类型的一个变量实例
  • return语句不是必须的
  • 返回值列表可以有多个
方法案例
  • 方法中不接收参数
  • 方法中接收一个参数
  • 方法中接收多个参数并有返回值
代码语言:javascript
复制
package main
import "fmt"

type Person struct{
  Name string
}

func (p Person) speak(){   // 定义方法speak():最直接
  fmt.Println(p.Name, "是一个好人!")
}

func (p Person) jisuan(){   // 方法中没有参数
  res := 0
  for i := 1;i <= 1000;i++{
    res += i
  }
  fmt.Println(p.Name, "计算的结果是=", res)
}

func (p Person) jisuan2(n int){   // 方法中接受一个参数
  res := 0
  for i := 1;i <= n;i++{
    res += i
  }
  fmt.Println(p.Name, "计算的结果是=", res)
}

func (p Person) getSum(n1, n2 int) int{   // 方法中接受多个参数和返回值
  return n1 + n2
}

func main(){
  var p Person  // 创建结构体实例
  p.Name = "Tom"
  p.speak()  // 调用方法
  p.jisuan()
  p.jisuan2()
  
  res := p.getSum(10,20) // 调用方法,并用res接受结果 
  fmt.Println(res)
}
方法的调用和传参机制
代码语言:javascript
复制
package main
import "fmt"

// 定义结构体
type Person struct{
  Name string
}

// 定义一个方法
func (p Person) getSum(n1, n2 int) int{   // 方法中接受多个参数和返回值
  return n1 + n2
}

func main(){
  var p Person  // 创建结构体实例 p
  n1 := 10
  n2 := 20
  res := p.getSum(n1, n2) // 调用方法,并用res接受结果;调用的时候创建独立的栈
  fmt.Println(res)
}

  • 通过一个变量调用方法时,调用机制和函数类似
  • 不同之处:调用方法时,变量本身也会作为一个参数传递到方法中
  • 如果变量是值类型,进行值拷贝;如果变量是引用类型,进行地址拷贝
代码语言:javascript
复制
package main
import "fmt"

type Circle struct{
  radius float64
}

// 声明一个方法area和Circle进行绑定,返回的值面积
func (c Circle) area() float64{
  return 3.14 * c.radius * c.radius
}

// 为了提高效率,通常将方法和结构体的指针类型进行绑定
func (c *Circle) area2() float64{
  // 标准写法:c是指针,(*c).radius
  //return 3.14 * (*c).radius * (*c).radius  
  fmt.Println("c 是 *Circle 指向的地址", c)  // c本身就是指针
  c.radius = 10
  return 3.14 * c.radius * c.radius
}

func main(){
  var c Circle   // 声明结构体
  c.radius = 4.0  // 半径
  res := c.area()  // 调用方法
  fmt.Println(res)
  
  var c Circle
  fmt.Println("main c 结构体变量的地址 = %p", &c)   // & 表示的是取地址
  c.radius = 5.0
  res2 := (&c).area2()
  // 编译器底层优化:(&c).area2()  等价于 c.area2()
  fmt.Println("面积=", res2)
  fmt.Println("c.radius = ", c.radius)   // 10
}
  • Golang中的方法作用在指定的数据类型上,因此是自定义数据类型,都可以有方法,不仅仅是结构体,比如int 、float32
  • 如果一个类型实现了String()方法,fmt.Println()默认会调用这个变量的String()进行输出。
代码语言:javascript
复制
package main
import "fmt"

type integer int

func (i integer) print(){
  fmt.Println(i)
}

func (i *integer) change(){
  *i = *i + 1 
}

// 定义结构体
type Student struct{
  Name string
  Age int
}

// 定义方法
func (stu *Student) String() string{
  str := fmt.Sprintf("Name=[%v]  Age=[%v]", stu.Name, stu.Age)
  return str
}

func main(){
  var i integer = 10
  i.print()   // 10
  i.change()
  fmt.Println(i)   // 11,通过指针的引用传递进行修改
  
  stu := Student{
    Name := "tom",
    Age := 20,
  }
  fmt.Println(&stu)    // 传入的是地址
}
课堂练习题
代码语言:javascript
复制
package main
import "fmt"

type MethodUtils struct{
  // 结构体中可以没有任何字段
}

// 方法中没有参数和返回值:打印10 * 8的星号
func (mu MethodUtlis) Print(){
  for i:= 1;i <= 10;i++{
    for j:=1; j <= 8;j++{
      fmt.Print("*")
    }
    fmt.Println()   // 换行作用
  }
}

// 方法中带有参数和返回值:求面积
func (mu MethodUtlis) area(lenght, width  float64) (float64){
  return length * width
}

// 判断奇偶数
func (mu *MehtdoUtlis) JudgeNum(n int){
  if n % == 0{
    fmt.Println("偶数")
  }else{
    fmt.Println("奇数")
  }
}

// 根据输入的行数和列数,打印符号
func (mu *MethodUtlis) PrintStr(m int, n int, key string){
  for i := 1;i<=m;i++{
    for j := 1;j<=n;j++{
      fmt.Println(key)
    }
    fmt.Println()   // 换行操作
  }
}

func main(){
  var mu MethodUtlis  // 创建结构体实例和调用方法
  mu.Print()
  
  res := mu.area(2.5, 8.4)
  fmt.Println(res)
  
  mu.JudgeNum(10)
  
  mu.PrintStr(5,7,"*")
}
代码语言:javascript
复制
package main
import "fmt"

// 实现简易计算器

type Calcuator struct{
  Num1 float64
  Num2 float64
}

// 方式1:写出4种方法单独实现
func (cal *Calcuator) getSum() float64{
  // 标注形式:return (*cal).Num1 +(*cal).Num2
  return cal.Num1 + cal.Num2
}

// 方式2:用一个方法实现
func (cal * Calcuator) getRes(operator byte) float64{
  res := 0.0
  switch operator{
  case '+':
    res = cal.Num1 + cal.Num2
  case '-':
    res = cal.Num1 - cal.Num2
  default:
    fmt.Println("运算符有问题")
  }
  return res
}

func main(){
  var cal Calcuator
  
  // 方式1
  cal.Num1 = 1.2
  cal.Num2 = 2.3
  fmt.Printf("sum=%v \n", fmt.Sprintf("%.2f", cal.getSum()))
  
  // 方式2
  res := cal.getRes("+")  // 调用方法,传入参数
  fmt.Println("res",res)
}
方法和函数的区别
  • 调用方式不同
    • 函数:函数名+实参列表
    • 方法:变量.方法名(实参列表)
  • 对于普通函数,接收者是值类型,不能将指针类型的数据直接传递
  • 对于方法,接收者是值类型时,可以直接使用指针类型的变量调用方法,反之亦然
代码语言:javascript
复制
package main
import "fmt"

type Person struct{
  Name string
}

// 普通函数
func test01(p Person){
  fmt.Println(p.Name)
}

func test02(p *Person){
  fmt.Println(p.Name)
}

// 方法
func (p Person) test03(){
  p.Name = 'jack'
  fmt.Println("test03() = ",p.Name)  // jack
}

func (p *Person) test04(){
  p.Name = "mary"
  fmt.Println(p.Name)   // mary
}


func main(){
  p := Person("tom")
  test01(p)    // 必须传入值类型,和上面的方法必须保持一致
  test02(&p)   // 必须传入指针类型
  
  // 比较3和4的区别
  p.test03()
  fmt.Println("main() p.name=", p.name)   // tom
  (&p).test03()  // 编译器内部处理;形式上传入的地址,仍然是值拷贝
  fmt.Println("main() p.name=", p.name)   // tom
  
  (&p).test04()
  fmt.Println("main() p.Name=", p.Name)   // mary
  p.test04()   // 形式上传入的值类型,仍然是地址拷贝
  fmt.Println("main() p.Name=", p.Name)   // mary
}

总结:到底是哪种拷贝方式,主要是看方法中传递是值类型还是指针类型。注意观察和方法绑定的是什么类型。

OOP实例
代码语言:javascript
复制
package main
import "fmt"

// 1. 先定义结构体
type Student struct{
  name string
  gender string
  age int
  id int
  score float64
}

type Box struct{
  length float64
  width float64
  height float64
}

// 2. 根据结构体写出方法
func (s *Student) say() string{
  infoStr := fmt.Sprintf("name=[%v] gender=[%v] age=[%v] id=[%v] score=[%v]", s.name, s.gender, s.age, s.id, s.score)
  return infoStr
}

func (box *Box) getVolumn() float64{
  return box.length * box.width * box.height
}

func main(){
  // 3. 创建方法的实例,传入具体信息
  var s = Student{
    name: "tom",
    gender: "male",
    age: 18,
    id: 100,
    score: 90,
  }
  fmt.Println(s.say())
  
  // 求体积
  var box Box   // 创建一个实例,同时定义3个属性值
  box.length = 1.1
  box.width = 2.2
  box.height = 2.0
  volumn := box.getVolumn()  // 调用方法
  fmt.Println("体积为=%.2f", volumn)
}
结构体创建方式
代码语言:javascript
复制
package main
import "fmt"

type Stu struct{
  Name string
  Age int
}

func main(){
  // 要注意顺序
  var stu1 = Stu{"小明", 20}
  stu2 := Stu{"小明", 20}
  
  // 顺序无关
  var stu3 = Stu {   
    Name: "小明",
    Age: 20,
  }
  
  stu4 := Stu{
    Age: 20,
    Name: "小明"
  }
  fmt.Println(stu1, stu2, stu3, stu4)
  
  // 方式2:返回结构体的指针类型
  var stu5 = &Stu{"小明", 20}
  stu6 := &Stu{"小明", 20}
  var stu7 = &Stu {   
    Name: "小明",
    Age: 20,
  }
  
  stu8 := &Stu{
    Age: 20,
    Name: "小明"
  }
  fmt.Println(*stu5, *stu6, *stu7, *stu8)
}

工厂模式

当创建的结构体首字母是大写的,外部可以直接调用

但是一般是小写的,那么如何使用呢?此时使用到的是工厂模式

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-11-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 方法和工厂模式
    • 方法介绍
      • 方法声明
        • 方法案例
          • 方法的调用和传参机制
            • 课堂练习题
              • 方法和函数的区别
                • OOP实例
                  • 结构体创建方式
                  • 工厂模式
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档