今天讲外观模式,这个模式其实理解起来超级简单,为什么呢?这个模式其实我觉得可以用四个字来形容它:化繁为简。因为这个模式就是用来提供简化接口的。什么是简化接口?其实就是将很多复杂的接口组合起来成为一个新接口。你或许会问,把许多复杂的接口组合起来不是会更复杂吗,怎么会变得简单,这不是自相矛盾吗?哈哈,那你就错了,其实外观模式更像一个东西:宏。又拿宏说事儿了,不是上次命令模式里面提到过“命令宏”的吗,这个模式和宏有什么关系呢?嘿嘿,如果把上次“命令宏”称为“狭义的宏”的话,那么外观模式就是“广义的宏”(似乎越来越难理解了……)。
别急,让我慢慢道来,先解决为什么叫“广义的宏”这个问题。首先,广义代表这个模式具有很高的通用性,也就是说,它不只是限于对单一类型的接口进行“捆绑销售”,它还能对不同类型的接口进行“集中甩卖”,哎,说的专业点,就是外观模式可以将不同子系统的接口组合成一个接口供用户使用,而这个接口因为组合了很多的功能,使得用户不用自己去定义n多的子系统,然后自己手动操作这些子系统的方法,而只需要调用这个合成的接口就能完成一大堆的功能,是不是简化了呢?
再来详细讲一下吧,定义还是必须的:
- 外观模式:
为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。 - 适用性:
- 当你要为一个复杂子系统提供一个简单接口时。子系统往往因为不断演化而变得越来越复杂。大多数模式使用时都会产生更多更小的类。这使得子系统更具可重用性,也更容易对子系统进行定制,但这也给那些不需要定制子系统的用户带来一些使用上的困难。Facade 可以提供一个简单的缺省视图,这一视图对大多数用户来说已经足够,而那些需要更多的可定制性的用户可以越过facade层。
- 客户程序与抽象类的实现部分之间存在着很大的依赖性。引入facade将这个子系统与客户以及其他的子系统分离,可以提高子系统的独立性和可移植性。
- 当你需要构建一个层次结构的子系统时,使用facade模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,你可以让它们仅通过facade进行通讯,从而简化了它们之间的依赖关系。
再上类图:
对这类图是不是有点无语?是不是感觉摸不着头脑?呵呵,别被它的外表所吓倒,其实你只需要关心最上面的那个接口就行了,对于subsystem也就是子系统可以完全不需要仔细的考虑,而只需要知道如何调用子系统提供的接口就行了,我们还是用个例子来说吧:
就拿开车来说,开车是需要很多的操作的,很多小细节啊(这可是学驾照时的亲身体会啊),我们简单说一下:开车门->插钥匙->启动发动机->踩离合器->进档->松离合器->松手刹->加油门->转动方向盘...(车跑起来了,哈哈,不过后序步骤就要靠你自己了;))。但随着科技的发展,现在果断出现了全自动的汽车(科技真给力),对于这种全自动的汽车来说,显然没有了人,这些操作就只能又计算机控制了,如果现在要你写一个这样的全自动驾车的程序,使得用户只需要插入钥匙,点击开车按钮就能自动开车了(这就方便了,每个人都会),那该怎么写呢?
是不是觉得很复杂?没关系,让复杂的问题交给科学家吧,咱们要懂得利用他们的成果,嘻嘻,假设科学家们已经将复杂的控制功能封装好了,成为了一个个子系统,比如车门,档位,发动机,离合器,手刹,油门都有了各自的控制方法,那我们现在就只需要写汽车的接口,就三个:权限认证(插钥匙,当然现在科技这么发达,也有可能出现别的验证方式呢),还有开车,停车,怎么做呢:
//汽车的接口 public interface ICar { public bool auth(); public void drive(); public void stop(); } 如前所述,现在科学家提供了很多的控制类接口: //车门接口 public interface ICarDoor { public void open(); public void close(); } //档位接口 public interface IGears { public void setLevel(int level); } //转向灯接口 public interface ITurnLight { //0为左,1为右 public void setLight(int direction); } //喇叭接口 public interface ISpeaker { public void beep(); } //引擎接口 public interface IEngine { public void open(); public void close(); public void refuel(); } //离合器接口 public interface IClutch { public void press(); public void loose(); } //手刹接口 public interface IHandBrake { public void pull(); public void loose(); } //油门接口 public interface IAccelerator { public void accelerate(int level); } //方向盘 public interface IWheel { public void toLeft(int degree); public void toRight(int degree); } //权限认证(插钥匙,当然现在科技这么发达,也有可能出现别的验证方式呢), public interface IAuthSystem { public bool auth(); } 既然已经有了这么多接口,那么我们的自动系统就只需要根据这些接口进行顺序的调用就可以了: public class AutoCar implements ICar { ICarDoor carDoor; IGears gears; ITurnLight turnLight; ISpeaker speaker; IEngine engine; IClutch clutch; IHandBrake handBrake; IAccelerator accelerator; IWheel wheel; public AutoCar(ICarDoor carDoor, IGears gears, ITurnLight turnLight, ISpeaker speaker, IEngine engine, IClutch clutch, IHandBrake handBrake, IAccelerator accelerator, IWheel wheel, IAuthSystem authSystem) { this.carDoor=carDoor; this.gears=gears; this.turnLight=turnLight; this.speaker=speaker; this.engine=engine; this.clutch=clutch; this.handBrake=handBrake; this.accelerator=accelerator; this.wheel=wheel; this.authSystem=authSystem; } public void drive() { carDoor.close();//关门都由系统来做,人真懒啊,:) if(authSystem.auth())//验证权限 { engine.open();//打开发动机 clutch.press();//踩死离合器 gears.setLevel(1);//进1档 turnLight.setLight(0);//开左转向灯 speaker.beep();//鸣笛 clutch.loose();//松开离合器 handBrake.loose();//松手刹 accelerator.accelerate(1);//加一点就行了,别过猛了,不然会很暴力的,:) //后面的事交给科学家吧,:) } } public void stop(); }
这样一来,用户只需要关心ICar接口就行了,想想当你想出门的时候,只需要打开车门,插入钥匙,然后按“开车”按钮,然后什么都不用管,那将是多么的惬意啊,其实,这一切都得感谢外观模式啊,外观模式就是提供一个非常友好的外观给用户(长得漂亮谁不喜欢啊:)),使得用户不需要关心它内部的实现细节,使用户从繁重的工作中解脱出来。
唉,这上面就是外观模式?不就是把一堆类集合在一起,然后把需要的不同类的方法集中到一个方法里面实现吗,这么简单?是啊,就这么简单!多简单啊!我想现在你应该搞清楚为什么我把外观模式叫做广义的宏了吧,不信你再看看上面的代码,Car类中的drive()方法不就是个典型的宏吗,如果你还不清楚,请深入的学一下office word,:)
好了,这个模式就讲到这里,但顺便提一下另外几个模式:装饰者模式和适配器模式,难道这三个模式有什么共同点吗?是的,它们的共同点就是:对接口进行再次封装。装饰者模式是将被装饰者的接口动态的加上新的职责,适配器模式是将被适配者的接口转换成新的接口,而外观模式则是组合多个系统的接口创造新的接口。各种模式都有各自独特的作用,要看你怎么用了。