设计模式之接口型模式
接口型模式包括了:适配器模式、外观模式、组合模式和桥接模式。
接口型模式主要解决什么问题?
类Client的实例instanceClient希望使用另一个对象instanceX提供的服务service,但在设计时,我们并不能确定对象instanceX究竟属于哪个类。
解决的办法:
将对象instanceX提供的服务service抽象为一个接口ServiceProvider,然后让对象instanceClient通过持有接口ServiceProvider的实例来使用服务service。
1、适配器模式(Adapter)
将一个类的接口转换成客户系统的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。主要通过继承解决在软件系统中,常常要将一些"现存的对象"放到新的环境中,而新环境要求的接口是现对象不能满足的。必须注意的是适配器不是在详细设计时添加的,而是解决正在服役的项目的问题。
优点:1、可以让任何两个没有关联的类一起运行。 2、提高了类的复用。 3、增加了类的透明度。 4、灵活性好。
缺点:过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
使用场景:有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式。
举例说明一下,比如说现在有一个音乐播放器,它只能支持音乐文件mp3的播放,而不能支持其它音乐文件的播放,当然视频文件是不支持播放的;但是音乐文件却有很多种,比如说mp3、mp4、wav等,针对不同的音乐文件,我们都应该能播放它,这才是一个合格的音乐播放器。
那么如何扩展这个简易的音乐播放器呢:
第一步:本来有一个播放器接口(意思就是只要是个播放器就应该具有的功能),里面就只有播放一个功能
第二步:创建一个我要开发的更高级的播放器接口,可以播放wav和mp4格式的播放功能
第三步:我要播放wav、mp4格式,那么我就要做两个播放器以致可以播放这两种格式的音乐文件的播放器
第四步:既然有了两个格式的播放器,那么我就要做一个适配器,以至于每种格式的音乐文件进来,都能自动匹配去播放
第五步:对原本的播放器扩展功能(使用适配器),扩展可以播放mp4和wav音乐
最后运行验证一下高级那么一丢丢的播放器:
输出结果:
2、外观模式(Facade)
为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。主要是为了降低访问复杂系统的内部子系统时的复杂度,简化客户端与之的接口。
本来两个用户,调用的不同的接口,如果再来几个客户,又是调用不同的接口,会造成整个调用体系非常混乱,所以为了使客户端不与系统耦合,而定义了外观类与系统耦合。
优点: 1、减少系统相互依赖。 2、提高灵活性。 3、提高了安全性。
缺点:不符合开闭原则,如果要改东西很麻烦,继承重写都不合适。
使用场景:1、为复杂的模块或子系统提供外界访问的模块。 2、子系统相对独立。 3、预防低水平人员带来的风险。
举个例子就是,超市给人提供的服务。如果说,一个顾客想要买鸡蛋、葡萄、鞋子,如果他不去超市,那他就要先找到卖鸡蛋的店铺其买鸡蛋,接着去买水果的点买葡萄,去生活用品店买鞋子,多麻烦。而你只要去超市,就可以一次性买到这三种东西。甚至,你到超市,连去拿鸡蛋、葡萄、鞋子都懒得去,那就叫超市的服务员也同样是这个意思(我很怀疑现在超市有没有这样的服务,哈哈哈)。
第一步:创建一个超市接口,定义这个超市卖东西的
第二步:实现这个超市的接口,就是说,超市里具体卖的什么东西
第三步:创建一个超市的超级好的服务员
最后:告诉服务员你要买的东西,让服务员去拿(哈哈哈)
输出结果:
3、组合模式(Composite)
将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得客户对单个对象和复合对象的使用具有一致性。它在我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以向处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。
优点: 1、高层模块调用简单。 2、节点自由增加。
缺点:因为叶结点和枝结点调用同一个接口,所有叶结点会实现一些没有意义的方法功能。
使用场景:部分、整体场景,如树形菜单,文件、文件夹的管理。
举个简单的栗子,当我们买东西的时候打成一个包,就是只要整体,但所买到的东西有单个的,也有已经打包(组合)了的,这个时候我们又要把单个的与已经打包的打成一个大包(注意,这里不是说要把之前打好的包再添加一个,而是两个一起打成一个大包,汇成一个整体)。
第一步:创建一个接口
第二步:单个对象与组合对象实现这个接口
最后:将组合对象和单个对象一起打包
输出:
4、桥接模式(BridgePattern)
将抽象部分与它的实现部分分离,使它们都可以独立地变化。主要是因为在有多种可能会变化的情况下,用继承会造成类爆炸问题,扩展起来不灵活。
优点: 1、抽象和实现的分离。 2、优秀的扩展能力。 3、实现细节对客户透明。
缺点:桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
使用场景: 1、如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。 2、对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。 3、一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
举个栗子,比如说画一个圆,这个圆有两个维度,一个是这个圆的大小,坐标;还有一个是这个圆的颜色。那么就可以用桥接的模式来实现
第一步:创建一个桥接实现接口
第二步:创建实现这个接口的实体类
第三步:使用桥接接口创建图形的抽象类
第四步:创建实现抽象类
最后:画出不同颜色不同大小的圆
输出:
桥接模式与适配器模式有点相似:
共同点:桥接和适配器都是让两个东西配合工作。
不同点:出发点不同。
适配器:改变已有的两个接口,让他们相容。
桥接模式:分离抽象化和实现,使两者的接口可以不同,目的是分离。
所以说,如果你拿到两个已有模块,想让他们同时工作,那么你使用的适配器。如果你还什么都没有,但是想分开实现,那么桥接是一个选择。桥接是先有桥,才有两端的东西,适配是先有两边的东西,才有适配器,桥接是在桥好了之后,两边的东西还可以变化。例如游戏手柄,就象个桥,它把你的任何操作转化成指令。