首页 > 技术文章 > 什么是接口(中级篇)——接口在设计模式中的使用(一)

ShimizuShiori 2015-10-30 15:45 原文

设计模式——一个会让很多人觉得高大上的名词。

实际上,设计模式并没有什么高深的知识在里面,它只是一种思想,使用这种思想,可以使得你的系统更容易被更新、维护,尽可能得避免了所谓的”动一发触全身“的被动状态。

我们把上面的话反过来看一下,就变成另一个意思了:

我们尽可能得使得系统变得容易更新、容易维护,我们使用各种办法使得”动一发触全身“的被动状态发生率降至最低的思路,就是设计模式的原出发点了。

 

关于设计模式的文章,网络上也是大把大把的,一般都会按照它们总结出来的各种模式一一介绍,比如用一篇文章介绍一下工厂模式(公认最简单的模式)。

但是我不认为设计模式是真有着明显的这个或者那个模式,而是一种思想,只要使用到了,就是好的。

我将顺着”什么是接口”,这系列的教学,带大家正式进入“设计模式”的教学篇,在这里,我们不是生硬得将设计模式拆开后一个一个讲解。而是将分析的思路慢慢溶入每一行代码中,自然而然的形成了各种设计模式。

 

首先,我们来看看这个例子:

对于一个做过几个项目的人来说,让他们去完成任何平台(窗体、网页等)上的登录行为,那是太容易不过的事了。

他们的思路肯定是这样的:

1、一个方法:接受参数 用户名,密码

2、拿这两个参数拼一个SQL语句

3、判断是否有查询结果

3-Y、记录下用户ID(以备后用),再加上一些其它操作,跳转窗体、页面,登录完成

3-N、提示用户”用户名或密码错误“

“很好,这样就完成了,太简单不过了” —— by 无名的程序员

 

唔,确实,从功能上来说,确实完成的非常好

然后我们来假设一些可能会遇到的情况:

 

1、要求单点登录,即一个用户不可以登录两次

“这好办,在判断登录成功后,再加一堆代码,判该这个用户ID是否在线,如果不在线才可以登录;如果在线提醒他” —— by 无名的程序员

唔,很轻松地解决了这个问题,很好。我们继续

 

2、你的上司希望这个单点登录是个可选的逻辑

"这依然简单,在配置文件写一个开关,在(1)添加的代码前加判断,小轻松~~" —— by 无名的程序员

唔,解决的很漂亮,以后只要改改配置文件,什么都不是问题了

 

3、项目越做越大,不光有窗体,还有网站,还有手机,我们不能一个一个写登录了,需要统一一个WebService控制所有程序

"唔,这次我学乖了,还是先做一个配置开关吧,顺便把WebService的URL也存进去,表示是否使用统一登录机制,然后利用分支来进行不同的登录流程" —— by 有长进的无名程序员

不得不说,变厉害了。

 

4、由于重大版本的更新,表结构变了……

5、由于重大版本的更新,WebService协议变了——

6、登录流程出现了BUG

"哎呀,登录流程有这么多分支,到底哪出BUG了" —— by 抓头的无名程序员

 

随着产品的不断提升,需求在变,功能也在变,堆着一堆配置项和一堆switch代码,时间久了,自己都不一定能管理好这些思路,更不要说,这只是登录。

现在我们设想一下:

我们现在站在一个豪宅的大门口,我们想进去 —— 登录

我们只需要给门卫看一下脸、报一下名字、出示一下门卡 —— 用户名 + 密码

门卫去后面干了些你不知道的事情 —— 登录流程

门卫告诉你可以进、或是不可以进 —— 登录结果

 

有没有发现一个很关键的事情? —— 登录流程我们不关心

对我们手上的这段代码来说,最关心的是如果登录成功干什么,如果登录失败干什么。

至于如何验证,按理说,不是这时候考虑的,应该有一个专门的"东西"负责这件事,至于这个东西怎么完成的,和我没关系。

这个概念,恰恰好与接口想吻合 —— 一个只提供了方法签名、属性、事件,但不去实现其功能的最抽象的类型

public interface ILoginChecker
{
    bool Login(string loginName,string password);
}

在你的代码中只需要声明一个ILoginChecker成员,并通过构造函数来决定它的具体类型,在程序中直接使用它即可。

public class LoginWindow : Form
{
    private ILoginChecker loginChecker;

    public LoginWindow(ILoginChecker chcker)
    {
        this.loginChecker = chcker;
    }

    void LoginButton_Click(object sender, EventArgs e)
    {
        string loginName = null, password = null;
        //从控件上取值
        //判断空值
        //等一切OK时
        if (this.loginChecker.Login(loginName, password))
        {
            //登录成功
        }
        else
        {
            //登录失败
        }
    }
}

我们来总结一下思路,把不关心的、可变的、可调整、可维护的内容,先不去实现,写成一个接口,直接使用这个接口上的方法,并把接口的填充交给构造函数(这一步不是必须)

我们把这个思路叫作 —— "控制反转",简称IOC

 

控制反转,是在大型项目中最常见存在的一种模式。Java中最有名的框架Spring其核心就是一个控制反转容器。

本次介绍的只是控制反转中最核心的思想,示例代码尚不能作为一个优秀的控制反转示例。

 

文章为作者原创,转载请注明出处,谢谢 http://www.cnblogs.com/ShimizuShiori/p/4923427.html

推荐阅读