Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

把大象塞进冰箱,共分几步? #200

Open
bingoohuang opened this issue May 25, 2021 · 3 comments
Open

把大象塞进冰箱,共分几步? #200

bingoohuang opened this issue May 25, 2021 · 3 comments

Comments

@bingoohuang
Copy link
Owner

bingoohuang commented May 25, 2021

把大象塞进冰箱的OO实现

  • 问:把大象塞进冰箱,共分几步?
  • 答:分三步,第一步,打开冰箱门,第二步,把大象塞进去,第三步,关上冰箱门。对于经常忘记关门的人来说,在关门环节,还需要检查一下,是否真的关好了。

JAVA实现

  1. 定义冰箱接口
  2. 定义普通冰箱类,实现接口
  3. 定义密码冰箱类,继承普通冰箱类,重载PutInside方法和Check方法,因为这两个方法,需要验证密码
  4. 创建密码冰箱类,开门、塞大象、关门,一气呵成,输出与预期相符
public class Elephant {
    public static void main(String[] args) {
        PinRefrigerator r = new PinRefrigerator();
        r.Open();      //  Normal Open
        r.PutInside(); //  Pin PutInside,
        r.Close();     // Normal Close, Pin Check
    }

    public interface Refrigerator {
        void Open();
        void PutInside();
        void Close();
        void Check();
    }
    public static class NormalRefrigerator implements Refrigerator {
        public void Open() { System.out.println("Normal Open"); }
        public void PutInside() { System.out.println("Normal PutInside"); }
        public void Close() { System.out.println("Normal Close");this.Check(); }
        public void Check() { System.out.println("Normal Check"); }
    }
    public static class PinRefrigerator extends NormalRefrigerator {
        public void PutInside() { System.out.println("Pin PutInside"); }
        public void Check() { System.out.println("Pin Check"); }
    }
}

GO实现

刘金良同学,按照Java的OO思路,迅速上手了一般GO实现,可是实际输出与预期不相符了,主要是密码冰箱重载的Check方法,竟然没有被调用到。

package main

import "fmt"

func main() {
	r := PinRefrigerator{}
	r.Open()      // Normal Open
	r.PutInside() // Pin PutInside
	r.Close()     // Normal Close, Normal Check
}

type Refrigerator interface {
	Open()
	PutInside()
	Close()
	Check()
}

type NormalRefrigerator struct{}

func (NormalRefrigerator) Open()      { fmt.Println("Normal Open") }
func (NormalRefrigerator) PutInside() { fmt.Println("Normal PutInside") }
func (n NormalRefrigerator) Close()   { fmt.Println("Normal Close"); n.Check() }
func (NormalRefrigerator) Check()     { fmt.Println("Normal Check") }

type PinRefrigerator struct {
	NormalRefrigerator
}

func (PinRefrigerator) PutInside() { fmt.Println("Pin PutInside") }
func (PinRefrigerator) Check()     { fmt.Println("Pin Check") }

改一版,依然带着JAVA的印迹,区别如下:

  1. 大接口拆分成小接口 (践行一下 SRP )
  2. 组合,组合,组合 (践行一下 组合优于继承)
  3. 接收器改成指针类型

这下可以正常输出了

package main

import "fmt"

type Opener interface{ Open() }
type Closer interface{ Close() }
type Checker interface{ Check() }

type NormalRefrigerator struct {
	Checker
}

func (*NormalRefrigerator) Open()      { fmt.Println("Normal Open") }
func (*NormalRefrigerator) PutInside() { fmt.Println("Normal PutInside") }
func (n *NormalRefrigerator) Close()   { fmt.Println("Normal Close"); n.Check() }

type PinRefrigerator struct {
	Opener
	Closer
}

func (*PinRefrigerator) PutInside() { fmt.Println("Pin PutInside") }
func (*PinRefrigerator) Check()     { fmt.Println("Pin Check") }

func main() {
	n := &NormalRefrigerator{}
	r := &PinRefrigerator{Opener: n, Closer: n}
	n.Checker = r
	r.Open()      // Normal Open
	r.PutInside() // Pin PutInside
	r.Close()     // Normal Close, Pin Check
}

再改第3版,更加gopher style了,对比JAVA,是不是感觉:

  1. "碎"了一地
  2. 但每一个都好简单啊
  3. 组合,就是搭积木,比继承好玩多了不是,各种小部件更加灵活了
package main

import "fmt"

type Opener interface{ Open() }
type Closer interface{ Close() }
type Checker interface{ Check() }

type NormalOpener struct{}
type NormalCloser struct{}
type NormalPutInsider struct{}

func (*NormalOpener) Open()                   { fmt.Println("Normal Open") }
func (*NormalPutInsider) PutInside()          { fmt.Println("Normal PutInside") }
func (n *NormalCloser) Close(checker Checker) { fmt.Println("Normal Close"); checker.Check() }

type PinPutInsider struct{}
type PinChecker struct{}

func (*PinPutInsider) PutInside() { fmt.Println("Pin PutInside") }
func (*PinChecker) Check()        { fmt.Println("Pin Check") }

func main() {
	opener := NormalOpener{}
	PutInsider := PinPutInsider{}
	closer := NormalCloser{}
	checker := PinChecker{}

	opener.Open()          // Normal Open
	PutInsider.PutInside() // Pin PutInside
	closer.Close(&checker) // Normal Close, Pin Check
}
@CharLemAznable
Copy link

是不是可以不用写那么多struct, 直接把"behave"作为func来写呢?

@CharLemAznable
Copy link

package main

import (
    "fmt"
)

type Open func()
type PutInside func()
type Close func(check Check)
type Check func()

var NormalOpen Open = func() { fmt.Println("Normal Open") }
var NormalPutInside PutInside = func() { fmt.Println("Normal PutInside") }
var NormalClose Close = func(check Check) { fmt.Println("Normal Close"); if nil != check { check() } }
var NormalCheck Check = func() { fmt.Println("Normal Check") }

var PinPutInside PutInside = func() { fmt.Println("Pin PutInside") }
var PinCheck Check = func() { fmt.Println("Pin Check") }

func main() {
    var open = NormalOpen
    var putInside = PinPutInside
    var close = NormalClose
    var check = PinCheck

    open()
    putInside()
    close(check)
}

@bingoohuang
Copy link
Owner Author

bingoohuang commented May 26, 2021

是不是可以不用写那么多struct, 直接把"behave"作为func来写呢?

理论上完全可以的啦。所以经常有这种 func 与 interface 互换的模式:

// Open 定义了一个打开的接口.
type Opener interface{ Open() }
// OpenHandler 定义了一个打开的行为.
type OpenHandler func()
// Open 让 OpenHandler 隐式实现了 Opener 接口.
func (o OpenHandler) Open() {o()}

// 1. 当你需要 Opener 接口,但是只有 OpenHandler 的时候,你可以直接用,因为 OpenHandler 已经实现了 Opener 接口,最多套一层 OpenHandler(yourFn)
// 2. 当你需要 OpenHandler 函数,但是只有 Opener 对象的时候,你可以直接使用 opener.Open 

但是使用 interface 的方式,有个好处,那就是 IDE (如 goland ),可以 Go to Implementations, 或者 Go to Method Specifications,阅读代码,非常方便

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants