单一职责
在软件组件的设计中,如果责任划分的不清晰,使用继承得到的结果往往是随着需求的变化,子类急剧膨胀,同时充斥着重复代码,这时候的关键是划清责任。
装饰模式
动态(组合)地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类(继承)更为灵活(消除重复代码 & 减少子类个数)。
动机
- 在某些情况下我们可能会“过度地使用继承来扩展对象的功能”,由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀。
- 如何使“对象功能的扩展”能够根据需要来动态地实现?同时避免“扩展功能的增多”带来的子类膨胀问题?从而使得任何“功能扩展变化”所导致的影响将为最低?
问题
例如,有一个流操作,我们抽出它的通用方法,然后在继承并实现这些方法,
package decorate
import "fmt"
type Stream interface {
Read(nums int) byte
Seek(position int)
Write(data byte)
}
// 文件流
type FileStream struct {
Stream
}
func (fs *FileStream) Read(nums int) byte {
fmt.Println("File stream read")
return 0
}
func (fs *FileStream) Seek(position int) {
fmt.Println("File stream seek")
}
func (fs *FileStream) Write(data byte) {
fmt.Println("File stream write")
}
// 网络流
type NetworkStream struct {
Stream
}
// 具体实现
func (ns *NetworkStream) Read(nums int) byte {
fmt.Println("Network stream read")
return 0
}
func (ns *NetworkStream) Seek(position int) {
fmt.Println("Network stream seek")
}
func (ns *NetworkStream) Write(data byte) {
fmt.Println("Network stream write")
}
// 内存流
type MemoryStream struct {
Stream
}
// 具体实现
func (ms *MemoryStream) Read(nums int) byte {
fmt.Println("Memory stream read")
return 0
}
func (ms *MemoryStream) Seek(position int) {
fmt.Println("Memory stream seek")
}
func (ms *MemoryStream) Write(data byte) {
fmt.Println("Memory stream write")
}
然后我们需要对流进行加密操作,但是加密操作需要对于具体的某个流进行操作,于是有以下代码
// 扩展操作,对文件流加密
type CryptoFileStream struct {
fs *FileStream
}
// 具体实现
func (cfs *CryptoFileStream) Read(nums int) byte {
//额外操作
fmt.Println("Encryption Before Read ")
res := cfs.fs.Read(nums)
//额外操作
fmt.Println("Encryption After Read ", res)
return res
}
func (cfs *CryptoFileStream) Seek(position int) {
fmt.Println("Encryption Before Seek")
cfs.fs.Seek(position)
fmt.Println("Encryption After Seek")
}
func (cfs *CryptoFileStream) Write(data byte) {
fmt.Println("Encryption Before Write")
cfs.fs.Write(data)
fmt.Println("Encryption After Write")
}
然后我们发现,不管对什么流操作,但加密流的方法处理流程是不变的。可能还需要,Buffer
扩展。代码重复度过高。每次都继承,可以看到有这么多类
可见这类的增长深度是有多块,1+n+n*m!/2
。对于变化部分的封装,文件、网络、内存流实现不变,对于扩展功能封装.
package decorate
import "fmt"
type Stream interface {
Read(nums int) byte
Seek(position int)
Write(data byte)
}
// 文件流
type FileStream struct {
Stream
}
func (fs *FileStream) Read(nums int) byte {
fmt.Println("File stream read")
return 0
}
func (fs *FileStream) Seek(position int) {
fmt.Println("File stream seek")
}
func (fs *FileStream) Write(data byte) {
fmt.Println("File stream write")
}
// 网络流
type NetworkStream struct {
Stream
}
// 具体实现
func (ns *NetworkStream) Read(nums int) byte {
fmt.Println("Network stream read")
return 0
}
func (ns *NetworkStream) Seek(position int) {
fmt.Println("Network stream seek")
}
func (ns *NetworkStream) Write(data byte) {
fmt.Println("Network stream write")
}
// 内存流
type MemoryStream struct {
Stream
}
// 具体实现
func (ms *MemoryStream) Read(nums int) byte {
fmt.Println("Memory stream read")
return 0
}
func (ms *MemoryStream) Seek(position int) {
fmt.Println("Memory stream seek")
}
func (ms *MemoryStream) Write(data byte) {
fmt.Println("Memory stream write")
}
// 扩展操作,对流加密
type CryptoStream struct {
Stream //继承,相当于必须实现Stream接口
stream Stream //流指针,运行时确定
}
// 构造器
func NewCryptoStream(stream Stream) *CryptoStream {
return &CryptoStream{stream: stream}
}
// 具体实现
func (cs *CryptoStream) Read(nums int) byte {
fmt.Println("Crypto stream before read")
cs.stream.Read(nums)
fmt.Println("Crypto stream after read")
return 0
}
func (cs *CryptoStream) Seek(position int) {
fmt.Println("Crypto stream before seek")
cs.stream.Seek(position)
fmt.Println("Crypto stream after seek")
}
func (cs *CryptoStream) Write(data byte) {
fmt.Println("Crypto stream before write")
cs.stream.Write(data)
fmt.Println("Crypto stream after write")
}
// 扩展操作,buffered
type BufferedStream struct {
Stream //继承,相当于必须实现Stream接口
stream Stream //流指针,运行时确定
}
// 构造器
func NewBufferedStream(stream Stream) *BufferedStream {
return &BufferedStream{stream: stream}
}
func (bs *BufferedStream) Read(nums int) byte {
fmt.Println("Buffered stream before read")
bs.stream.Read(nums)
fmt.Println("Buffered stream after read")
return 0
}
func (bs *BufferedStream) Seek(position int) {
fmt.Println("Buffered stream before seek")
bs.stream.Seek(position)
fmt.Println("Buffered stream after seek")
}
func (bs *BufferedStream) Write(data byte) {
fmt.Println("Buffered stream before write")
bs.stream.Write(data)
fmt.Println("Buffered stream after write")
}
// 扩展操作,CryptBuffered
type CryptBufferedStream struct {
Stream //继承,相当于必须实现Stream接口
stream Stream //流指针,运行时确定
}
// 构造器
func NewCryptBufferedStream(stream Stream) *CryptBufferedStream {
return &CryptBufferedStream{stream: stream}
}
// 具体实现
func (cbs *CryptBufferedStream) Read(nums int) byte {
fmt.Println("CryptBuffered stream before read")
cbs.stream.Read(nums)
fmt.Println("CryptBuffered stream after read")
return 0
}
func (cbs *CryptBufferedStream) Seek(position int) {
fmt.Println("CryptBuffered stream before seek")
cbs.stream.Seek(position)
fmt.Println("CryptBuffered stream after seek")
}
func (cbs *CryptBufferedStream) Write(data byte) {
fmt.Println("CryptBuffered stream before write")
cbs.stream.Write(data)
fmt.Println("CryptBuffered stream after write")
}
图不一定标准,具体使用,可以通过组合实现想要的功能
func TestCryptoBufferedFileStream_Read(t *testing.T) {
fs := &FileStream{}
cryptoStream := NewCryptoStream(fs)
bufferedStream := NewBufferedStream(cryptoStream)
bufferedStream.Read(10)
}
由于每个具体实现类里都有Stream
字段,所以进一步抽象。提取出Decorate
类,再由具体扩展去继承该类。
package decorate
import "fmt"
type Stream interface {
Read(nums int) byte
Seek(position int)
Write(data byte)
}
// 文件流
type FileStream struct {
Stream
}
func (fs *FileStream) Read(nums int) byte {
fmt.Println("File stream read")
return 0
}
func (fs *FileStream) Seek(position int) {
fmt.Println("File stream seek")
}
func (fs *FileStream) Write(data byte) {
fmt.Println("File stream write")
}
// 网络流
type NetworkStream struct {
Stream
}
// 具体实现
func (ns *NetworkStream) Read(nums int) byte {
fmt.Println("Network stream read")
return 0
}
func (ns *NetworkStream) Seek(position int) {
fmt.Println("Network stream seek")
}
func (ns *NetworkStream) Write(data byte) {
fmt.Println("Network stream write")
}
// 内存流
type MemoryStream struct {
Stream
}
// 具体实现
func (ms *MemoryStream) Read(nums int) byte {
fmt.Println("Memory stream read")
return 0
}
func (ms *MemoryStream) Seek(position int) {
fmt.Println("Memory stream seek")
}
func (ms *MemoryStream) Write(data byte) {
fmt.Println("Memory stream write")
}
// 抽象出Stream的装饰器
type DecorateStream struct {
Stream //继承,必须实现Stream接口,当然可以交给子类实现
}
// 构造器
func NewDecorateStream(stream Stream) DecorateStream {
return DecorateStream{
Stream: stream,
}
}
// 扩展操作,对流加密
type CryptoStream struct {
DecorateStream //继承,
}
// 构造器
func NewCryptoStream(stream Stream) *CryptoStream {
return &CryptoStream{
DecorateStream: NewDecorateStream(stream),
}
}
// 具体实现
func (cs *CryptoStream) Read(nums int) byte {
fmt.Println("Crypto stream before read")
cs.DecorateStream.Read(nums)
fmt.Println("Crypto stream after read")
return 0
}
func (cs *CryptoStream) Seek(position int) {
fmt.Println("Crypto stream before seek")
cs.DecorateStream.Seek(position)
fmt.Println("Crypto stream after seek")
}
func (cs *CryptoStream) Write(data byte) {
fmt.Println("Crypto stream before write")
cs.DecorateStream.Write(data)
fmt.Println("Crypto stream after write")
}
// 扩展操作,buffered
type BufferedStream struct {
DecorateStream //继承,
}
// 构造器
func NewBufferedStream(stream Stream) *BufferedStream {
return &BufferedStream{DecorateStream: NewDecorateStream(stream)}
}
func (bs *BufferedStream) Read(nums int) byte {
fmt.Println("Buffered stream before read")
bs.DecorateStream.Read(nums)
fmt.Println("Buffered stream after read")
return 0
}
func (bs *BufferedStream) Seek(position int) {
fmt.Println("Buffered stream before seek")
bs.DecorateStream.Seek(position)
fmt.Println("Buffered stream after seek")
}
func (bs *BufferedStream) Write(data byte) {
fmt.Println("Buffered stream before write")
bs.DecorateStream.Write(data)
fmt.Println("Buffered stream after write")
}
使用
func TestCryptoBufferedFileStream_Read(t *testing.T) {
fs := &FileStream{}
cryptoStream := NewCryptoStream(fs)
bufferedStream := NewBufferedStream(cryptoStream)
var stream Stream
stream = bufferedStream
stream.Read(10)
}
类图
这样类的个数是1+n+m
.
总结
组合由于继承!
类图,同时想要继承和组合同一个对象,大概率是装饰模式。
- 通过采用组合而非继承的手法, Decorator模式实现了在运行时动态扩展对象功能的能力,而且可以根据需要扩展多个功能。避免了使用继承带来的“灵活性差”和“多子类衍生问题”。
- Decorator类在接口上表现为is-a Component的继承关系,即Decorator类继承了Component类所具有的接口。但在实现上又表现为has-a Component的组合关系,即Decorator类又使用了另外一个Component类。
- Decorator模式的目的并非解决“多子类衍生的多继承”问题,Decorator模式应用的要点在于解决“主体类在多个方向上的扩展功能”——是为“装饰”的含义。
桥模式
将抽象部分(业务功能)与实现部分(平台实现)分离,使它们都可以独立地变化。
动机:
- 由于某些类型的固有的实现逻辑,使得它们具有两个变化的维度,乃至多个纬度的变化。
- 如何应对这种“多维度的变化”?如何利用面向对象技术来使得类型可以轻松地沿着两个乃至多个方向变化,而不引入额外的复杂度?
实现
Message
抽象实现
type Messager interface {
Login(username string, password string)
SendMessage(message string)
SendPicture(image image.Image)
PlaySound()
DrawShape()
WriteText()
Connect()
}
对于PC平台
// 平台实现
type PCMessagerBase struct {
Messager
}
func (messager *PCMessagerBase) PlaySound() {
//PC ...
}
func (messager *PCMessagerBase) DrawShape() {
//PC ...
}
func (messager *PCMessagerBase) WriteText() {
//PC ...
}
func (messager *PCMessagerBase) Connect() {
//PC ...
}
对于Mobile平台
type MobileMessagerBase struct {
Messager
}
func (messager *MobileMessagerBase) PlaySound() {
//Mobile ...
}
func (messager *MobileMessagerBase) DrawShape() {
//Mobile ...
}
func (messager *MobileMessagerBase) WriteText() {
//Mobile ...
}
func (messager *MobileMessagerBase) Connect() {
//Mobile ...
}
他们的主要区别在于,
PlaySound()
DrawShape()
WriteText()
Connect()
这些方法的实现上有所区别。
接下来是业务的抽象实现,精简版业务
// 业务抽象
type PCMessagerLite struct {
PCMessagerBase
}
func (pc *PCMessagerLite) Login(username string, password string) {
pc.Connect()
//...
}
func (pc *PCMessagerLite) SendMessage(message string) {
pc.WriteText()
}
func (pc *PCMessagerLite) SendPicture(image image.Image) {
pc.DrawShape()
}
复杂业务
type PCMessagerPerfect struct {
PCMessagerBase
}
func (pc *PCMessagerPerfect) Login(username string, password string) {
pc.PlaySound()
//...
pc.Connect()
//...
}
func (pc *PCMessagerPerfect) SendMessage(message string) {
pc.PlaySound()
//...
pc.WriteText()
//...
}
func (pc *PCMessagerPerfect) SendPicture(image image.Image) {
pc.PlaySound()
//...
pc.DrawShape()
//..
}
对于移动端也有类似的实现。
如果要使用,
var messager Messager
messager = &PCMessagerPerfect{}
类图
这样的话,假设业务抽象有m个,类的数目就有:1+n+m*n
个,n种平台
问题:
- 重复度太高,且是结构型的重复
解决:
- 发现规律,即继承转组合
初步抽象,继承转指针
// 业务抽象
type PCMessagerLite struct {
messager *PCMessagerBase
}
func (pc *PCMessagerLite) Login(username string, password string) {
pc.messager.Connect()
//...
}
func (pc *PCMessagerLite) SendMessage(message string) {
pc.messager.WriteText()
}
func (pc *PCMessagerLite) SendPicture(image image.Image) {
pc.messager.DrawShape()
}
type PCMessagerPerfect struct {
messager *PCMessagerBase
}
func (pc *PCMessagerPerfect) Login(username string, password string) {
pc.messager.PlaySound()
//...
pc.messager.Connect()
//...
}
func (pc *PCMessagerPerfect) SendMessage(message string) {
pc.messager.PlaySound()
//...
pc.messager.WriteText()
//...
}
func (pc *PCMessagerPerfect) SendPicture(image image.Image) {
pc.messager.PlaySound()
//...
pc.messager.DrawShape()
//..
}
可用发现这个指针可用进一步抽象,使得其既可以是Mobile
,也可以是PC
package bridge
import "image"
type Messager interface {
Login(username string, password string)
SendMessage(message string)
SendPicture(image image.Image)
PlaySound()
DrawShape()
WriteText()
Connect()
}
// 平台实现
type PCMessagerBase struct {
Messager
}
func (messager *PCMessagerBase) PlaySound() {
//PC ...
}
func (messager *PCMessagerBase) DrawShape() {
//PC ...
}
func (messager *PCMessagerBase) WriteText() {
//PC ...
}
func (messager *PCMessagerBase) Connect() {
//PC ...
}
type MobileMessagerBase struct {
Messager
}
func (messager *MobileMessagerBase) PlaySound() {
//Mobile ...
}
func (messager *MobileMessagerBase) DrawShape() {
//Mobile ...
}
func (messager *MobileMessagerBase) WriteText() {
//Mobile ...
}
func (messager *MobileMessagerBase) Connect() {
//Mobile ...
}
// 业务抽象
type MessagerLite struct {
messager Messager //既可以是PC又可用是Mobile
}
func (lite *MessagerLite) Login(username string, password string) {
lite.messager.Connect()
//...
}
func (lite *MessagerLite) SendMessage(message string) {
lite.messager.WriteText()
}
func (lite *MessagerLite) SendPicture(image image.Image) {
lite.messager.DrawShape()
}
type MessagerPerfect struct {
messager Messager
}
func (perfect *MessagerPerfect) Login(username string, password string) {
perfect.messager.PlaySound()
//...
perfect.messager.Connect()
//...
}
func (perfect *MessagerPerfect) SendMessage(message string) {
perfect.messager.PlaySound()
//...
perfect.messager.WriteText()
//...
}
func (perfect *MessagerPerfect) SendPicture(image image.Image) {
perfect.messager.PlaySound()
//...
perfect.messager.DrawShape()
//..
}
进一步发现,
type Messager interface {
Login(username string, password string)
SendMessage(message string)
SendPicture(image image.Image)
PlaySound()
DrawShape()
WriteText()
Connect()
}
是不合理的,拆分为两个部分
type Messager interface {
Login(username string, password string)
SendMessage(message string)
SendPicture(image image.Image)
}
type MessagerImp interface {
PlaySound()
DrawShape()
WriteText()
Connect()
}
其中Messager
是被继承的,MessagerImpl
是指针被调用的,(由于MessagerImpl
在类里又是重复的,应该往父类里提,Go有点不好实现,暂时没提)。
package bridge
import (
"fmt"
"image"
)
type Messenger interface {
Login(username string, password string)
SendMessage(message string)
SendPicture(image image.Image)
}
type MessageImp interface {
PlaySound()
DrawShape()
WriteText()
Connect()
}
// 平台实现
type PCMessagerImplementation struct {
MessageImp
}
func (messager *PCMessagerImplementation) PlaySound() {
//PC ...
fmt.Println("PCMessagerImplementation PlaySound")
}
func (messager *PCMessagerImplementation) DrawShape() {
//PC ...
fmt.Println("PCMessagerImplementation DrawShape")
}
func (messager *PCMessagerImplementation) WriteText() {
//PC ...
fmt.Println("PCMessagerImplementation WriteText")
}
func (messager *PCMessagerImplementation) Connect() {
//PC ...
fmt.Println("PCMessagerImplementation Connect")
}
type MobileMessagerImplementation struct {
MessageImp
}
func (messager *MobileMessagerImplementation) PlaySound() {
//Mobile ...
fmt.Println("MobileMessagerImplementation PlaySound")
}
func (messager *MobileMessagerImplementation) DrawShape() {
//Mobile ...
fmt.Println("MobileMessagerImplementation DrawShape")
}
func (messager *MobileMessagerImplementation) WriteText() {
//Mobile ...
fmt.Println("MobileMessagerImplementation WriteText")
}
func (messager *MobileMessagerImplementation) Connect() {
//Mobile ...
fmt.Println("MobileMessagerImplementation Connect")
}
// 业务抽象
type MessagerLite struct {
Messenger //继承
impl MessageImp //实现指针
}
func (lite *MessagerLite) Login(username string, password string) {
lite.impl.Connect()
//...
}
func (lite *MessagerLite) SendMessage(message string) {
lite.impl.WriteText()
}
func (lite *MessagerLite) SendPicture(image image.Image) {
lite.impl.DrawShape()
}
type MessagerPerfect struct {
Messenger //继承
impl MessageImp //实现指针
}
func (perfect *MessagerPerfect) Login(username string, password string) {
perfect.impl.PlaySound()
//...
perfect.impl.Connect()
//...
}
func (perfect *MessagerPerfect) SendMessage(message string) {
perfect.impl.PlaySound()
//...
perfect.impl.WriteText()
//...
}
func (perfect *MessagerPerfect) SendPicture(image image.Image) {
perfect.impl.PlaySound()
//...
perfect.impl.DrawShape()
//..
}
此时类的个数变为1+m+n
,但实现的功能是m*n
测试
package bridge
import "testing"
func TestMessagerImplPrefect(t *testing.T) {
//运行时装配,组合思想
//实现类
var impl MessageImp
impl = &PCMessagerImplementation{}
//实例化
var messager Messenger
messager = &MessagerPerfect{impl: impl}
messager.Login("Hello", "World")
impl = &MobileMessagerImplementation{}
messager = &MessagerLite{impl: impl}
messager.Login("Mobile", "123456")
}
最终可用通过Messager
提供统一的调用方法,而具体的实现根据MessagerImp
抽象了出来,这样实现了m*n
的功能。主要是因为平台
的扩展,和业务
的扩展,应该分开。
总结
- Bridge模式使用“对象间的组合关系”解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。所谓抽象和实现沿着各自纬度的变化,即“子类化”它们。
- Bridge模式有时候类似于多继承方案,但是多继承方案往往违背单一职责原则(即一个类只有一个变化的原因),复用性比较差。Bridge模式是比多继承方案更好的解决方法。
- Bridge模式的应用一般在“两个非常强的变化维度”,有时一个类也有多于两个的变化维度,这时可以使用Bridge的扩展模式。