Go语言面组合式向对象编程基础总结

Go语言的面向对象编程简单而干净,通过非侵入式接口模型,否定了C/C++ Java C#等传统面向对象编程语言的复杂度的必要性,我们发现在Go中即使简单的组合也能达到传统面向对象语言的效果,而且耦合度非常低,按照Go的作者之一也就是C语言的作者之一说的一句话:Go是更好的C语言。

1、Go中任意类型Any即  interface{}类型,也就是空接口,可以赋值为任意类型

2、可以为其他类型 内置类型 不包括指针类型添加相应的方法 但是注意的一点是一定要用别名。。进行包装

记住想要 为类型 添加新的方法 那么请把类型定义别名,别名和原来的类型就不一样了成了新类型

在Go中只能对非本地类型添加方法......也就是int不能添加方法 需要 type Int int 才可以为Int添加方法

package main

import(

"fmt"

)

type Integer int

func (a Integer) Less(b Integer)bool {

return a<b

}

func main(){

var a Integer=1

r:=a.Less(2)

fmt.Println(r)

}

/////用法2 组合之指针

package main

import(

"fmt"

)

type Integer int

func (a *Integer) Less(b Integer)bool {

*a-=10

return *a<b

}

type Int int

func (a *Int) LessIne(b Int)bool{

return *a<b

}

func main(){

var a Integer=1

var aa int=10

a=Integer(aa)

r:=a.Less(2)

fmt.Println(r)

}

3、go中不存在this指针,通过语法糖显式传递对象

在Go语言中没有隐藏的this指针”这句话的含义是:

 方法施加的目标(也就是“对象” )显式传递,没有被隐藏起来;

 方法施加的目标(也就是“对象” )不需要非得是指针,也不用非得叫this。

4、用type定以后的别名类型就是新类型了 只有强制转换才能使用

package main

import(

"fmt"

)

type Integer int

func (a Integer) Less(b Integer)bool {

return a<b

}

func main(){

var a Integer=1

var aa int=10

a=Integer(aa)  //不强制转换会编译出错的

r:=a.Less(2)

fmt.Println(r)

}

5、Go中的值语义和引用

channel map本质是指针 因为复制他们没有意义  ........数组切片 []type 本质上是值类型 interface接口非常重要

值语义和引用语义的差别在于赋值,比如下面的例子:

b = a

b.Modify()

如果b的修改不会影响a的值,那么此类型属于值类型。如果会影响a的值,那么此类型是引用

类型。

Go语言中的大多数类型都基于值语义,包括:

 基本类型,如byte、int、bool、float32、float64和string等;

 复合类型,如数组(array) 、结构体(struct)和指针(pointer)等。

Go语言中类型的值语义表现得非常彻底。我们之所以这么说,是因为数组。

如果读者之前学过C语言,就会知道C语言中的数组比较特别。通过函数传递一个数组的时

候基于引用语义, 但是在结构体中定义数组变量的时候基于值语义 (表现在为结构体赋值的时候,

该数组会被完整地复制) 。

Go语言中的数组和基本类型没有区别,是很纯粹的值类型,例如:

var a = [3]int{1, 2, 3}

var b = a

b[1]++

fmt.Println(a, b)

该程序的运行结果如下:

[1 2 3] [1 3 3]。

这表明b=a赋值语句是数组内容的完整复制。要想表达引用,需要用指针:

var a = [3]int{1, 2, 3}

var b = &a

b[1]++

fmt.Println(a, *b)

该程序的运行结果如下:

[1 3 3] [1 3 3]

这表明b=&a赋值语句是数组内容的引用。变量b的类型不是[3]int,而是*[3]int类型。

Go语言中有4个类型比较特别,看起来像引用类型,如下所示

数组切片:指向数组(array)的一个区间。

 map:极其常见的数据结构,提供键值查询能力。

 channel:执行体(goroutine)间的通信设施。

 接口(interface) :对一组满足某个契约的类型的抽象。

但是这并不影响我们将Go语言类型看做值语义。下面我们来看看这4个类型。

数组切片本质上是一个区间,你可以大致将[]T表示为:

type slice struct {

first *T

len int

cap int

}

因为数组切片内部是指向数组的指针,所以可以改变所指向的数组元素并不奇怪。数组切片

类型本身的赋值仍然是值语义。

map本质上是一个字典指针,你可以大致将map[K]V表示为:

type Map_K_V struct {

// ...

}

type map[K]V struct {

impl *Map_K_V

}

基于指针,我们完全可以自定义一个引用类型,如:

type IntegerRef struct {

impl *int

}

channel和map类似,本质上是一个指针。将它们设计为引用类型而不是统一的值类型的原因

是,完整复制一个channel或map并不是常规需求。

同样,接口具备引用语义,是因为内部维持了两个指针,示意为:

type interface struct {

data *void

itab *Itab

}

接口在Go语言中的地位非常重要。关于接口的内部实现细节,在后面的高阶话题中我们再

细细剖析。

7、Go语言中的结构体  组合非继承

Go语言的结构体(struct)和其他语言的类(class)有同等的地位,但Go语言放弃了包括继

承在内的大量面向对象特性,只保留了组合(composition)这个最基础的特性。

组合甚至不能算面向对象特性,因为在C语言这样的过程式编程语言中,也有结构体,也有

组合。组合只是形成复合类型的基础。

上面我们说到,所有的Go语言类型(指针类型除外)都可以有自己的方法。在这个背景下,

Go语言的结构体只是很普通的复合类型,平淡无奇。例如,我们要定义一个矩形类型:

type Rect struct {

x, y float64

width, height float64

}

然后我们定义成员方法Area()来计算矩形的面积:

func (r *Rect) Area() float64 {

return r.width * r.height

}

可以看出, Go语言中结构体的使用方式与C语言并没有明显不同。

8、Go中的组合精华  创建结构体指针 和为 结构体扩充成员函数的时候.....传递 值类型和指针类型的区别

package main

import "fmt"

type Rect struct{

x,y float64

width,height float64

}

///如果写成rct Rect 那么内部的修改不会影响到 外部结构

///如果写成rct*Rect那么内部的修改会影响到外部结构的值 这就是 指针的效果

func (rct *Rect)Area() float64{

rct.width=1000 ///也可以(*rct).width=1000一样

return rct.width*rct.height

}

func main(){

rct:=new(Rect)

//对于结构体指针,...调用方法和值类型一样直接.唯一的区别是 作为参数传递的时候 传递的是地址 值可以被修改

//所以进行组合的时候就有两种选择

// 可以写成 var rct*Rect=&Rect{}

//也可以写成 var rct Rect=Rect{}

//var rct *Rect=new(Rect)

//也可以写成 var rct Rect=Rect{1,2,3,4}

rct.width=10.0

rct.height=10.0

area:=rct.Area()

fmt.Println(area)

fmt.Println(rct.width)

}

9、普通的组合继承 ...........................以及组合指针继承 以及覆盖 和函数 成员名字冲突

//通过值类型继承

package main

import "fmt"

//Go中继承属于匿名组合 .......可以从对象继承 也可以从指针匿名继承...

//匿名继承会去掉包名,,,所以不能同时继承类名相同的  即使不在同一个包中

type Base struct{

Name string

Age  uint8

}

///为Base结构体组合进去两个函数

func (pBase*Base) showName(){

fmt.Println("Age",pBase.Name)

}

func (pBase*Base) showAge(){

fmt.Println("Age",pBase.Age)

}

//创建Sub结构体

type Sub struct{

//组合Base修改内存模型

//匿名组合进Base 对于调用者是不知道的

//即使我们覆盖了 Base的方法 但是我们还是可以通过xxx.Base.xxx()调用基类的方法的

//如果是*Base我们需要在调用处手动添加new Base 否则运行会出错的

Base

}

func (pSub*Sub) showName(){

fmt.Println("Before Sub ShowName")

pSub.Base.showName()

fmt.Println("After Sub ShowName")

}

func main(){

obj:=new(Sub)

obj.Name="张三"

obj.Age=15

obj.showName()

obj.showAge()

}

///通过指针类型继承

package main

import "fmt"

//Go中继承属于匿名组合 .......可以从对象继承 也可以从指针匿名继承...

//匿名继承会去掉包名,,,所以不能同时继承类名相同的  即使不在同一个包中

type Base struct{

Name string

Age  uint8

}

///为Base结构体组合进去两个函数

func (pBase*Base) showName(){

fmt.Println("Age",pBase.Name)

}

func (pBase*Base) showAge(){

fmt.Println("Age",pBase.Age)

}

//创建Sub结构体

type Sub struct{

//组合Base修改内存模型

//匿名组合进Base 对于调用者是不知道的

//即使我们覆盖了 Base的方法 但是我们还是可以通过xxx.Base.xxx()调用基类的方法的

*Base

}

func (pSub*Sub) showName(){

fmt.Println("Before Sub ShowName")

pSub.Base.showName()

fmt.Println("After Sub ShowName")

}

func main(){

obj:=new(Sub)

//由于使用指针继承所以 我们要设置匿名组合模板的内存对象 地址

obj.Base=&Base{}

obj.Name="张三"

obj.Age=15

obj.showName()

obj.showAge()

}

10、Go语言的可见性 权限是包一级的,包外的不能访问包内的小写开头成员......包内无所谓

11、Go的非侵入式接口 和实现

/////Go语言会为每一个成员函数 自动生成对应的函数  比如 func(a *A) 会自动生成 func (a A) .....

///反过来则不行 因为 func (a A)这时候传递的是形参  (&a).xx()改变的是 参数 副本 而不是 外部类

package main

import "fmt"

///非侵入式接口

////接口 和实现完全分析 减少耦合

///实现方只负责实现  接口方只负责封装自己的借口就行...实现方甚至不知道 有这个接口的存在 这就是 Go的 非侵入式接口的特点

type IFly interface{

fly()

}

type ISay interface{

say()

}

type Bird struct{

}

//由于匿名传递进来的是指针类型 所对于接口的赋值必须是 指针

func (pBird*Bird) fly(){

fmt.Println("i am a bird, i can fly()!")

}

//由于匿名传递的不是指针类型是值类型 所以接口赋值 可以不是指针而是值

func (pBird Bird) say(){

fmt.Println("i am a bird, i can say()!")

}

func main(){

birdObj:=Bird{}

var iFly IFly=&birdObj

iFly.fly()

var iSay ISay=birdObj

iSay.say()

}

13、接口之间是可以相互赋值的

实现了相同方法的接口可以相互赋值,如果接口B是A非超集,那么  A可以赋值为B

对象不可以被赋值为接口 ,繁殖接口可以被赋值为实现了 某些方法的对象 或者包含他方法的 接口对象

package main

import "fmt"

///非侵入式接口

////接口 和实现完全分析 减少耦合

///实现方只负责实现  接口方只负责封装自己的借口就行...实现方甚至不知道 有这个接口的存在 这就是 Go的 非侵入式接口的特点

type IFly interface{

fly()

}

type ISay interface{

say()

}

type IFly1 interface{

fly()

}

type ISay1 interface{

say()

}

type Bird struct{

}

//由于匿名传递进来的是指针类型 所对于接口的赋值必须是 指针

func (pBird*Bird) fly(){

fmt.Println("i am a bird, i can fly()!")

}

//由于匿名传递的不是指针类型是值类型 所以接口赋值 可以不是指针而是值

func (pBird Bird) say(){

fmt.Println("i am a bird, i can say()!")

}

func main(){

birdObj:=Bird{}

var iFly IFly=&birdObj

iFly.fly()

var iSay ISay=birdObj

iSay.say()

////接口之间的赋值

var iFly1 IFly1=iFly

iFly1.fly()

}

14、Go中的值类型非常的彻底  数组都是值类型

15、关于给类型添加String()方法   相当于 其他语言的toString 用于打印输出

package main

import "fmt"

///非侵入式接口

////接口 和实现完全分析 减少耦合

///实现方只负责实现  接口方只负责封装自己的借口就行...实现方甚至不知道 有这个接口的存在 这就是 Go的 非侵入式接口的特点

type IFly interface{

fly()

}

type ISay interface{

say()

}

type IFly1 interface{

fly()

}

type ISay1 interface{

say()

}

type Bird struct{

}

//由于匿名传递进来的是指针类型 所对于接口的赋值必须是 指针

func (pBird*Bird) fly(){

fmt.Println("i am a bird, i can fly()!")

}

//由于匿名传递的不是指针类型是值类型 所以接口赋值 可以不是指针而是值

func (pBird Bird) say(){

fmt.Println("i am a bird, i can say()!")

}

func (pBird Bird) String() string{

return "aaaaaaaaaa"

}

func main(){

birdObj:=Bird{}

var iFly IFly=&birdObj

iFly.fly()

var iSay ISay=birdObj

iSay.say()

////接口之间的赋值

var iFly1 IFly1=iFly

iFly1.fly()

fmt.Println(birdObj)

}

16、接口的组合 就是把多个接口组合到一起......接口中只有函数没有属性

type IFly interface{

fly()

}

type ISay interface{

say()

}

type IFly1 interface{

fly()

}

type ISay1 interface{

say()

}

type ISay_Fly interface{

ISay

IFly

}

17、接口查询 obj,ok=val.(Interface)   返回查询的接口 并且返回查询结果

x.(type) 获取类型 只能在switch中用

x.(OterTypeInterface) 判断x是否是指定接口类型 返回指定接口对象,和查询结果

在Go语言中,还可以更加直截了当地询问接口指向的对象实例的类型,例如:

var v1 interface{} = ...

switch v := v1.(type) {

case int:    // 现在v的类型是int

case string: // 现在v的类型是string

...

}

就像现实生活中物种多得数不清一样,语言中的类型也多得数不清,所以类型查询并不经常

使用。它更多是个补充,需要配合接口查询使用,例如:

type Stringer interface {

String() string

}

func Println(args ...interface{}) {

for _, arg := range args {

switch v := v1.(type) {

case int:                        // 现在v的类型是int

case string:                     // 现在v的类型是string

default:

if v, ok := arg.(Stringer); ok { // 现在v的类型是Stringer

val := v.String()

// ...

} else {

// ...

}

}

}

}

当然,Go语言标准库的Println()比这个例子要复杂很多,我们这里只摘取其中的关键部

分进行分析。对于内置类型,Println()采用穷举法,将每个类型转换为字符串进行打印。对

于更一般的情况,首先确定该类型是否实现了String()方法,如果实现了,则用String()方

法将其转换为字符串进行打印。否则,Println()利用反射功能来遍历对象的所有成员变量进

行打印。

是的,利用反射也可以进行类型查询,详情可参阅reflect.TypeOf()方法的相关文档。此

外,

18、 Any类型  对于匿名结构体赋值给任意类型  没法取出 具体每个匿名结构体的内部属性 只能前部打印 通过系统默认的String()函数

var any1 interface{}=1

var any2 interface{}="b"

var any3 interface{}=struct{x ,y string}{"hello,world","aaaaa"}

fmt.Println(any1,any2,any3)

由于Go语言中任何对象实例都满足空接口interface{},所以interface{}看起来像是可

以指向任何对象的Any类型,如下:

var v1 interface{} = 1       // 将int类型赋值给interface{}

var v2 interface{} = "abc"   // 将string类型赋值给interface{}

var v3 interface{} = &v2     // 将*interface{}类型赋值给interface{}

var v4 interface{} = struct{ X int }{1}

var v5 interface{} = &struct{ X int }{1}

当函数可以接受任意的对象实例时,我们会将其声明为interface{},最典型的例子是标

准库fmt中PrintXXX系列的函数,例如:

func Printf(fmt string, args ...interface{})

func Println(args ...interface{})

...

总体来说,interface{}类似于COM中的IUnknown,我们刚开始对其一无所知,但可以通

过接口查询和类型查询逐步了解它。

由于Go语言中任何对象实例都满足空接口interface{},所以interface{}看起来像是可

以指向任何对象的Any类型,如下:

var v1 interface{} = 1       // 将int类型赋值给interface{}

var v2 interface{} = "abc"   // 将string类型赋值给interface{}

var v3 interface{} = &v2     // 将*interface{}类型赋值给interface{}

var v4 interface{} = struct{ X int }{1}

var v5 interface{} = &struct{ X int }{1}

当函数可以接受任意的对象实例时,我们会将其声明为interface{},最典型的例子是标

准库fmt中PrintXXX系列的函数,例如:

func Printf(fmt string, args ...interface{})

func Println(args ...interface{})

...

总体来说,interface{}类似于COM中的IUnknown,我们刚开始对其一无所知,但可以通

过接口查询和类型查询逐步了解它。

时间: 2024-10-12 15:37:46

Go语言面组合式向对象编程基础总结的相关文章

Android 内功心法(番外)——写在设计模式前,面对对象编程基础

我写的一系列"Android 内功心法"着重讲到android中经常使用的设计模式.那么如果有些程序员刚刚接触设计模式,那就有必要确定一下自己面对对象编程的基础是否牢固了. 因为这直接关系到你阅读设计模式的速度和理解质量. 接下来我将简单介绍java中面对对象编程的一些基础知识. 1,类和修饰符 public class ClassTest{ public ClassTest{ } public void test(){ } } 其中类的定义是以"class"来决定

11.高阶函数(匿名/*递归/函数式)对象编程基础

高阶函数匿名函数匿名函数存在的情况:内置函数函数式编程递归函数式编程面向对象的程序设计类:实例:OOP类的名称空间/对象的名称空间 高阶函数 匿名函数 lambda x:x+y #return x+y 定义标志/参数(形式类似函数传参)/跟表达式(返回) 匿名函数存在的情况: 执行完这行之后,如果没有被赋值给别的变量 其引用计数为0,就会被内存垃圾回收机制清空 from functoolimport reduce sorted 倒序并且生成新列表/sort是仅仅倒序 map 映射 reduce

LINUX下C语言编程基础

实验二 Linux下C语言编程基础 一.实验目的 1. 熟悉Linux系统下的开发环境 2. 熟悉vi的基本操作 3. 熟悉gcc编译器的基本原理 4. 熟练使用gcc编译器的常用选项 5 .熟练使用gdb调试技术 6. 熟悉makefile基本原理及语法规范 7. 掌握静态库和动态库的生成 二.实验步骤 1. 快捷键 Ubuntu中: 2. vim VIM是一个非常好的文本编辑器,很多专业程序员使用VIM编辑代码,即使以后你不编写程序,只要跟文本打交道,都应该学学VIM,可以浏览参考一下普通人

C语言博客(5)编程基础之怎样理解八进制和十六进制?

在C语言的学习编程过程中,除了二进制,编程中也经常使用八进制和十六进制.这些也同样是编程基础的教学,要想成为C语言大神,这是一定要了解清楚的. 八进制有0~7共8个数字,基数为8,逢八进一,借一当八:十六进制中,用A来表示10,B表示11,C表示12,D表示13,E表示14,F表示15,因此有0~F共16个数字,基数为16,逢16进1,借1当16.例如: 八进制 3072 = 3×83 + 0×82 + 7×81 + 2×80 = 1536 + 0 + 56 + 2 = 1594 十六进制 E3

C语言博客(4)编程基础之怎样理解二进制思想以及数据的存储?

本次我要描述的编程基础的内容是怎样理解二进制思想以及数据的存储,这也是许多初学者需要的编程基础. 我们平时使用的数字都是由 0~9 共十个数字组成的,例如 1.9.10.297.952 等,一个数字最多能表示九,如果要表示十.十一.二十九.一百等,就需要多个数字组合起来. 例如表示 5+8 的结果,一个数字不够,只能"进位",用 13 来表示:这时"进一位"相当于十,"进两位"相当于二十. 因为逢十进一,也因为只有 0~9 共十个数字,所以叫做十

Java多线程编程基础之线程对象

在进入java平台的线程对象之前,基于基础篇(一)的一些问题,我先插入两个基本概念. [线程的并发与并行] 在单CPU系统中,系统调度在某一时刻只能让一个线程运行,虽然这种调试机制有多种形式(大多数是时间片轮巡为主),但无论如何,要通过不断切换需要运行的线程让其运行的方式就叫并发(concurrent).而在多CPU系统中,可以让两个以上的线程同时运行,这种可以同时让两个以上线程同时运行的方式叫做并行(parallel). 在上面包括以后的所有论述中,请各位朋友谅解,我无法用最准确的词语来定义储

C语言——第一部分 C语言概述以及编程基础

C语言 第一部分 编程基础 1.通过"语言"来控制计算机,让计算机为我们做事情,这样的语言就叫做编程语言(Programming Language). 2.编程语言也有固定的格式和词汇,例如:编程语言有很多种,常用的有C语言.C++.Java.C#.PHP.JavaScript等,每种语言都有自己擅长的方面,例如: l C语言和C++主要用于PC软件开发.底层开发.单片机和嵌入式系统: l Java和C#不但可以用来开发软件,还可以用来开发网站后台程序: l PHP主要用来开发网站后台

没有任何编程基础可以直接学习python语言吗?学会后能够做什么?

很明确的说 python非常适合没有任何编程基础的人入门.. 目前应用最多的:全栈开发.数据分析.运维开发,今天我们就以这三个重点的岗位来做一下自学Python的规划,希望你在学之前就能有结果的来走得更远. 很多人在问,python学了之后能做些什么? 既然你没有碰过 Python ,不知编程为何物的人,我就不提编程里面的项目名了--创一个小群,供大家学习交流聊天如果有对学python方面有什么疑惑问题的,或者有什么想说的想聊的大家可以一起交流学习一起进步呀.也希望大家对学python能够持之以

js面对对象编程

说到js,很大一部分人会说我很熟悉,在日常的web开发中经常用,那么你的js代码是符合面对对象思路的吗?那你会问我面向过程的js代码有什么不好吗?我的感受是面对对象的js编码更加简洁,减少了混乱,可维护行增强,适合编写富客户端时应用. 好了,首先看看js里如何定义对象: <html> <head> <script type="text/javascript"> var obj=new Object(); obj.name='josh'; obj.ag