golang如何在内嵌结构体中调用外层的成员, 类似于java父类this调用子类覆盖的方法?

1. Java的多态

我们先来看java面向对象的多态范例

abstract class Animal {
    public void doActions() {
        this.breathe();
        this.eat();
        this.sleep();
    }
    
    public void breathe() {
        System.out.println("Breathing Air!");
    }
    
    abstract public void eat();

    public void sleep() {
		System.out.println("Sleeping Zzz!");
    }

}

class Human extends Animal {
	@override
    public void eat() {
		System.out.println("Eating everthing!");
    }
  
}

class Cat extends Animal {
	@override
    public void eat() {
		System.out.println("Eating fish!");
    }
  
	@override
    public void sleep() {
		System.out.println("Sleeping rumble!");
    }
}

class Dog extends Animal {
	@override
    public void eat() {
		System.out.println("Eating bones!");
    }
  
	@override
    public void sleep() {
		System.out.println("Sleeping Woof!");
    }
}

class Application {
	public void main(String[] arg) {
        new Human().doActions();
		new Cat().doActions();
        new Dog().doActions();
	}
}

父类Animal成员方法doActions()调用了子类覆盖后的breathe(), eat(), sleep(). 如果子类未覆盖, 则仍会沿用父类的实现. 在写Animal代码时, 子类还没写出来, 也可能子类是另外的开发者书写的. 

这是面向对象多态里已经实现的. 但golang并不是面向对象的, 它没有完整的继承关系, 组合式内嵌是没有this指针, 也无法获取外层覆盖方法的.

2. golang无法调用多态

下面是golang结构体不能调用的原因分析

package main

import "fmt"

type Animal struct {
}

func (a *Animal) doActions() {
  a.breathe() // a参数无法获得子类覆盖
  a.eat() // 'a' cannot obtain function override
  a.sleep()
}
    
func (a *Animal) breathe() {
  fmt.Println("Breathing Air!")
}
    
func (a *Animal) void eat() {
  panic("unimplment")
}

func (a *Animal) sleep() {
  fmt.Println("Sleeping Zzz!")
}

type Human struct {
  Animal
}

//@override
func (a *Human) void eat() {
  fmt.Println("Eating everthing!")
}

func (a *Human) void learn() {
  fmt.Println("Learning knowledge!")
}

type Cat struct {
  Animal
}

//@override
func (c *Cat) eat() {
  fmt.println("Eating fish!")
}
  
//@override
func (c *Cat) sleep() {
  fmt.Println("Sleeping rumble!")
}

type Dog struct {
  Animal
}

//@override
func (d *Dog) eat() {
  fmt.Println("Eating bones!")
}
  
//@override
func (d *Dog) sleep() {
  fmt.Println("Sleeping Woof!")
}

func main() {
  human := &Human{}
  human.doActions()
  // see override, print "Eating everthing!"
  human.eat()
  
  &Cat{}.doActions()
  &Dog{}.doActions()
}



很多时候, 我们经常会遇到, 部分代码先编写调用流程, 这个时候甚至子类结构体还没定义出来.

当编写具体实现代码时, 还要求有部分代码重写.

这在面向对象里太常见了, 那么与之对应的功能golang要如何实现呢?

3. 实现方案

这边推荐两种方案: 建造器和基础函数

3.1. 建造器
package main

import "fmt"

type Animal struct {
}

func (a *Animal) doActions() {
  a.breathe() // a参数无法获得子类覆盖
  a.eat() // 'a' cannot obtain function override
  a.sleep()
}
    
func (a *Animal) breathe() *Animal {
  fmt.Println("Breathing Air!")
  return a
}
    
func (a *Animal) void eat() *Animal {
  panic("unimplment")
  return a
}

func (a *Animal) sleep() *Animal {
  fmt.Println("Sleeping Zzz!")
  return a
}

type Human struct {
  Animal
}

//@override
func (a *Human) void eat() *Human {
  fmt.Println("Eating everthing!")
  return a
}

func (a *Human) void learn() *Human {
  fmt.Println("Learning knowledge!")
  return a
}

type Cat struct {
  Animal
}

//@override
func (c *Cat) eat() *Cat {
  fmt.println("Eating fish!")
  return c
}
  
//@override
func (c *Cat) sleep() *Cat {
  fmt.Println("Sleeping rumble!")
  return c
}

type Dog struct {
  Animal
}

//@override
func (d *Dog) eat() *Dog {
  fmt.Println("Eating bones!")
  return d
}
  
//@override
func (d *Dog) sleep() *Dog {
  fmt.Println("Sleeping Woof!")
  return d
}

func main() {
  human := &Human{}
  human.doActions()
  // see override, print "Eating everthing!"
  human.eat()
  
  &AnimalBuilder{&Cat{}}.doActions().End()
  &AnimalBuilder{&Dog{}}.breathe().eat().sleep().End() // 链式调用
}

type AnimalBudiler struct {
  AnimalInterface
}

func (builder *AnimalBudiler) doActions() {
  builder.breathe() // 获得子类覆盖
  builder.eat()
  builder.sleep()
}

func (builder *AnimalBudiler) End() {
  
}

type AnimalInterface interface {
  breathe()
  eat()
  sleep()
}

实现原理: 利用嵌套结构体只有外部(结构体之外)代码才产生多态(有覆盖用覆盖函数, 没覆盖用原函数)的特性, 使用AnimalBuidler构造器来组织, 内部代码的调用. 也就是说把"父类"的doActions()写在了Builder里. 凡是跨父子结构体的都要抽到Builder里.

优点是对父类, 子类分别编写没有影响, 流程清晰;

缺点是相较于原先单个父类函数调用而言, 调用处的改动比较大, 而且要不断的维护AnimalInterface接口以保证创造器内部可访问子类覆盖的方法.

建造器比较适合大型流程的调用与覆盖.

3.2. 基础函数
package main

import "fmt"

type Animal struct {
}

func (a *Animal) doActions() {
  a.breathe() // a参数无法获得子类覆盖
  a.eat() // 'a' cannot obtain function override
  a.sleep()
}

func (a *Animal) doActionsBase(food string, sound string) {
  a.breathe()
  a.eatBase(food)
  a.sleep(sound)
}
    
func (a *Animal) breathe() {
  fmt.Println("Breathing Air!")
}
    
func (a *Animal) void eat() {
  panic("unimplment")
}

func (a *Animal) void eatBase(food string) {
	fmt.Println("Eating "+food+"!")
}

func (a *Animal) sleep() {
  a.sleepBase("Zzz")
}

func (a *Animal) sleepBase(sound string) {
  fmt.Println("Sleeping "+sound+"!")
}

type Human struct {
  Animal
}

//@override
func (a *Human) void eat() {
  a.eatBase("everthing")
}

func (a *Human) void learn() {
  fmt.Println("Learning knowledge!")
}

type Cat struct {
  Animal
}

//@override
func (c *Cat) eat() {
  c.eatBase("fish")
}
  
//@override
func (c *Cat) sleep() {
  c.sleepBase("rumble")
}

type Dog struct {
  Animal
}

//@override
func (d *Dog) eat() {
  d.eatBase("bones")
}
  
//@override
func (d *Dog) sleep() {
  d.sleepBase("Woof")
}

func main() {
  human := &Human{}
  human.doActions()
  // see override, print "Eating everthing!"
  human.eat()
  
  &Cat{}.doActions()
  &Dog{}.doActions()
}

实现原理: 将父类需要被子类覆盖的函数额外多建立出一个基础函数, 将影响到实现差异的部分用参数传入, 如范例中xxxBase格式命名的函数. 然后让父类和子类传入不同的参数来得到不同的处理逻辑, 也就是说实际代码是由基础函数完成的, 原函数只是组装不同的参数值.

一般来说原函数提供对外访问, 基础函数可以通过对外接口减裁掉.

注意: 基础函数的参数应当是数值变量, 不应是函数变量. 传递函数会增加复杂度, 容易造成地狱回调, 而且这也违反了数据驱动设计原则.

优点: 最接近于面向对象的代码结构, 可读性较高, 改动量最少, 后续维护成本低;

缺点: 额外多出许多函数, 另外需要将原函数抽离出变量控制执行代码;

4. 不推荐侵入性属性

type AnimalInterface interface {
  breathe()
  eat()
  sleep()
}


type Animal struct {
// 成员变量-记录子类实例
  this AnimalInterface
// 函数变量-记录子类实现函数
  eat func()
  sleep func()
}

func (a *Animal) doActions() {
  a.this.breathe()
  a.this.eat()
  a.this.sleep()
}

type Human struct {
  Animal
}

type Cat struct {
  Animal
}

type Dog struct {
  Animal
}

/// 调用时
func main() {
  human := &Human{}
// 显然这不是个良好的代码--
  human.Animal.this = human
  human.Animal.eat = human.eat
  human.Animal.sleep = human.sleep
}

我们看到额外的成员函数变量, 或者子类的成员接口, 除了占用了结构体的空间, 还增加了初始化负担.

把内层结构体把带外层结构体当作成员来使用, 这个写法读代码上就是病句.

那么写得难看,代码量就能减少了吗?实际上并没有.

代码并不是一锤子买卖, 功能会不断增加, 当功能扩展时除了实现函数要改, 成员函数变量也要增加, 同时初始化赋值还不能少, 那就是全都要改.

题外话, java底层实现确实也是使用this函数指针保持当前实例 (类似于范例中的AnimalInterface变量), 但由编译器自动完成, 无需人工编写.

换言之, 如果`侵入性属性`是自动同步修改的行为, 代码不需要开发者维护的情况下, 也是一个很好的实现方案. 或许使用`go generate`能做得到也不一定.

  • 23
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值