桥接模式-组装的艺术
或许,已经使用过桥接模式的设计思想,只是,你可能不知道它叫“桥接模式”而已。
我们学习设计模式,并不一定要生搬硬套到我们的代码架构中,而设计模式背后的思想,才是真正的精髓。正如本文所讲的桥接模式,听起来很陌生,就算不陌生,也不像平时常用的单例、工厂、适配、代理模式频繁出现,但是,桥接模式的设计思想其实很简单,说不定你平时在写代码过程中,就已经悄悄用上了。
但是,桥接模式也难,它难在概念的理解上,如果是根据概念去思考分析某一个功能场景是否可设计为桥接模式,那么 ,理解起来就稍微费劲一点,但桥接模式的实现是很简单的,一起分析讨论下下面一个功能需求场景。
用手机主题来说,一套完整的主题,主要涵盖以下几个要素:
图标、字体、壁纸、息屏
我们平时在使用手机主题时,可以以一整套主题为单位来切换,也可以在已有的多套主题当中,混合选取不同的主题要素,比如应用主题A的字体,主题B的图标,主题C的壁纸和息屏,又或者是单独下载某一个元素,单独应用上这个元素和其他元素进行搭配。
从这个功能使用体验来看,它的设计是很灵活的,可任由用户随意搭配。那么,如果从代码实现的角度来看,该如何实现呢?
首先,设计大于实现,先看一下设计结构图:

这个架构图其实很简单,为了更好的扩展和更灵活的设计和搭配,我们把主题和主题的四大元素都做一个抽象,然后每一个主题、每一个主题元素都是由外部实现。
由于主题做了抽象,所以我们的手机主题商店里头,出现了各种各样的主题;又由于主题元素(字体、图标、壁纸、息屏)做了抽象,所以主题商店里又有各种各样的主题元素,用户也能根据自己的喜欢,进行混搭。
如果你是代码设计小白,还是云里雾里,我们直接看代码好了。
#抽象主题
public abstract class Theme {// 主题IDprivate Long themeId;// 主题名字private String themeName;private Font font;private Icon icon;private Wallpaper wallpaper;private AOD aod;public Theme(Long themeId, String themeName,Font font, Icon icon, Wallpaper wallpaper, AOD aod) {this.themeId = themeId;this.themeName = themeName;this.font = font;this.icon = icon;this.wallpaper = wallpaper;this.aod = aod;}public void show() {System.out.println("主题名:" + themeName);System.out.println("字体名:" + font.getEleName());System.out.println("图标名:" + icon.getEleName());System.out.println("壁纸名:" + wallpaper.getEleName());System.out.println("息屏名:" + aod.getEleName());}
}
#主题实现
// 我们可以自定义自己的主题,继承Theme即可
public class CustomTheme extends Theme {public CustomTheme(Long themeId, String themeName,Font font, Icon icon, Wallpaper wallpaper, AOD aod) {super(themeId, themeName, font, icon, wallpaper, aod);}
}
#主题元素抽象
// 主题元素基类,任何主题元素都要继承它
public abstract class ThemeElement {// 主题元素Idprivate Long eleId;// 主题元素名称private String eleName;// 所属的主题ID,如果不属于任何一个主题,则为-1,// 如单独下载的字体、图标、壁纸、息屏,这种情况就不属于任何一套主题private Long themeId;public ThemeElement(Long eleId, String eleName, Long themeId) {this.eleId = eleId;this.eleName = eleName;this.themeId = themeId;}public Long getEleId() {return eleId;}public String getEleName() {return eleName;}public Long getThemeId() {return themeId;}
}// 字体抽象
public abstract class Font extends ThemeElement {public Font(Long eleId, String eleName, Long themeId) {super(eleId, eleName, themeId);}
}// 图标抽象
public abstract class Icon extends ThemeElement {public Icon(Long eleId, String eleName, Long themeId) {super(eleId, eleName, themeId);}
}// 壁纸抽象
public abstract class Wallpaper extends ThemeElement {public Wallpaper(Long eleId, String eleName, Long themeId) {super(eleId, eleName, themeId);}
}// 息屏抽象
public abstract class AOD extends ThemeElement {public AOD(Long eleId, String eleName, Long themeId) {super(eleId, eleName, themeId);}
}
#主题元素实现
注:由于内容篇幅关系,我们暂且定制这些主题元素
public class CuteFont extends Font {public CuteFont() {super(1L, "可爱风字体", -1L);}
}public class SquareFont extends Font {public SquareFont() {super(2L, "方正字体", -1L);}
}public class CrayonIcon extends Icon {public CrayonIcon() {super(5L, "蜡笔小新图标", -1L);}
}public class CityWallpaper extends Wallpaper {public CityWallpaper() {super(3L, "城市壁纸", -1L);}
}public class AnimalWallpaper extends Wallpaper {public AnimalWallpaper() {super(4L, "动物壁纸", -1L);}
}public class ClockAOD extends AOD {public ClockAOD() {super(6L, "时钟息屏", -1L);}
}
有了这些具体的主题元素之后(相当于手机主题的设计者设计好了主题元素),可以进行主题设计,用设计好的主题元素进行组装,构建出一个主题;也可以单独存在,上传到主题商店,供使用者下载。
那么这里我们直接用现有的主题元素,去混搭两款自定义的主题,代码如下:
public class App {public static void main(String[] args) {// 搭配自定义主题,并应用于手机系统显示CustomTheme customTheme1 = new CustomTheme(1L, "混搭自定义主题1",new CuteFont(), new CrayonIcon(), new CityWallpaper(), new ClockAOD());CustomTheme customTheme2 = new CustomTheme(2L, "混搭自定义主题2",new SquareFont(), new CrayonIcon(), new AnimalWallpaper(), new ClockAOD());customTheme1.show();customTheme2.show();}
}
然后我们看输出结果:
主题名:混搭自定义主题1
字体名:可爱风字体
图标名:蜡笔小新图标
壁纸名:城市壁纸
息屏名:时钟息屏主题名:混搭自定义主题2
字体名:方正字体
图标名:蜡笔小新图标
壁纸名:动物壁纸
息屏名:时钟息屏Process finished with exit code 0
不出意外的,我们用现有的定制好的主题元素,混搭出了我们想要的主题。
到这里,如果稍微有点代码设计经验的你,不难发现,这种设计思想和思路,似乎真的很常用,无非就是继承加组合,事实上,确实如此,桥接模式的设计实现本来就很简单。如果你是初入职场,去找工作面试时,如果面试官问你如何理解面向对象编程的几大特性,平时写代码中如何应用几大特性,这个例子和实现,或许能帮助有个比较好的回答。
并且,如果跟实际工作或生活中的场景关联起来,桥接模式也不难理解了。桥接模式也可以叫做组装模式,组装模式跟装饰者模式不一样,它们有一个本质的区别:在组装模式中,规定了必须包含的几个组件,少了谁,都无法构成一个完整的功能模块;而装饰者模式(也叫包装模式),是在原有功能上,加上一些点缀,以让这个功能变得更加的丰富。
用个很贴切的例子说明:
小轿车就是由发动机、离合器、变速器、轮胎、底盘、外壳等一个一个组件组装起来,但是每一个汽车组件,都会有不同的组件品牌商去生产,生产出来后卖给汽车品牌商,汽车品牌商用这些组件组装成一辆汽车,少了哪一个必要组件都不能算是一辆汽车,这就是桥接模式;而平时我们所看到的汽车改装,比如车漆、多媒体设备等,这些东西,就是汽车的点缀,你可以有,有了,汽车的气质就变得更加高档豪华,没有,也可以开,这就是装饰者模式。
那,桥接模式其实就很简单嘛,哪里难了?桥接模式的本质是很简单,但是桥接模式的创造者给出的概念定义却没那么好理解,下面是GoF的《设计模式》一书中对桥接模式的定义:
Decouple an abstraction from its implementation so that the two can vary independently.
翻译成中文是:“将抽象和实现解耦,让它们可以独立变化”。
是吧,如果仅从概念定义去理解,挺难领会到桥接模式的奥义的。而在业界中,对桥接模式普遍还有第二种定义:
如果一个类存在两个或多个独立变化的维度,那么我们可以通过组合的方式,让这两个或多个维度可以独立进行扩展。
其实,第二种定义更好理解一点。我们平时所说的面向接口编程,只是说在系统的某一个功能上讲接口和实现解耦,而桥接模式,则是对系统功能进行更细粒度的拆分,将各个独立的维度(或功能模块)进行抽象,然后使用时,按需组合或组装。
结合我们前文说的手机主题场景来看,一个主题就是一个比较完整的系统功能,而主题中的每一个元素,如字体、图标、壁纸、息屏,对于这个完整的系统功能来说,都是更细粒度且独立的子模块,那么,为了系统设计上的更好扩展和更灵活的搭配,我们将这些独立的子模块抽象出来,在广大的主题设计者去设计具体的主题元素,这样就能够让使用的用户灵活的混搭主题了,这就是桥接模式的精髓。同时,我有理由相信,你平时写代码过程中,一定有过类似的设计思想,证明你也是个老司机~
同时这里也想说一下,其实设计模式最精髓的东西是其背后的思想,因此对于同一种设计模式,也可以有不同的架构实现,所以,当我们去学习一种新的设计模式时,要把握住这种设计模式的核心思想,掌握了这个核心思想之后,将来如何应用到我们的项目中,真的就是随心所欲了。
THE END
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
