10.面向对象

    在学习接口时,已经接触了一些面向对象的概念,在这个章节中,我们来重点学习一下面向对象的相关概念。

    面向对象程序设计(OOP:Object-oriented programming)是一种具有对象概念的编程典范,同时也是一种程序开发的抽象方针,包含数据、特性、代码与方法。主要涉及两个核心概念。

  • 类(Class): 根据某类事物的共同特征高度抽象出一种类型。例如动物、人类等
  • 对象(Object): 是类的一个具体实现,即类的实例。例如:人类是一个类,而具体的一个人(例如:小明)则是人类的一个实例,一个类可以拥有很多个实例

    面对对象的核心三要素如下所示:

  • 封装:将属性(数据)和方法(操作)封装,提供访问控制,隐藏实现的细节,仅对外暴露必要的接口
  • 继承:子类可以从父类直接获取属性和方法,减少重复定义。若子类与父类有不同,可以自己重新定义新的属性和方法,也可以覆盖父类的同名属性和方法
  • 多态:父类和子类虽使用同一个方法,但不同子类表现却不一样,就是不同的态。可以简单理解为龙生九子,但各有所好

    只有实现了以特征的语言,才能算是面向对象编程范式的语言。所以从严格意义上来讲,Go并不是面向对象编程范式的语言,但却以借鉴了面向对象的一些特征,Go语言通过组合的方式实现了类似的功能。

10.1 封装

    通过结构体,可以将数据字段封装在里面,也可以为结构体绑定方法。还可以进行访问控制。

  • 字段、方法标识符首字母大写,实现包外可见的访问控制
  • 字段、方法标识符首字母小写,仅包内可见

通过首字母的大小写,实现类似于Java中的public和private访问控制

10.2 继承

    Go语言没有提供继承语法,但可以结构体嵌套来达到类似效果。

10.3 多态

    Go语言不能像Java语言那样使用多态,但可以通过引入接口来达到类似的效果,示例代码如下所示:

package main

import "fmt"

type Run interface {
    Run() string
}

type Animal struct {
    Name string
}

func (a *Animal) Run() string {
    return fmt.Sprintf("Animal:%s is running", a.Name)
}

type Cat struct {
    // 结构体嵌套
    Animal
    Color string
}

func (c *Cat) Run() string {
    fmt.Printf("%s is running,color is: %s\n", c.Name, c.Color)
    return c.Animal.Run()
}

type Dog struct {
    Animal
    Type string
}

func (d *Dog) Run() string {
    fmt.Printf("%s is running,type is: %s\n", d.Name, d.Type)
    return d.Animal.Run()
}

func test(a Run) {
    // 多态
    fmt.Println(a.Run())
}

func main() {
    d := &Dog{Animal: Animal{Name: "小黄"}, Type: "柯基"}
    c := &Cat{Animal: Animal{Name: "小花"}, Color: "黄色"}
    test(d)
    test(c)
}

    运行结果如下所示:

小黄 is running,type is: 柯基
Animal:小黄 is running
小花 is running,color is: 黄色
Animal:小花 is running

    方法test使用同一个类型的同一接口却表现不同,这就是多态。

10.4 覆写

    即子类通过定义一个与父类同名的方法,达到重写的功能,示例代码如下所示:

package main

import "fmt"

type Animal struct {
    Name string
}

func (a *Animal) Run() string {
    return fmt.Sprintf("Animal:%s is running", a.Name)
}

type Cat struct {
    // 结构体嵌套
    Animal
    Color string
}

func (c *Cat) Run() string {
    return fmt.Sprintf("Cat:%s is running", c.Name)
}

func main() {
    c := &Cat{Animal: Animal{Name: "小花"}, Color: "黄色"}
    // 调用子类自身的Run方法
    fmt.Println(c.Run())
    // 调用父类的Run方法
    fmt.Println(c.Animal.Run())
}

    运行结果如下所示:

Cat:小花 is running
Animal:小花 is running

在上面代码通过为Cat增加一个Run方法,从而覆盖了父类的Run方法,从而达到不依赖父类结构体方法。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 课程来自慕课网Visitor.zc老师 封装 访问修饰符 使用指向来调用类中的共有变量 继承 父类中的私有成员变量...
    Maserati丶阅读 1,755评论 0 0
  • 简单来说类是对象的模板在JS中没有类,所以在JS中所谓的 类 就是构造函数。对象就是由构造函数创建出来的实例对象。...
    jqClub阅读 4,744评论 0 0
  • java继承 继承的概念 继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。 继承就是子类继...
    863cda997e42阅读 3,940评论 0 1
  • 但是随着编程语言的不断迭代、演化,人们发现继承这种特性容易造成层次不清、代码混乱,所以,很多编程语言在设计的时候就...
    pyNeverGiveUp阅读 832评论 0 0
  • C++类和对象 C++ 在 C 语言的基础上增加了面向对象编程,C++ 支持面向对象程序设计。类是 C++ 的核心...
    863cda997e42阅读 3,902评论 0 4