设计模式 -- 桥接模式

2023-11-22 09:24:58 浏览数 (1)

场景

某公司准备开发一个跨平台图像浏览系统,要求可以显示JPG、PNG等多种格式图片,并且能够在Windows、Linux等多个操作系统上运行

示例类图

使用了一种多层继承结构,Image是抽象父类,而JPGImage、PNGImage等作为其直接子类,由于每一种图像在不同的操作系统的屏幕上显示有所差异,因此需要为不同的图像类再提供一组在不同操作系统显示的子类,JPGImage提供子类JPGWindows、JPGLinux,分别用于在Windows、Linux不同的操作系统下显示图像

问题来了 由于采用多层继承结构,系统扩展非常麻烦,无论是增加新的图像格式还是增加新的操作系统,都需要增加大量的具体类,这将导致系统变得非常庞大,增加运行和维护开销

问题改进 将图像格式与操作系统两个维度分离,使得它们可以独立变化,增加新的图像文件格式或者操作系统时都对另一个维度不造成任何影响

示例类图(改进)

表述 (结构型模式)

将抽象部分与它的实现部分分离,使它们都可以独立地变化

如果软件系统中某个类存在俩个或多个维度变化,通过该模式可以将这俩个维度分离出来,使两者可以独立发展,与多继承方案不同,它将俩个独立变化的维度设计为俩个独立的继承等级结构,并且在抽象层建立一个抽象关联

桥接模式类图

桥接模式类图

  • Abstraction(抽象类):定义中所说的抽象部分,通常在这个对象里面,要维护一个实现部分(Implementor)的对象引用,在抽象对象里面的方法,需要调用实现部分的对象来完成。这个对象里面的方法,通常都是跟具体的业务相关的方法
  • RefinedAbstraction(具体类):它实现了在Abstraction中声明的抽象业务方法,在RefinedAbstraction中可以调用在Implementor中定义的业务方法
  • Implementor(实现类接口):定义实现类的接口,这个接口不一定要与Abstraction的接口完全一致,通常Implementor接口提供基本操作,而Abstraction定义的接口可能会做更多更复杂的操作,Implementor接口对这些基本操作进行了声明,而具体实现交给其子类
  • ConcreteImplementor(具体实现类):具体实现Implementor接口,在不同的ConcreteImplementor中提供基本操作的不同实现,在程序运行时,ConcreteImplementor对象将替换其父类对象,提供给抽象类具体的业务操作方法

优点

  • 桥接模式使用聚合关系,解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化
  • 提高了系统的可扩展性,可以独立地对抽象部分和实现部分进行扩展
  • 桥接模式可以取代多层继承方案,可减少子类的个数

缺点

  • 桥接模式的引入会增加系统的理解与设计难度,由于聚合关系建立在抽象层,要求开发者针对抽象进行设计与编程
  • 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限

使用场景

  • 一个类存在两个独立变化的维度,且这两个维度都需要进行扩展
  • 如果一个系统需要在抽象化和具体化之间增加更多的灵活性,避免在两个层次之间建立静态的继承关系,通过桥接模式可以使它们在抽象层建立一个关联关系
  • 对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用

示例

需求V1:某公司准备开发一个跨平台图像浏览系统,要求可以显示JPG、PNG等多种格式图片,并且能够在Windows、Linux等多个操作系统上运行

代码语言:javascript复制
class Image {
    var system : System
    var format : String
    init(system:System,format:String) {
        self.system = system
        self.format = format
    }
    func parseFile() {
        self.system.show(format: self.format)
    }
}

class JPGImage : Image {
    
}

class PNGImage: Image {
    
}

class System {
    func show(format:String) {
        
    }
}
class WindowsSystem : System {
    override func show(format:String) {
        print("(format)格式在windows系统下显示")
    }
}

class LinuxSystem : System {
    override func show(format:String) {
        print("(format)格式在Linux系统下显示")
    }
}

客户端调用:

代码语言:javascript复制
let win = WindowsSystem()
let lin = LinuxSystem()
let jpgSys = JPGImage.init(system: win, format: "jpg" )
jpgSys.parseFile()
let jpgLin = JPGImage.init(system: lin, format: "jpg" )
jpgLin.parseFile()

let pngSys = PNGImage.init(system: win, format: "png")
pngSys.parseFile()
let pngLin = PNGImage.init(system: lin, format: "png")
pngLin.parseFile()

log:
jpg格式在windows系统下显示
jpg格式在Linux系统下显示
png格式在windows系统下显示
png格式在Linux系统下显示

需求V2:增加新的图像文件格式TIF

只需要创建TIFImage继承自Image即可

代码语言:javascript复制
class TIFImage : Image {
    
}

//客户端:
let tifSys = PNGImage.init(system: win, format: "tif")
tifSys.parseFile()
let tifLin = PNGImage.init(system: lin, format: "tif")
tifLin.parseFile()
tif格式在windows系统下显示
tif格式在Linux系统下显示

需求V2:增加一个新的操作系统Mac OS

只需要创建MacOSSystem继承自System即可

代码语言:javascript复制
class MacOSSystem : System {
    override func show(format:String) {
        print("(format)格式在Mac OS系统下显示")
    }
}

//客户端:
let mac = MacOSSystem()
let jpgMac = JPGImage.init(system: mac, format: "jpg")
jpgMac.parseFile()
let pngMac = PNGImage.init(system: mac, format: "png")
pngMac.parseFile()
let tifMac = JPGImage.init(system: mac, format: "tif")
tifMac.parseFile()

log:
jpg格式在Mac OS系统下显示
png格式在Mac OS系统下显示
tif格式在Mac OS系统下显示

0 人点赞