Objective-C设计模式解析-外观

°ฅ ฅ。

°ฅ ฅ。

发表于 2017-01-03 11:48:25

看图识模式

自助游(图1)

旅行社(图2)

举个栗子

不知道大家有没有通过旅行社报团出去旅游的经历?这是一个很好的外观模式的应用。

方式一:客户直接调用各个子系统的功能,和各个子系统之间形成紧耦合的关系(上图一)
方式二:提供一个高层接口,该高层接口负责和子系统进行交互,并向客户提供需要使用的接口(上图二)
从上面两种方式的图式结构可以看到,对客户来说,方式二比方式一要好用很多,因为在方式二中,客户不需要知道各个子系统的逻辑,只需要和高层接口交互就可以了。实际上方式二,就是我们这里要说的外观模式了。

如果这里不应用外观模式,我们(上图中的Client),就得自己去联系交通工具、预定旅馆、饭馆、景点门票等,相信这样的旅程,大家会感觉很累。有了外观角色(上图中的Facade),它会帮我们去处理这些事情。

迪米特法则

简介

得墨忒耳定律(Law of Demeter,缩写LoD)亦稱為“最少知识原则(Principle of Least Knowledge)”,是一种软件开发的设计指導原則,特别是面向对象的程序设计。得墨忒耳定律是松耦合的一种具体案例。該原則是美国東北大學在1987年末在发明的。
这个原理的名称来源于希腊神话中的农业女神,孤独的得墨忒耳(Demeter)。

定义:一个对象应该对其他对象保持最少的了解。
问题由来:类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。

解决方案:尽量降低类与类之间的耦合。

自从我们接触编程开始,就知道了软件编程的总的原则:低耦合,高内聚。无论是面向过程编程还是面向对象编程,只有使各个模块之间的耦合尽量的低,才能提高代码的复用率。低耦合的优点不言而喻,但是怎么样编程才能做到低耦合呢?那正是迪米特法则要去完成的。
迪米特法则又叫最少知道原则,最早是在1987年由美国Northeastern University的Ian Holland提出。通俗的来讲,就是一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类来说,无论逻辑多么复杂,都尽量地的将逻辑封装在类的内部,对外除了提供的public方法,不对外泄漏任何信息。迪米特法则还有一个更简单的定义:只与直接的朋友通信。首先来解释一下什么是直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖、关联、组合、聚合等。其中,我们称出现成员变量、方法参数、方法返回值中的类为直接的朋友,而出现在局部变量中的类则不是直接的朋友。也就是说,陌生的类最好不要作为局部变量的形式出现在类的内部。

一些比喻

最常见的比喻是:不要和陌生人说话

看看这个:假设我在便利店购物。付款时,我是应该将钱包交给收银员,让她打开并取出钱?还是我直接将钱递给她?

再做一个比喻:人可以命令一条狗行走(walk),但是不应该直接指挥狗的腿行走,应该由狗去指挥控制它的腿如何行走。

模式与意义

迪米特法则可以简单说成:talk only to your immediate friends。 对于OOD来说,又被解释为下面几种方式:一个软件实体应当尽可能少的与其他实体发生相互作用。每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。
迪米特法则的初衷在于降低类之间的耦合。由于每个类尽量减少对其他类的依赖,因此,很容易使得系统的功能模块功能独立,相互之间不存在(或很少有)依赖关系。
迪米特法则不希望类之间建立直接的联系。如果真的有需要建立联系,也希望能通过它的友元类来转达。因此,应用迪米特法则有可能造成的一个后果就是:系统中存在大量的中介类,这些类之所以存在完全是为了传递类之间的相互调用关系——这在一定程度上增加了系统的复杂度。
设计模式中的门面模式(Facade)和中介模式(Mediator),都是迪米特法则应用的例子。
值得一提的是,虽然Ian Holland对计算机科学的贡献也仅限于这一条法则,其他方面的建树不多,但是,这一法则却不仅仅局限于计算机领域,在其他领域也同样适用。比如,美国人就在航天系统的设计中采用这一法则。

模式定义

外观模式为子系统中一组不同的接口提供统一的接口。外观定义了上层接口,通过降低复杂度和隐藏子系统间的通信及依存关系,让子系统易于使用。

API 的使用者完全不知道这内部的业务逻辑有多么复杂。当我们有大量的类并且它们使用起来很复杂而且也很难理解的时候,外观模式是一个十分理想的选择。
外观模式把使用和背后的实现逻辑成功解耦,同时也降低了外部代码对内部工作的依赖程度。如果底层的类发生了改变,外观的接口并不需要做修改。

结构图

这个定义,通过上面引言的图示讲解,应该很好理解了,这里再分析一下定义中的两个重要角色:

外观角色:就是引言图示中的“高层接口”,客户端可以调用这个角色的方法;另外,该角色知道相关的子系统的功能和责任。

子系统角色:可以同时有一个或者多个子系统。每一个子系统都可以被客户端直接调用,或者被外观角色调用。

代码

比方说今天我不想开车,于是打电话叫了出租车。只要出租车能把我送到目的地,我不在乎车牌和型号。我会直接对司机说“我要去somePlace”,然后司机会执行一系列的命令(松手刹、换挡、踩油门等等)。出租车司机抽象出了驾驶汽车的底层负责操作的细节。他通过提供驾驶服务(简化了接口),把我与原本复杂的车辆操作接口分离。出租车和我直接的接口只是一个简单的“我要去xxx”命名。

很多旧的面向对象应用程序中,可能有许多类分散于带有各种功能的系统中。要把这些类用于某个功能,需要知道全部细节才能在一组算法中使用它们。如果从逻辑上将其中一些类组合成一个简单的接口,可以让这些类更易于使用。

编写代码

Car定义了几个操其内部对象的方法,如pressBrakes、releaseBrakes、changGears...。客户端想要使用Car的内部功能,必须了解如何使用这些方法进行正确操作。

@interface Car : NSObject

/// 踩刹车
- (void)pressBrakes;
/// 松刹车
- (void)releaseBrakes;
/// 换挡
- (void)changGears;
/// 踩油门
- (void)pressAccelerator;
/// 松油门
- (void)releaseAccelerator;

@end 


@implementation Car

- (void)pressBrakes
{
    NSLog(@"car: 踩刹车");
}

- (void)releaseBrakes
{
    NSLog(@"car: 松刹车");
}

- (void)changGears
{
    NSLog(@"car: 换挡");
}

- (void)pressAccelerator
{
    NSLog(@"car: 踩油门");
}

- (void)releaseAccelerator
{
    NSLog(@"car: 松油门");
}

@end

Taximeter本身是一个复杂的系统,它有两个让客户端操作其对象的方法。start和stop方法只是让Taximeter开始或停止。这里不深入Taximeter的细节。

@interface Taximeter : NSObject

/// 开始打表
- (void)start;
/// 结束打表
- (void)stop;

@end  


@implementation Taximeter

- (void)start
{
    NSLog(@"Taximeter: 开始打表");
}

- (void)stop
{
    NSLog(@"Taximeter: 结束打表");
}

@end  

目前,出租车服务系统里面有两个复杂的子系统。需要一个CarDriver作为外观以简化接口。代码如下

@interface CarDriver : NSObject

- (void)driveToLocation:(CGPoint)place;

@end  


@implementation CarDriver

- (void)driveToLocation:(CGPoint)place
{
    // ....
    
    // 开启计价器
    Taximeter *meter = [Taximeter new];
    [meter start];
    
    // 操作车辆,直到到达位置place
    Car *car = [Car new];
    [car releaseBrakes];
    [car changGears];
    [car pressAccelerator];
    
    // ....
    
    // 到达位置place,停下车和计价器
    [car releaseAccelerator];
    [car pressBrakes];
    [meter stop];
    
    // ....
}

@end  

CarDriver的外观方法决定了客户端可以用多简单的方式使用整个出租车服务系统。客户只需调用driveToLocation:方法,其余的操作就会在消息调用中发生。客户端不需要了解底层发生的一切。

总结

通过上面的讲解,我们来分析一下外观模式的特点:

  • Facade设计模式注重从架构的层次去看整个系统,而不是单个类的层次。很多时候,它是一种架构设计模式,比如我们常用的三层架构。

  • Facade模式简化了整个系统的接口,同时对于系统内部(子系统)与外部程序(Client)来说,也达到了一种“解耦”的效果。

根据外观模式的特点,我们可以在以下情况中使用Facade模式:

  • 不需要使用一个复杂系统的所有功能,只需要使用系统的部分功能时,那么应用Façade模式提供一个高层接口将比直接调用原系统的接口简单得多。

  • 希望封装或者隐藏原系统的接口时,可以考虑使用外观模式。

  • 希望使用原系统的部分功能,而且还希望增加一些新的功能。

  • 构建一个具有层次结构的子系统时,使用Facade模式定义子系统中每层的高级接口。如果子系统之间是相互依赖的,你可以让它们仅通过Facade进行通讯,从而简化了它们之间的依赖关系。

其实这个设计模式我们很常见,一般我们使用第三方类的时候都会有这种模式,使用第三方时我们只需要引用第三方的其中改一个文件就能满足很多功能的使用。我只这个文件就是讲子系统的一些方法归并到了这个文件中,从而使使用者上手更快。

内容来源:https://segmentfault.com/a/1190000007987012

用户评论
开源开发学习小组列表