设计模式读书笔记2
上次讲了设计模式中的建造型模式,这次来谈谈结构型模式。其实结构型模式与建造型模式有很大的相似之处,有时都是为了返回一个新构造的对象,而结构型模式的重点在于基于已有的类的组合来构造符合特定要求的新类。下面基于源码分析一下几种常见的结构型模式。
首先说桥接模式,结构型模式中最重要的模式,用于把抽象化与实现化解耦,使得二者可以独立变化,更通俗准确地说就是分别定义高层的接口和具体的实现,使得具体实现修改了,高层接口可以不变。桥接模式与其他很多设计模式有着相似之处,我一开始看了一些桥接模式的源码后感觉它与工厂模式没有什么区别。其实,桥接模式的思想是纯粹的抽象与实现的分离,其他很多模式中都蕴含着这样的思想,所以存在一些相似性,而桥接模式是当中最标准的抽象、实现解耦模式。比如要说工厂模式与桥接模式有什么区别,实质上区别还是很大的,工厂模式的目的在于创建对象时使用统一的接口而不暴露创建逻辑,而桥接模式的目的如上所说,对不同的实现方式用统一的接口进行操作,可见工厂模式可以看作桥接模式在创建对象时的特例。
以下是桥接模式的一般性源码:
public abstract class Implementor{ 2: public abstract void OperationImp(); 3: } 4: 5: public class ConcreteImplementorA extends Implementor{ 6: public void OperationImp(){ 7: //省略实现... 8: } 9: } 10: 11: public class ConcreteImplementorB extends Implementor{ 12: public void OperationImp(){ 13: //省略实现... 14: } 15: } 16: public abstract class Abstraction{ 17: protected Implementor implementor; 18: public abstract void Operation(); 19: } 20: 21: public class RefinedAbstraction extends Abstraction{ 22: public RefinedAbstraction(Implementor implementor){ 23: this.implementor=implementor; 24: } 25: 26: public void Operation(){ 27: //... 28: implementor.OperationImp();//调用实现化角色实现的操作 29: //... 30: } 31: } 32: 33: public class Client{ 34: public static void main(String[] args){ 35: Abstraction abstraction=new RefinedAbstraction(new ConcreteImplementorA()); 36: abstraction.Operation(); 37: } 38: }
这就很好地说明了桥接模式的内涵,也给出了桥接模式的使用方式。把上述代码中的 Abstraction 和 Implementor 看作操作系统与不同的硬件设备,操作系统为用户提供了 read 、 write 这样的文件操作方式,而这些函数内部封装了不同硬件设备的不同驱动函数,这就是桥接模式的典型例子。
接着说适配器模式。相对于桥接模式,适配器模式更好理解,它是两个不兼容的接口之间的桥梁,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。这样对用户来说,可以用统一的方式调用原本不兼容的类。举个例子,AudioPlayer 可以播放 mp3 格式的音频文件,AdvancedMediaPlayer 可以播放 vlc 和 mp4 格式的文件,我们想要让 AudioPlayer 播放其他格式的音频文件。实现方式:
public class MediaAdapter implements MediaPlayer { AdvancedMediaPlayer advancedMusicPlayer; public MediaAdapter(String audioType){ if(audioType.equalsIgnoreCase("vlc") ){ advancedMusicPlayer = new VlcPlayer(); } else if (audioType.equalsIgnoreCase("mp4")){ advancedMusicPlayer = new Mp4Player(); } } @Override public void play(String audioType, String fileName) { if(audioType.equalsIgnoreCase("vlc")){ advancedMusicPlayer.playVlc(fileName); }else if(audioType.equalsIgnoreCase("mp4")){ advancedMusicPlayer.playMp4(fileName); } } }
public class AudioPlayer implements MediaPlayer { MediaAdapter mediaAdapter; @Override public void play(String audioType, String fileName) {
if(audioType.equalsIgnoreCase("mp3")){ System.out.println("Playing mp3 file. Name: "+ fileName); }
else if(audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")){ mediaAdapter = new MediaAdapter(audioType); mediaAdapter.play(audioType, fileName); } else{ System.out.println("Invalid media. "+ audioType + " format not supported"); } } }
这样就把两种接口不相同的类用统一的方式封装,对用户来说几个功能都可以用相同的方式调用。适配器模式也体现出结构型模式的特点,即把几种不同的类用特定方式组合到一起,形成符合需求的新类。
再来说说装饰器模式,其目的是在不改变类的结构的情况下,向其动态地添加新的功能。通常我们要扩展一个类会使用继承方式,而由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀,这时可以考虑装饰器模式。该模式实现方式是新建一个装饰类(虚类)并添加被装饰的类作为成员,继承该类并重写相关功能函数,添加新的功能。以上一篇博客中的 Shape 形状类为例(相关源码见上一篇博客),我们将把一个形状装饰上不同的颜色,同时又不改变形状类。
public abstract class ShapeDecorator implements Shape { protected Shape decoratedShape; public ShapeDecorator(Shape decoratedShape){ this.decoratedShape = decoratedShape; } public void draw(){ decoratedShape.draw(); } }
public class RedShapeDecorator extends ShapeDecorator { public RedShapeDecorator(Shape decoratedShape) { super(decoratedShape); } @Override public void draw() { decoratedShape.draw(); setRedBorder(decoratedShape); } private void setRedBorder(Shape decoratedShape){ System.out.println("Border Color: Red"); } }
这样就在形状类的基础上添加了颜色功能。
最后说说外观模式,这种模式非常简单却十分常用,即向客户端提供了一个客户端可以访问系统的接口,而隐藏了系统的复杂性。简单来说就是用户可以简洁地调用接口中的功能,而不关注具体实现细节。服务器与客户端之间的通信就采用这种模式。依然以 Shape 类为例,希望统一调用不同形状的 draw 函数。
public class ShapeMaker { private Shape circle; private Shape rectangle; private Shape square; public ShapeMaker() { circle = new Circle(); rectangle = new Rectangle(); square = new Square(); } public void drawCircle(){ circle.draw(); } public void drawRectangle(){ rectangle.draw(); } public void drawSquare(){ square.draw(); } }
public class FacadePatternDemo { public static void main(String[] args) { ShapeMaker shapeMaker = new ShapeMaker(); shapeMaker.drawCircle(); shapeMaker.drawRectangle(); shapeMaker.drawSquare(); } }
就是这么简单的方式。其与工厂模式的区别在于,工厂模式的目的在于创建,而外观模式的目的在于使用调用功能。
还有一些其他的结构型模式,比如组合模式用树形结构表示一组相似对象的层次,过滤器模式用特定要求来过滤一组对象,享元模式用于共享相同的对象来节省资源,代理模式用一个类代表另一个类的功能来避免直接访问。细节详见相关资料,这次就不多说了,下次再来讲讲行为型设计模式。