13-面向对象
面向对象三大特性:封装、继承、多态
Golang 没有类的概念,也没有面向对象的概念。
准确的说,面向对象、封装、继承、多态、抽象等等,这些都是编程思想,不同的语言实现这些特性的方式不同。
例如 Java
,用的是类 class
,访问修饰符 public、protected、default、private
等来实现;
在 Golang 中,用的是结构体 struct
、标识符首字母大小写 等来实现。
面向过程、面向对象、一切皆对象、一切皆文件 等诸如此类的概念,
在学习之初可能会成为初学者的一道坎,也可能是帮助新手更快入门的好帮手;
等到学到一定程度以后,这些思想能帮助我们快速解决一些问题,也可能开始禁锢我们的思想;
善于变通者会慢慢看透本质,脱离这些思想的枷锁,对编程形成自己的认知。
变通者和不变通者的区别在于:是否愿意深入底层(汇编、组原等等),是否愿意摒弃语言执念。
- 封装
- 封装也叫信息隐藏、数据访问保护。通过暴露有限的访问接口,外部仅能通过类提供的方式来访问内部信息或数据。
- 需要编程语言提供权限访问控制语法来支持。如:
- Java 中的
public、protected、private
- Python 中标识符的双下划线前缀
__xxx
或__slots__
白名单 - Golang 中标识符首字母大小写。
- Java 中的
- 封装存在的意义,
- 一方面是保护数据不被随意修改,提高代码可维护性;
- 一方面是仅暴露有限的必要接口,提高易用性。
- 抽象
- 封装讲的是如何隐藏信息、保护数据,抽象讲的就是如何隐藏的具体实现。
- 抽象可以通过接口类或者抽象类来实现,但不需要特殊的语法机制来支持。
- 抽象存在的意义,
- 一方是是提高代码的可扩展性、维护性,修改实现不需要修改定义,减少代码改动范围;
- 另一方面,抽象也是处理复杂系统的有效手段,能有效过滤掉不必关注的信息。
- 继承
- 继承是用来表示类之间的
is-a
和has-a
的关系,分为两种模式:单继承和多继承。 - 单继承表示一个子类只能继承一个父类,多继承表示一个子类可以继承多个父类。
- 需要编程语言提供特殊语法机制来支持。如:
- Java 中的
extends
关键字 - Python 中类名后的括号
- Go 中的结构体嵌套
- Java 中的
- 继承存在的意义,是用来解决代码复用的问题。
- 继承是用来表示类之间的
- 多态
- 多态是指子类可以替代父类。在实际代码运行过程中,调用子类的方法实现。
- 需要编程语言提供特殊语法机制来支持。如:继承、接口、duck-typing。
- 多态可以提高代码的扩展性和复用性,是很多设计模式、设计原则、编程技巧的代码实现的基础。
封装
在 Java 等面向对象中,会将一类事物抽象出属性
和行为
,并通过语言层面限定 访问性
。
属性 使用基本数据类型或复合类型描述;
行为 通过函数描述,并称之为方法
;
属性+方法
组成一个类class
;
访问性各个语言实现不同。
在 Go 中没有类的概念,
而是将事物的 属性 使用基本数据类型或复合数据类型 封装在结构体中,
而 行为 是通过 给函数限定调用者的方式 实现。
访问性 是通过首字母大写为 public
,首字母小写为 private
。
这种限定了调用者的函数,我们称之为方法。
而拥有方法的结构体,我将其称之为类。
Golang 中,类 = 结构体 + 限定调用者的函数 + 访问性
// class A
type A struct {
fieldA
}
func (a A) aMethod() {
...
}
构造函数
Go 不支持像 Java 那样的构造函数,但是可以通过 标识符首字母大小写 + 工厂函数
实现构造函数。
一般分为4步:
- 将结构体、字段的首字母小写
- 结构体所在的包提供一个工厂模式的函数,首字母大写,模拟一个构造函数。按照规范,构造函数的名字以
new
或New
开头。注意返回值必须是结构体指针,因为结构体是值类型。 - 提供首字母大写的 Get 方法,用于获取属性的值
- 提供首字母大写的 Set 方法,用于设置属性的值
eg:
type person struct {
name string
age int
}
// 写一个工厂函数,首字母大写,其他方就可以访问,相当于构造函数
func NewPerson (name string, age int) (*person, error) {
if age <= 0 || name == "" {
return nil, errors.New("Parameters Error.")
}
return &person{
name, age}, nil
}
// Get 方法
func (p person) GetName() string{
return p.name
}
func (p person) GetAge() int {
return p.age
}
// Set 方法
func (p person) SetName(name string) error {
if name == "" {
return errors.New("Parameters Error.")
}
p.name = name
return nil
}
func (p person) SetAge(age int) error {
if age <= 0 {
return errors.New("Parameters Error.")
}
p.age = age
return nil
}
func main() {
// 然后这样创建对象:
p := NewPerson("Boii", 18)
fmt.Println(p) // &{Boii 18}
p.GetName() // Boii
p.SetAge(