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

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

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

带着这一疑问,我请教了我的老师,他给我的回答是:通过接口来分隔分离逻辑,就可以达到低耦合的效果。

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

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

/// <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
        {
            //登录失败
        }
    }
}

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

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

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

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

如果能有一个容器,能够在运行时(注1)判断使用哪个ILoginChecker类型,那就方便多了。

运行时:与"编译时"相对应,比如我说定义一个变量Int32 i,这个i的类型就是Int32,这是在编译时决定的,因为程序在由代码编译为程序时,已经可以确定i就是一个Int32类型了。

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

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

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

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

2、保安公司根据每个需求方的要求不同,配备不同的保安

3、保安公司拥有满足各种需求的保安

这样一来,我们就要再补充一个相当于是保安公司的类型了,用来创建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、分析当前方法要的主要事情

2、将可能存在变更的逻辑,建立接口,待以后实现

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

利与弊的权衡

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

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

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

时间: 2024-11-01 14:46:53

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

『中级篇』容器的技术概述(二)

容器的前世今生 ###物理机 部署非常慢购买服务器服务,放在IDC机房,各种走流程,很多流程不可控制流程慢. 成本非常高物理的服务器,高额的配置成本贵. 资源浪费资源太多了,针对app的服务可能利用率不够充分. 难于迁移和扩展迁移app端的服务器,我们要提前准备好一个新的物理服务器,环境需要重新的迁移.资源消耗比较大的话,用户增加比较快需要扩展内存,cpu,硬盘麻烦,可能最后还是选择购买新的物理服务器. 可能会被限定硬件厂家那些已经采用RISC架构或非x86平台的用户来说,要想体验到x86平台的

设计模式中聚合和组合--代码中的实现

●引言: 最近一直在看设计模式,以前写过一篇文章:设计模式中的关系在代码中的实现 之后他们问我说:聚合和组合在代码上怎样表现出生命期的不同.因为当时是真心不懂,不敢回答,现在有一点点心得,和大家分享一下. ●定义: 聚合:表示两个对象之间是整体和部分的弱关系,部分的生命周期可以超越整体.如电脑和鼠标. ?大话上的聚合: ?大话上的组合: 表示两个对象之间是整体和部分的强关系,部分的生命周期不能超越整体,或者说不能脱离整体而存在.组合关系的"部分",是不能在整体之间进行共享的. ●异同点

Android LCD(二):常用接口原理篇【转】

本文转载自:http://blog.csdn.net/xubin341719/article/details/9125799 关键词:Android LCD TFT TTL(RGB)  LVDS  EDP MIPI  TTL-LVDS  TTL-EDP平台信息:内核:linux2.6/linux3.0系统:android/android4.0 平台:samsung exynos 4210.exynos 4412 .exynos 5250 作者:xubin341719(欢迎转载,请注明作者)欢迎指

(转)LCD:LCD常用接口原理篇

关键词:android LCD TFT TTL(RGB)  LVDS  EDP MIPI  TTL-LVDS  TTL-EDP平台信息:内核:linux2.6/linux3.0系统:android/android4.0 平台:samsung exynos 4210.exynos 4412 .exynos 5250 作者:xubin341719(欢迎转载,请注明作者)欢迎指正错误,共同学习.共同进步! 下载链接:LCD规格书(404份),之前工作用用到的 . LCD规格书00  . LCD规格书0

Linux常用C函数-接口处理篇(网络通信函数)

接口处理篇accept,bind,connect,endprotoent,endservent,getsockopt,htonl,htons,inet_addr,inet_aton,inet_ntoa,listen,ntohl,ntohs,recv,recvfrom,recvmsg,send,sendmsg,sendto,setprotoent,setservent,setsockopt,shutdown,socket, accept(接受socket连线) 相关函数 socket,bind,l

第6篇 接口

接口是C#的一种引用数据类型.接口像是一个抽象类,可以定义方法成员,属性,索引器和事件等,但是接口不提供对成员的实现,继承接口的类必须提供接口成员的实现.         类用于描述的是事物的共性基本功能,接口用于定义的都是事物的额外功能. 一 接口的好处         规范性:定义接口像是在定义一种规范,当一个项目庞大复杂的时候,用接口去定义业务规范,这些接口可以用来告诉开发人员需要实现哪些具体的业务功能,并且连命名也被定义好了(可以防止开发人员任意取名).         多继承:多继承的

三、抽象类和接口的对比-----《大话设计模式》

一.抽象类 c#允许把类和方法声明为abstract,即抽象类和抽象方法.抽象类不能实例化,抽象方法必须被子类重写,如果类中包含抽象方法,那么类就必须定义为抽象类,不论是否还包含其他一般方法. 抽象类拥有尽可能多的共同代码,尽可能少的数据. abstract class Animal //加abstract关键字,表明是抽象类 { -- protected abstract string getShoutSound(); //在方法返回值前加abstract表明此方法是抽象方法. -- //抽象

opencv的C语言接口和C++接口差别(入门篇)

opencv是一个开源的图像处理库,最经典的1.0版本号提供的接口都是C语言接口. 后来的opencv2.x版本号保留了C语言接口,可是提供了C++接口,当中的C语言接口仅仅是为了向后兼容,而C++接口才是大势所趋. 那么这两者有什么差别呢?今天介绍一下配置过程中的差别. 以opencv2.3.0和vs2008为例.配置时.先要配置包括文件和库文件,然后在系统变量path中加入dll文件.最后再项目属性中加入附加依赖库.我平时用的功能不多.一般加入这几项opencv_core230.lib.op

opencv的C语言接口和C++接口区别(入门篇)

opencv是一个开源的图像处理库,最经典的1.0版本提供的接口都是C语言接口.后来的opencv2.x版本保留了C语言接口,但是提供了C++接口,其中的C语言接口只是为了向后兼容,而C++接口才是大势所趋.那么这两者有什么区别呢?今天介绍一下配置过程中的区别. 以opencv2.3.0和vs2008为例,配置时,先要配置包含文件和库文件,然后在系统变量path中添加dll文件,最后再项目属性中添加附加依赖库.我平时用的功能不多,一般添加这几项opencv_core230.lib,opencv_

Android中级篇之百度地图SDK v3.5.0-一步一步带你仿各大主流APP地图定位移动选址功能

定位+移动选址 百学须先立志-学前须知: 我们经常在各大主流APP上要求被写上地址,如百度外卖.爱鲜蜂收货地址等等:其中他们大多数是可以让我们在地图上移动选址.就如下面这段GIF演示的一样: 尽信书,不如无书-能学到什么? 1.地图状态MapStatus类及监听setOnMapStatusChangeListener 2.定位LocationClient类 3.反地理编码GeoCoder类 工欲善其事必先利其器-申请Key 百度地图访问应用(AK)申请地址:http://lbsyun.baidu