blog
blog copied to clipboard
把大象塞进冰箱,共分几步?
把大象塞进冰箱的OO实现
- 问:把大象塞进冰箱,共分几步?
- 答:分三步,第一步,打开冰箱门,第二步,把大象塞进去,第三步,关上冰箱门。对于经常忘记关门的人来说,在关门环节,还需要检查一下,是否真的关好了。
JAVA实现
- 定义冰箱接口
- 定义普通冰箱类,实现接口
- 定义密码冰箱类,继承普通冰箱类,重载PutInside方法和Check方法,因为这两个方法,需要验证密码
- 创建密码冰箱类,开门、塞大象、关门,一气呵成,输出与预期相符
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的印迹,区别如下:
- 大接口拆分成小接口 (践行一下 SRP )
- 组合,组合,组合 (践行一下 组合优于继承)
- 接收器改成指针类型
这下可以正常输出了
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,是不是感觉:
- "碎"了一地
- 但每一个都好简单啊
- 组合,就是搭积木,比继承好玩多了不是,各种小部件更加灵活了
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
}
是不是可以不用写那么多struct, 直接把"behave"作为func来写呢?
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)
}
是不是可以不用写那么多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,阅读代码,非常方便