《设计模式》学习笔记8——外观模式

123人阅读 评论(0) 收藏 举报
分类:

定义

外观模式引用书中的定义如下:

为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
外观模式又称为门面模式,它是一种对象结构型模式。外观模式是迪米特法则的一种具体实
现,通过引入一个新的外观角色可以降低原有系统的复杂度,同时降低客户类与子系统的耦
合度。

理解

对于这个模式的理解,我觉得着重点在于几个词上,分别是一组接口统一入口降低复杂度
结合我们目前的系统进行说明,首先说什么是一组接口。
在我们的系统中,由于业务需要,有三种日志,分别是系统日志、告警日志、监控日志,我用非正式的代码简单表示如下:
系统日志类接口定义:

/**
 * 日志统一管理类,外观类
 * 
 * @author tzx
 * @date 2018年1月4日
 */
public class LogHandle {
    private SysLogService sysLogService = new SysLogService();
    private AlarmLogService alarmLogService = new AlarmLogService();
    private MonitorLogService monitorLogService = new MonitorLogService();

    public void error(String msg, Object... args) {
        sysLogService.error(msg, args);
        alarmLogService.error(msg, args);
        monitorLogService.error(msg, args);
    }
}

告警日志类接口定义:

/**
 * 告警日志接口
 * 
 * @author tzx
 * @date 2018年1月4日
 */
public class AlarmLogService {
    private Logger sysLog = LoggerFactory.getLogger(this.getClass());

    public void error(String msg, Object... args) {
        String sys = "alarm";
        msg = "#" + msg + "#" + sys + "#";
        sysLog.error(msg, args);
    }
}

监控日志类接口:

/**
 * 监控日志接口
 * 
 * @author tzx
 * @date 2018年1月4日
 */
public class MonitorLogService {
    private Logger sysLog = LoggerFactory.getLogger(this.getClass());

    public void error(String msg, Object... args) {
        String sys = "monitor";
        msg = "|" + msg + "|" + sys + "|";
        sysLog.error(msg, args);
    }
}

上边三个日志接口为了表示功能的不同,简单地对日志内容进行了不同的拼接,实际项目中自然不会这样。
这三个日志接口,都是属于日志的操作,只是打印出的日志内容被不同的系统采集再使用,而且他们基本上也都同时被使用到,所以可以说他们就是属于外观模式定义中的那一组接口
在没有使用外观模式的情况下,我们在业务代码中需要打印日志的时候可能就会是下边所示这样:

/**
 * 博客操作类
 * 
 * @author tzx
 * @date 2018年1月4日
 */
public class BlogService {
    private SysLogService sysLogService = new SysLogService();
    private AlarmLogService alarmLogService = new AlarmLogService();
    private MonitorLogService monitorLogService = new MonitorLogService();

    public void addBlog() {
        try{
        System.out.println("");
        }catch (Exception e) {
            sysLogService.error("系统异常{}", new Object[] { e.getMessage() });
            alarmLogService.error("系统异常{}", new Object[] { e.getMessage() });
            monitorLogService.error("系统异常{}", new Object[] { e.getMessage() });
        }
    }
}
/**
 * 用户操作类
 * 
 * @author tzx
 * @date 2018年1月4日
 */
public class UserService {
    private SysLogService sysLogService = new SysLogService();
    private AlarmLogService alarmLogService = new AlarmLogService();
    private MonitorLogService monitorLogService = new MonitorLogService();

    public void addUser() {
        try {
            System.out.println("");
        } catch (Exception e) {
            sysLogService.error("系统异常{}", new Object[] { e.getMessage() });
            alarmLogService.error("系统异常{}", new Object[] { e.getMessage() });
            monitorLogService.error("系统异常{}", new Object[] { e.getMessage() });
        }
    }
}

很显然,两个service中有大量重复的代码。这里只有两个类打印日志,就需要一共调用6次日志打印方法,每多出一个类需要打印这样的日志,就会以3的倍数增加代码。
而且这里的示例几乎是最简单的,在实际业务开发的时候可能就远不止三行。
同时,像上边这种做法,假如后续日志系统增多,需要增加到四种甚至五种或者更多,那个每个打印日志的类都需要进行相应的修改,很显然,这是极不利于维护和拓展的。
因此,按照常规的思路,可能就会有两种做法:
一种是把这种打印日志的代码进行提取,封装为一个方法进行调用。但是这种做法会存在一定的问题,首先,这个方法本身和业务是无关的,放在业务类中不合适。其次,除非所有需要打印日志的类都有一个共同的父类,把这个方法放在父类中,否则就需要定义多次该方法。
于是乎,就有了第二种做法,那就是把这段代码提取到另外一个类中,那么这个类就是所谓的外观类了:

/**
 * 日志统一管理类,外观类
 * 
 * @author tzx
 * @date 2018年1月4日
 */
public class LogHandle {
    private SysLogService sysLogService = new SysLogService();
    private AlarmLogService alarmLogService = new AlarmLogService();
    private MonitorLogService monitorLogService = new MonitorLogService();

    public void error(String msg, Object... args) {
        sysLogService.error(msg, args);
        alarmLogService.error(msg, args);
        monitorLogService.error(msg, args);
    }
}

有了这样一个类之后,我们的业务代码调用的时候就只需要一次就可以搞定,而这个管理日志操作的类就是统一接口

/**
 * 用户操作类
 * 
 * @author tzx
 * @date 2018年1月4日
 */
public class UserService {
    private LogHandle logHandle = new LogHandle();
    public void addUser() {
        try {
            System.out.println("");
        } catch (Exception e) {
            logHandle.error("系统异常{}", new Object[] { e.getMessage() });
        }
    }
}

与此同时,如果后续日志系统变化,不论是增加新的日志类型,还是删减旧的日志类型,都只需要改动logHandle类就可以了,而且每个调用日志的地方代码也都减少了很多,这就大大降低了系统的复杂度

要点

那么根据上边的理解和示例,外观模式有如下一些要点:
1. 需要有一组接口,他们几乎都是成套被使用;
2. 一个外观类或接口,实际操作上边的一组接口,而提供一个公开的接口方法供外部调用;
3. 客户端只需要一次调用外观类的接口,而不需要分别调用最开始那一组接口的每一个。

在没有学这个模式的时候,我觉得单例模式是最简单的设计模式,而现在来看,这个似乎才是最简单的设计模式。

demo源码可在github下载:https://github.com/tuzongxun/mypattern

查看评论

【设计模式】学习笔记10:外观模式(Facade)

上一次我们已经知道了适配器模式是如何将一个类的接口转换成另一个符合客户期望的接口了。Java中要做到这一点,必须将一个不兼容接口的对象包装起来,变成兼容的对象。 这次要学的是外观模式. 外观模式是将一...
  • shuangde800
  • shuangde800
  • 2013-08-13 00:42:12
  • 2754

设计模式练习(10)——外观模式

外观模式 一、题目:在计算机主机(MainFrame)中,只需要按下主机的开机按钮(on()),就可以调用其他硬件设备和软件的启动方法,如内存(Memory)的自检(check()),CPU的运行(r...
  • qq_33220449
  • qq_33220449
  • 2017-01-20 15:38:01
  • 648

最常用的设计模式---外观模式(C++实现)

外观模式:提供了一个统一的接口,用来访问子系统的一群接口。外观定义了一个高层接口,让子系统更容易使用。目地:让接口变得简单,是为了简化子系统的接口。...
  • lh844386434
  • lh844386434
  • 2014-01-09 17:25:22
  • 1787

【C#设计模式-外观模式】

一.概述:外观模式,为子系统中的一组接口提供一个一致的界面,定义一个高层接口,这个接口使得这一子系统更加容易使用。 二.结构:Facade这个外观类为子系统提供一个共同的对外接口,Clients客户对...
  • heyangyi_19940703
  • heyangyi_19940703
  • 2016-05-03 12:45:10
  • 1514

《Java设计模式》之外观模式

外观模式(Facade pattern)涉及到子系统的一些类。所谓子系统,是为提供一系列相关的特征(功能)而紧密关联的一组类。例如,一个Account类、Address类和CreditCard类相互关...
  • u011225629
  • u011225629
  • 2015-08-16 08:00:49
  • 1600

C#设计模式——外观模式

外观模式: 外观模式是很常用的模式,在软件开发过程中,客户端程序经常会与复杂系统的内部子系统进行耦合,从而导致客户端程序随着子系统的变化而变化,然而为了将复杂系统的内部子系统与客户端之间的依赖解耦,从...
  • hongwei15732623364
  • hongwei15732623364
  • 2016-02-02 19:44:34
  • 498

戏说设计模式(一)外观模式

0. 前言我发明了一个可以做菜的机器,它有一个菜单,可以给你做菜单里菜肴,如你选择菜单里的西红柿炒蛋,它就会给你来一盘西红柿炒蛋。你以为我在讲真?开玩喜,我只是想用这个例子讲一下外观模式(Facade...
  • TimHeath
  • TimHeath
  • 2016-11-25 16:32:46
  • 633

java/android 设计模式学习笔记(14)---外观模式

这篇博客来介绍外观模式(Facade Pattern),外观模式也称为门面模式,它在开发过程中运用频率非常高,尤其是在现阶段各种第三方 SDK 基本很大概率都会使用外观模式。通过一个外观类使得整个系统...
  • zhao_zepeng
  • zhao_zepeng
  • 2016-07-17 18:14:44
  • 3760

JAVA设计模之外观模式

Facade(外观)模式为子系统中的各类(或结构与方法)提供一个简明一致的界面,隐藏子系统的复杂性,使子系统更加容易使用。 使用facade以前假设有五个类:propressor, compiler,...
  • mishifangxiangdefeng
  • mishifangxiangdefeng
  • 2016-10-09 09:16:53
  • 347

Android设计模式(二十二)-外观模式

外观模式猛一听有点蒙逼,但是在开发中我们应该都用过,只是没这个概念罢了。比如在开发时通常会把图片加载框架和网络框架进行封装,封装到最后只暴露出来一个最上级的类供外部调用,外部调用这一个类提供的方法,然...
  • qq_25806863
  • qq_25806863
  • 2017-04-11 00:01:31
  • 891
    公告栏
    个人资料
    专栏达人 持之以恒
    等级:
    访问量: 89万+
    积分: 1万+
    排名: 1864