设计模式之外观模式

发布于:2024-05-06 ⋅ 阅读:(26) ⋅ 点赞:(0)

目录

1.概述

2.结构

3.实现示例

4.使用场景

5.总结


1.概述

        外观模式(Facade Pattern)是一种结构型设计模式,它为子系统中的一组接口提供一个统一的接口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式又称为门面模式,它是一种降低系统复杂度的模式,通过向客户端提供一个统一的接口,来隐藏系统的复杂性。

        外观模式是为了解决类与类之家的依赖关系的,像Java的spring框架一样,可以将类和类之间的关系配置到配置文件中,而外观模式就是将他们的关系放在一个Facade类中,降低了类类之间的耦合度。

2.结构

外观模式的UML结构图如下所示:

角色定义:

外观类(Facade):  提供一个简化的接口,封装了系统的复杂性。外观模式的客户端通过与外观对象交互,而无需直接与系统的各个组件打交道。外观类知道哪些子类复杂处理请求,将客户的请求代理给适当的子系统。

子系统(Subsystem):  由多个相互关联的类组成,实现子系统的功能,负责系统的具体功能。外观对象通过调用这些子系统来完成客户端的请求,处理Facade对象指派的任务。注意子类中没有Facade的任何信息,即没有对Facade对象的引用。

客户端(Client): 使用外观对象来与系统交互,而不需要了解系统内部的具体实现。

3.实现示例

        在C++中,外观模式可以通过定义一个接口(或类)来封装一组子系统的接口,从而提供一个更高层次、更简单的接口给客户端使用。以下是一个简单的C++外观模式示例,模拟了一个家庭自动化系统的场景。

        首先,我们定义几个子系统的接口和具体实现:

// 子系统接口  
class Light {  
public:  
    virtual ~Light() = default;  
    virtual void turnOn() = 0;  
    virtual void turnOff() = 0;  
};  
  
class TV {  
public:  
    virtual ~TV() = default;  
    virtual void turnOn() = 0;  
    virtual void turnOff() = 0;  
    virtual void setChannel(int channel) = 0;  
};  
  
// 子系统具体实现  
class ConcreteLight : public Light {  
public:  
    void turnOn() override {  
        std::cout << "Light is on.\n";  
    }  
  
    void turnOff() override {  
        std::cout << "Light is off.\n";  
    }  
};  
  
class ConcreteTV : public TV {  
public:  
    void turnOn() override {  
        std::cout << "TV is on.\n";  
    }  
  
    void turnOff() override {  
        std::cout << "TV is off.\n";  
    }  
  
    void setChannel(int channel) override {  
        std::cout << "TV is set to channel " << channel << ".\n";  
    }  
};

接下来,我们定义外观类,它封装了子系统的接口并提供了一个简单的接口给客户端使用:

// 外观类  
class HomeAutomationFacade {  
private:  
    Light* light;  
    TV* tv;  
  
public:  
    HomeAutomationFacade() : light(new ConcreteLight()), tv(new ConcreteTV()) {}  
    ~HomeAutomationFacade() {  
        delete light;  
        delete tv;  
    }  
  
    void turnOnAll() {  
        light->turnOn();  
        tv->turnOn();  
    }  
  
    void turnOffAll() {  
        light->turnOff();  
        tv->turnOff();  
    }  
  
    void watchMovie(int channel) {  
        tv->turnOn();  
        tv->setChannel(channel);  
        light->dim(); // 假设灯有调暗的功能,但不在Light接口中定义  
    }  
  
    // 注意:dim()方法可能不是Light接口的一部分,但为了演示,我们在这里调用它  
    // 在实际应用中,你可能需要扩展Light接口或外观类来处理这种情况  
    void dimLight() {  
        std::cout << "Light is dimmed.\n";  
        // 这里假设dim是一个简单的输出,实际中可能涉及更复杂的逻辑  
    }  
};

最后,客户端代码使用外观类来与系统进行交互:

int main() {  
    HomeAutomationFacade facade;  
  
    facade.turnOnAll(); // 打开所有设备  
  
    facade.watchMovie(5); // 观看5频道的电影,自动打开电视和调暗灯光  
  
    facade.turnOffAll(); // 关闭所有设备  
  
    return 0;  
}

在这个例子中,HomeAutomationFacade是外观类,它封装了LightTV子系统的接口,并为客户端提供了一个简单的接口(turnOnAllturnOffAllwatchMovie等)。客户端只需要与外观类交互,而不需要了解子系统的具体实现细节。

4.使用场景

1)当一个系统需要提供一个简单的接口来访问子系统时,可以使用外观模式。

2)当客户端与多个子系统之间存在复杂的依赖关系时,可以使用外观模式来简化这些依赖关系。

3)当需要构建一个层次结构的系统时,可以使用外观模式来定义系统中每一层的入口点。

5.总结

优点

1)降低耦合度:即减少相互依赖,客户端与子系统之间的耦合度降低,客户端只与外观角色交互,不需要了解子系统的具体实现。

2)简化操作:客户端不再需要了解子系统的内部实现和具体细节,只需要与外观角色交互,简化了客户端的操作。

3)易于维护:当子系统内部发生变化时,只需要修改外观角色,而不需要修改客户端代码,降低了维护成本。

缺点

1)不符合“开闭原则”:如果新增子系统或删除子系统,可能需要修改外观角色的代码,这在一定程度上违反了“开闭原则”。

2)可能隐藏了子系统的复杂性:如果外观角色设计得过于复杂,可能会隐藏子系统的复杂性,使得客户端难以理解和使用。