Java设计模式----外观模式(Facade)

1.什么是外观模式

外观模式是一种使用频率非常高的结构型设计模式,它通过引入一个外观角色来简化客户端与子系统之间的交互,为复杂的子系统调用提供一个统一的入口,降低子系统与客户端的耦合度,且客户端调用非常方便。

这样说明有点教条化,举一个在网上看到的例子吧:

不知道大家有没有比较过自己泡茶和去茶馆喝茶的区别,如果是自己泡茶需要自行准备茶叶、茶具和开水,如图1(A)所示,而去茶馆喝茶,最简单的方式就是跟茶馆服务员说想要一杯什么样的茶,是铁观音、碧螺春还是西湖龙井?正因为茶馆有服务员,顾客无须直接和茶叶、茶具、开水等交互,整个泡茶过程由服务员来完成,顾客只需与服务员交互即可,整个过程非常简单省事,如图1(B)所示。

在软件开发中,有时候为了完成一项较为复杂的功能,一个客户类需要和多个业务类交互,而这些需要交互的业务类经常会作为一个整体出现,由于涉及到的类比较多,导致使用时代码较为复杂,此时,特别需要一个类似服务员一样的角色,由它来负责和多个业务类进行交互,而客户类只需与该类交互。外观模式通过引入一个新的外观类(Facade)来实现该功能,外观类充当了软件系统中的“服务员”,它为多个业务类的调用提供了一个统一的入口,简化了类与类之间的交互。在外观模式中,那些需要交互的业务类被称为子系统(Subsystem)。如果没有外观类,那么每个客户类需要和多个子系统之间进行复杂的交互,系统的耦合度将很大,如图2(A)所示;而引入外观类之后,客户类只需要直接与外观类交互,客户类与子系统之间原有的复杂引用关系由外观类来实现,从而降低了系统的耦合度,如图2(B)所示。


外观模式中,一个子系统的外部与其内部的通信通过一个统一的外观类进行,外观类将客户类与子系统的内部复杂性分隔开,使得客户类只需要与外观角色打交道,而不需要与子系统内部的很多对象打交道。

      外观模式定义如下:

外观模式:为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

Facade Pattern: Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.

      外观模式又称为门面模式,它是一种对象结构型模式。外观模式是迪米特法则的一种具体实现,通过引入一个新的外观角色可以降低原有系统的复杂度,同时降低客户类与子系统的耦合度。

2.外观模式的实现

FileInput.java
package cn.limbo.facade;

import java.io.*;

/**
 * 实现从文件中读取明文
 * 单一职责原则
 * Created by limbo on 2016/12/1.
 */
public class FileInput {

    /**
     * 从文件中读入
     *
     * @param filePath 文件全路径
     * @return 文件内容
     */
    public static String read(String filePath) {
        File file = null;
        BufferedReader reader = null;
        StringBuilder sb = new StringBuilder();
        String line = null;
        try {
            file = isExist(filePath);
            reader = new BufferedReader(new FileReader(file));
            while((line = reader.readLine()) != null){
                sb.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        System.out.println("读入明文: " + sb.toString());
        return sb.toString();
    }


    /**
     * 判断文件是否存在,存在则返回文件本省,不存在则新建文件
     *
     * @param filePath 文件路径
     * @return 已存在的文件或者是新建的文件
     */
    private static File isExist(String filePath) throws IOException {
        File file = new File(filePath);

        if (!file.exists()) {
            file.createNewFile();
        }

        return file;
    }

}

FileOutput.java
package cn.limbo.facade;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

/**
 * 将密码写入到文件中
 * 单一职责原则
 * Created by limbo on 2016/12/1.
 */
public class FileOutput {

    /**
     * 将密文写入到文件中
     * @param cipher 密文
     * @param filePath 密文存储路径
     */
    public static void write(String cipher, String filePath){

        File file = null;
        FileWriter writer = null;
        try {
            file = isExist(filePath);
            writer = new FileWriter(file);
            System.out.println("写入文件,保存密文");
            writer.write(cipher);
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }


    /**
     * 判断文件是否存在,存在则返回文件本省,不存在则新建文件
     *
     * @param filePath 文件路径
     * @return 已存在的文件或者是新建的文件
     */
    private static File isExist(String filePath) throws IOException {
        File file = new File(filePath);

        if (!file.exists()) {
            file.createNewFile();
        }

        return file;
    }

}

CipherMachine.java
package cn.limbo.facade;

/**
 * 加密类的接口
 * Created by limbo on 2016/12/1.
 */
public interface CipherMachine {

    //加密类的操作
    public String doEncrypt(String plainText);

}
Mode7CipherMachine.java
package cn.limbo.facade;

/**
 * Created by limbo on 2016/12/1.
 */
public class Mode7CipherMachine implements CipherMachine {
    @Override
    public String  doEncrypt(String plainText) {
        String result = "";

        char[] chars = plainText.toCharArray();

        System.out.println("正在加密...");
        //模7运算
        for (char ch : chars) {
            result += String.valueOf((ch % 7));
        }

        System.out.println("得到密文: " + result);
        //返回密文
        return result;
    }
}
ShiftCipherMachine.java
package cn.limbo.facade;

/**
 * 移位加密法
 * Created by limbo on 2016/12/1.
 */
public class ShiftCipherMachine implements CipherMachine {

    //偏移量
    private int shift;

    public ShiftCipherMachine(int shift) {
        this.shift = shift;
    }

    @Override
    public String doEncrypt(String plainText) {
        String result = "";
        char[] chars = plainText.toCharArray();

        int temp = 0;

        System.out.println("正在加密...");
        for (char c : chars) {
            temp = (int) c;

            //小写字母移位
            if (c >= 'a' && c <= 'z') {
                temp += shift % 26;
                if (temp > 122)
                    temp -= 26;
                if (temp < 97)
                    temp += 26;
            }

            //大写字母移位
            if (c >= 'A' && c <= 'Z') {
                temp += shift % 26;
                if (temp > 90)
                    temp -= 26;
                if (temp < 65)
                    temp += 26;
            }
            result += (char)temp;
        }
        System.out.println("得到密文: " + result);
        return result;
    }

}

Facade.java
package cn.limbo.facade;

/**
 * 外观类,说明如下:
 * 实现一个加密模块 该模块由从文件中读取,加密文件内容,将加密内容输出三个部分组成
 * Created by limbo on 2016/12/1.
 */
public class Facade {

    //对于加密的方式有很多选择,所以这里主要使用一个加密机的子类,从而实现可以灵活配置的优点
    private CipherMachine cipherMachine;

    public Facade(CipherMachine cipherMachine) {
        this.cipherMachine = cipherMachine;
    }

    public void doEncrypt(String inputFilePath, String outputFilePath){
        String plainText = FileInput.read(inputFilePath);
        String cipher = cipherMachine.doEncrypt(plainText);
        FileOutput.write(cipher,outputFilePath);
    }

}

测试类:FacadeTest.java
package test;

import cn.limbo.facade.Facade;
import cn.limbo.facade.ShiftCipherMachine;
import org.junit.Test;

/**
 * Created by limbo on 2016/12/1.
 */
public class FacadeTest {

    @Test
    public void show(){
        Facade facade = new Facade(new ShiftCipherMachine(10));
        facade.doEncrypt("input.txt","output.txt");
    }

}

测试用例和结果


3.优点与缺点

外观模式的主要优点如下:

       (1) 它对客户端屏蔽了子系统组件,减少了客户端所需处理的对象数目,并使得子系统使用起来更加容易。通过引入外观模式,客户端代码将变得很简单,与之关联的对象也很少。

       (2) 它实现了子系统与客户端之间的松耦合关系,这使得子系统的变化不会影响到调用它的客户端,只需要调整外观类即可。

       (3) 一个子系统的修改对其他子系统没有任何影响,而且子系统内部变化也不会影响到外观对象。


外观模式的主要缺点如下:

       (1) 不能很好地限制客户端直接使用子系统类,如果对客户端访问子系统类做太多的限制则减少了可变性和灵活 性。

       (2) 如果设计不当,增加新的子系统可能需要修改外观类的源代码,违背了开闭原则。

4.使用场景

在以下情况下可以考虑使用外观模式:

       (1) 当要为访问一系列复杂的子系统提供一个简单入口时可以使用外观模式。

       (2) 客户端程序与多个子系统之间存在很大的依赖性。引入外观类可以将子系统与客户端解耦,从而提高子系统的独立性和可移植性。

       (3) 在层次化结构中,可以使用外观模式定义系统中每一层的入口,层与层之间不直接产生联系,而通过外观类建立联系,降低层之间的耦合度。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值