【设计模式】外观模式

项目经验:

hopeView客户端 访问服务器,只访问同一个接口,请求到哪个模块,由代理路由负责。

应用程序访问 数据库,只访问一个端口8066,具体访问到哪个库上,由mycat路由。

前言

其他设计模式介绍
1分钟全面了解“设计模式”
单例模式(Singleton) - 最易懂的设计模式解析
简单工厂模式(SimpleFactoryPattern)- 最易懂的设计模式解析
工厂方法模式(Factory Method)- 最易懂的设计模式解析
抽象工厂模式(Abstract Factory)- 最易懂的设计模式解析
策略模式(Strategy Pattern)- 最易懂的设计模式解析
适配器模式(Adapter Pattern)- 最易懂的设计模式解析
代理模式(Proxy Pattern)- 最易懂的设计模式解析
模板方法模式(Template Method) - 最易懂的设计模式解析
建造者模式(Builder Pattern)- 最易懂的设计模式解析
外观模式(Facade Pattern) - 最易懂的设计模式解析


目录


1. 介绍

1.1 定义

        外观模式是一种结构型设计模式,它提供了一个统一的接口来访问子系统中的一组接口。这个接口使得子系统更容易使用,因为客户端代码不需要直接与复杂的子系统交互,而是通过一个简化的接口进行操作。
外观模式的主要目的是为复杂的子系统提供一个更简单的接口,从而降低系统的耦合度,提高灵活性和可维护性。

如下图:

原理图

给个网站的导航例子你就懂了:以前我需要在搜索栏逐个搜索网站地址;有了网站导航(用了外观模式)后,就方便很多了

例子

1.2 主要作用

  • 外观模式的主要目的是为复杂的子系统提供一个更简单的接口,从而降低系统的耦合度,提高灵活性和可维护性。

2. 模式原理

2.1 UML类图 & 组成

2.2 实例讲解

接下来我用一个实例来对建造者模式进行更深一步的介绍。
a. 实例概况

  • 背景:小成的爷爷已经80岁了,一个人在家生活:每次都需要打开灯、打开电视、打开空调;睡觉时关闭灯、关闭电视、关闭空调;
  • 冲突:行动不方便,走过去关闭那么多电器很麻烦,代码如下:

**1. **电器类:

//灯类
public class SubSystemA_Light {  
     public void on(){  
        System.out.println("打开了灯....");  
    }  
      
     public void off(){  
        System.out.println("关闭了灯....");  
    }  
}  

//电视类
public class SubSystemB_Television {  
     public void on(){  
        System.out.println("打开了电视....");  
    }  
      
     public void off(){  
        System.out.println("关闭了电视....");  
    }  
}  

//空调类
public class SubSystemC_Aircondition {  
     public void on(){  
        System.out.println("打开了电视....");  
    }  
      
     public void off(){  
        System.out.println("关闭了电视....");  
    }  
}  

**2. **客户端调用:小成爷爷使用电器情况


public class Facade Pattern{ 
      public static void main(String[] args){
        {
            SubSystemA_Light light = new SubSystemA_Light();
            SubSystemB_Television television = new SubSystemB_Television();
            SubSystemC_Aircondition aircondition = new SubSystemC_Aircondition();

            //起床后开电器
            System.out.prinln("起床了");
            light.on();
            television.on();
            aircondition.on();
           System.out.prinln("可以看电视了");

           //睡觉时关电器
           System.out.prinln("睡觉了");
            light.off();
            television.off();
            aircondition.off();
             System.out.prinln("可以睡觉了");
        }
    }

结果

起床了
打开了灯
打开了电视
打开了空调
可以看电视了

睡觉了
关闭了灯
关闭了电视
关闭了空调
可以睡觉了

从上面可以看出,在不使用外观模式的情况下,小成爷爷需要对每个电器都进行操作,非常不方便

客户端与三个子系统都发送了耦合,使得客户端程序依赖与子系统

解决方案

小成买了一个智能家具控制器(外观对象/统一接口)给他爷爷,他爷爷只需要一键就能打开/关闭 灯、电视机、空调

  1. 即用外观模式来为所有子系统设计一个统一的接口
  2. 客户端只需要调用外观类中的方法就可以了,简化了客户端的操作

**1. **电器类同上

**2. **外观类:智能遥控器

public class Facade{
      
      SubSystemA_Light light;
      SubSystemB_Television television ;
      SubSystemC_Aircondition aircondition;
    

      //传参
    public Facade(SubSystemA_Light light,SubSystemB_Television television,SubSystemC_Aircondition aircondition){  
        this.light = light;  
        this.television  = television ;  
        this.aircondition =aircondition;  
    
    }  
      //起床后一键开电器
      public void on{
        System.out.prinln("起床了"); 
        light.on(); 
        television.on(); 
        aircondition.on();
    
        }

          //睡觉时一键关电器
          System.out.prinln("睡觉了"); 
          light.off(); 
          television.off(); 
          aircondition.off(); 
}

        
      
      }

**3. **客户端调用:爷爷使用智能遥控器的时候

public class Facade Pattern{ 
      public static void main(String[] args){
        {
            //实例化电器类
            SubSystemA_Light light = new SubSystemA_Light();
            SubSystemB_Television television = new SubSystemB_Television();
            SubSystemC_Aircondition aircondition = new SubSystemC_Aircondition();
            
            //传参
            Facade facade = new Facade(light,television,aircondition);
            
            //客户端直接与外观对象进行交互
            facade.on;
            System.out.prinln("可以看电视了"); 
            facade.off;
            System.out.prinln("可以睡觉了"); 

结果

起床了
打开了灯
打开了电视
打开了空调
可以看电视了

睡觉了
关闭了灯
关闭了电视
关闭了空调
可以睡觉了

通过上述这个常见的生活例子,我相信你已经完全明白了外观模式的原理了!!


3. 优缺点

在全面解析完后,我来分析下其优缺点:

3.1 优点

  • 降低了客户类与子系统类的耦合度,实现了子系统与客户之间的松耦合关系
  1. 只是提供了一个访问子系统的统一入口,并不影响用户直接使用子系统类
  2. 减少了与子系统的关联对象,实现了子系统与客户之间
    的松耦合关系,松耦合使得子系统的组件变化不会影响到它的客户。
  • 外观模式对客户屏蔽了子系统组件,从而简化了接口,减少了客户处理的对象数目并使子系统的使用更加简单。
  1. 引入外观角色之后,用户只需要与外观角色交互;
  2. 用户与子系统之间的复杂逻辑关系由外观角色来实现
  • 降低原有系统的复杂度和系统中的编译依赖性,并简化了系统在不同平台之间的移植过程

因为编译一个子系统一般不需要编译所有其他的子系统。一个子系统的修改对其他子系统没有任何影响,而且子系统内部变化也不会影响到外观对象。

3.2 缺点

  • 在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”
  • 不能很好地限制客户使用子系统类,如果对客户访问子系统类做太多的限制则减少了可变性和灵活性。

4. 应用场景

  • 要为一个复杂的子系统对外提供一个简单的接口
  • 提供子系统的独立性
  • 客户程序与多个子系统之间存在很大的依赖性

引入外观类将子系统与客户以及其他子系统解耦,可以提高子系统的独立性和可移植性。

  • 在层次化结构中,可以使用外观模式定义系统中每一层的入口

层与层之间不直接产生联系,而通过外观类建立联系,降低层之间的耦合度。


5. 与适配器模式的区别

  • 外观模式的实现核心主要是——由外观类去保存各个子系统的引用,实现由一个统一的外观类去包装多个子系统类,然而客户端只需要引用这个外观类,然后由外观类来调用各个子系统中的方法。
  • 这样的实现方式非常类似适配器模式,然而外观模式与适配器模式不同的是:适配器模式是将一个对象包装起来以改变其接口,而外观是将一群对象 ”包装“起来以简化其接口。它们的意图是不一样的,适配器是将接口转换为不同接口,而外观模式是提供一个统一的接口来简化接口

6. 总结

本文主要对外观模式进行了全面介绍,接下来将介绍其他设计模式,有兴趣可以继续关注Carson_Ho的最易懂的设计模式解析笔记!!!!



作者:Carson_Ho
链接:https://www.jianshu.com/p/1b027d9fc005
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

简单实例2:

假设我们有一个家庭影院系统,包含多个设备如投影仪、音响、DVD播放器等。每个设备都有自己的控制接口,但用户通常只需要执行一些常见的操作,例如“看电影”或“结束电影”。我们可以使用外观模式来简化这些操作。

#### 1. 定义子系统类

首先,定义各个子系统的类:

```cpp
#include <iostream>
#include <memory>

// 子系统类:投影仪
class Projector {
public:
    void on() const { std::cout << "Projector is on\n"; }
    void off() const { std::cout << "Projector is off\n"; }
    void wideScreenMode() const { std::cout << "Projector in widescreen mode\n"; }
};

// 子系统类:音响
class Amplifier {
public:
    void on() const { std::cout << "Amplifier is on\n"; }
    void off() const { std::cout << "Amplifier is off\n"; }
    void setDvd(DVDPlayer* dvd) { std::cout << "Amplifier setting DVD player\n"; }
    void setSurroundSound() const { std::cout << "Amplifier setting surround sound\n"; }
};

// 子系统类:DVD播放器
class DVDPlayer {
public:
    void on() const { std::cout << "DVD Player is on\n"; }
    void off() const { std::cout << "DVD Player is off\n"; }
    void play(std::string movie) const { std::cout << "DVD Player playing \"" << movie << "\"\n"; }
    void stop() const { std::cout << "DVD Player stopped\n"; }
};
```

#### 2. 创建外观类

接下来,创建一个外观类 `HomeTheaterFacade`,它封装了对上述子系统的调用:

```cpp
// 外观类:家庭影院外观
class HomeTheaterFacade {
private:
    std::unique_ptr<Projector> projector;
    std::unique_ptr<Amplifier> amplifier;
    std::unique_ptr<DVDPlayer> dvdPlayer;

public:
    HomeTheaterFacade() : 
        projector(std::make_unique<Projector>()),
        amplifier(std::make_unique<Amplifier>()),
        dvdPlayer(std::make_unique<DVDPlayer>()) {}

    // 提供简单的方法来观看电影
    void watchMovie(const std::string& movie) {
        std::cout << "Get ready to watch a movie...\n";
        projector->on();
        projector->wideScreenMode();
        amplifier->on();
        amplifier->setSurroundSound();
        amplifier->setDvd(dvdPlayer.get());
        dvdPlayer->on();
        dvdPlayer->play(movie);
    }

    // 提供简单的方法来结束电影
    void endMovie() {
        std::cout << "Shutting movie theater down...\n";
        dvdPlayer->stop();
        dvdPlayer->off();
        amplifier->off();
        projector->off();
    }
};
```

#### 3. 测试代码

最后,在 `main` 函数中测试外观类:

```cpp
int main() {
    // 创建家庭影院外观实例
    HomeTheaterFacade homeTheater;

    // 使用外观来观看电影
    homeTheater.watchMovie("Inception");

    // 使用外观来结束电影
    homeTheater.endMovie();

    return 0;
}
```

### 输出结果

当你运行这段代码时,你将看到如下输出:

```
Get ready to watch a movie...
Projector is on
Projector in widescreen mode
Amplifier is on
Amplifier setting surround sound
Amplifier setting DVD player
DVD Player is on
DVD Player playing "Inception"
Shutting movie theater down...
DVD Player stopped
DVD Player is off
Amplifier is off
Projector is off
```

### 总结

在这个例子中,`HomeTheaterFacade` 类作为外观,隐藏了启动和关闭家庭影院所需的所有复杂步骤。客户只需调用 `watchMovie` 和 `endMovie` 方法即可完成操作,而无需了解每个单独组件的具体工作方式。这不仅简化了客户的代码,也提高了系统的可维护性和扩展性。

外观模式非常适合用于以下场景:
- 当你需要为复杂子系统提供一个简单的接口时。
- 当你需要解耦客户端代码和子系统之间的依赖关系时。
- 当你需要在多个客户端之间提供一致的接口时。

希望这个例子能帮助你理解外观模式的应用。如果有任何问题或需要进一步的帮助,请随时告诉我!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值