什么是接口(中级篇)——接口在设计模式中的使用(二)

还记得个人软件工程老师是这么说的:软件应该往高内聚,低耦合的方向进行设计。html

当时,还身为一个初学者的我,不太明白老师的这句话——既然面向对象提供给了咱们”继承“这种高耦合的概念,那为什么咱们还要低耦合高内聚呢?难道放着继承的概念不用,而改成面向过程吗?数据库

带着这一疑问,我请教了个人老师,他给个人回答是:经过接口来分隔分离逻辑,就能够达到低耦合的效果。架构

 

咱们来回顾一下前一篇所学习的"控制反转"设计思想,其实质就是上面所说的"经过接口来分离逻辑"。函数

 

但以上一篇的示例来讲,达到这个目标,还有一些欠缺,首先让咱们来回顾一下上一篇的代码内容学习

/// <summary>
/// 这是一个负责登陆的接口类型
/// </summary>
public interface ILoginChecker
{
    /// <summary>
    /// 登陆,须要的参数是用户名和密码,须要返回一个bool类型表示登陆是否成功
    /// </summary>
    /// <param name="loginName"></param>
    /// <param name="password"></param>
    /// <returns></returns>
    bool Login(string loginName, string password);
}

public class LoginWindow : Form
{
    private ILoginChecker loginChecker;
    
    /// <summary>
    /// 经过构造函数决定使用哪一个ILoginChecker来负责登陆流程
    /// </summary>
    /// <param name="chcker"></param>
    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
        {
            //登陆失败
        }
    }
}

 

确定会有一些人带着这样的疑问:在代码的某个地方必定写着类型这样的内容:this

///实例化某个实现了ILoginChecker接口的类型
ILoginChecker checker = new XXXLoginChecker();
LoginWindow window = new LoginWindow(checker);
window.Show();

那么随着之后功能的更新,在不断地修改这里的ILoginChecker checker = new XXXLoginChecker();spa

时间久了,记不得这段代码写在哪儿,也是很头疼的一件事,最重要的是,依然没达到”易维护“的效果,和以前相同,每次的须求改更,或是环境变化,都须要重写这句话。设计

若是能有一个容器,可以在运行时(注1)判断使用哪一个ILoginChecker类型,那就方便多了。code

 

运行时:与"编译时"相对应,好比我说定义一个变量Int32 i,这个i的类型就是Int32,这是在编译时决定的,由于程序在由代码编译为程序时,已经能够肯定i就是一个Int32类型了。orm

再好比说我定义一个变量Object obj,这个obj的类型虽然是Object,可是会根据实际的赋值,发生类型的变换,而赋的什么值给它,程序在编译时是没法得知的,只有在运行到这里的时候才能知道,这就叫运行时。

 

咱们来假设一个现实场景:

场景中,咱们把ILoginChecker这个接口

一、门卫是由保安公司派出的

二、保安公司根据每一个需求方的要求不一样,配备不一样的保安

三、保安公司拥有知足各类需求的保安

 

这样一来,咱们就要再补充一个至关因而保安公司的类型了,用来建立ILoginChecker实例

因此咱们起个名字叫LoginCheckerFactory

里面只有一个主要方法:CreateLoginChecker,根据咱们指定的名称,返回一个具体的IloginChecker实例,咱们来看一下示例代码:

class LoginCheckerFactory
{
    public ILoginChecker CreateLoginChecker(string checkerName)
    {
        switch (checkerName)
        {
            case "Database": return new DatabaseLoginChecker(); //由数据库完成的登陆验证
            case "WebService": return new WebServiceLoginChecker(); //由WebService完成的登陆验证
            case " TCP": return new TCPLoginChecker(); //由TCP通信完成的登陆验证
            default: throw new ApplicationException("不存在这个名称的Checker实例");
        }
    }
}

 

此时,你会发现,登陆的断定流程是依赖于一个字符串,Database或WebService或TCP。到了这一步,我想大部分人都明白,这个字符串只要写在配置文件里,就大功告成了。

而后项目发布,在不一样的运行环境中间,我只须要改一下配置文件,就能实现各类方式的登陆了。

 

对于更新与维护是一样的便捷,你能够把不一样版本的ILoginChecker都放在这个工厂里,而后根据外部的一些版本号来更改实例。

在BUG的修正中,你也没必要直接修改类型自己,能够拷备一个出来,好比叫WebServiceLoginChecker2,这样即保证了程序原有的可运行性,又能够进行BUG的修正,一旦出现了”动一发触全身“的状况,也很是容易全身而退。

 

这样的设计思想、模式,咱们将其称之为工厂模式。工厂模式还分为简单工厂模式和抽象工厂模式,可是其最核心的思想,就是建立一个工厂,由工厂在运行时,进行动态的实例建立、返回。大大降底的类与类、模块与模块之间的耦合度,为更新与维护提供了很是好的隔离环境。

 

小结

经过第1、第二篇文章的学习,一种初步的构架思路已经产生。

一、分析当前方法要的主要事情

二、将可能存在变动的逻辑,创建接口,待之后实现

三、考虑到上述接口的实现多样性,创建工厂类型,由工厂类型负责建立接口实例

 

利与弊的权衡

从作产品的角度考虑,一个好的基本构架是产品最核心的保障,有了这样的保障,能够说,除非到你换语种的那一天,不然永远不会存在(推倒重来)的那一天,由于你的每个环节都被低耦合了,它们所有均可以被单独替换。

从作项目的角度考虑,一个好的基本构架是项目中最耗时间、最耗成本的阶段。在面向结果的项目负责人眼中,相对于可维护性进度才是最重要的,因此在作项目这种状况下,对使用架构应该进行一个速与质的权衡。

 

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

相关文章
相关标签/搜索