面向接口编程解析

来源:互联网 发布:周扬青家境知乎 编辑:程序博客网 时间:2024/05/29 03:03

相信经常使用设计模式的码农对面向接口编程已经有一定的了解,因为在各种设计模式当中,对面向接口的各种应用可谓是发挥得淋漓尽致。

什么是面向接口编程呢?我个人的定义是:在系统分析和架构中,分清层次和依赖关系,每个层次不是直接向其上层提供服务(即不是直接实例化在上层中),而是通过定义一组接口,仅向上层暴露其接口功能,上层对于下层仅仅是接口依赖,而不依赖具体类。

这样做的好处是显而易见的,首先对系统灵活性大有好处。当下层需要改变时,只要接口及接口功能不变,则上层不用做任何修改。甚至可以在不改动上层代码时将下层整个替换掉,实现了程序的可插拔性,对于不同的需求切换不同的实现,降低了耦合度,随着系统复杂性的提高这个优势会越来越明显。

我们都知道造电脑主板的不可能等CUP造好再开工,也不用等显示器先造好的,是因为各厂商只需要把自己的产品接口按预先规定的设定来建造就可以实现跟其它部件进行对接了,所以使用接口的另一个好处就是不同部件或层次的开发人员可以并行开工,只要接口一致,设计合理,完全可以并行进行开发,从而提高效率。

说到面向接口,那肯定就会联想到面向对象了,他们两个的关系不是敌对的,而是附属的。首先,面向接口编程和面向对象编程并不是平级的,它并不是比面向对象编程更先进的一种独立的编程思想,而是附属于面向对象思想体系,属于其一部分。或者说,它是面向对象编程体系中的思想精髓之一。

接下来从模式或架构中了解这种编程方式的优势和这种思想的内涵。

一、从大家都比较熟悉的MVC开始

MVC简介:本文不打算详细解释MVC架构,而是把重点放在其中的面向接口思想上。所以在这里,只对MVC做一个简略的介绍。MVC是一种用于表示层设计的复合设计模式。M、V、C分别表示模型(Model)、View(视图)、Controller(控制器)。它们的职责如下:

模型:用于存储应用中的数据及运行逻辑,是应用的实体。

视图:负责可视部分,用于与用户交互及呈现数据。视图只负责显示,不负责将用户的操作行为解释给模型。

控制器:负责将用户的行为解释给模型。根据指定的策略和用户的操作,调用模型的逻辑。

它们之间的交互有以下几种:

1.当用户在视图上做任何需要调用模型的操作时,它的请求将被控制器 截获。

2.控制器按照自身指定的策略,将用户行为翻译成模型操作,调用模型相应逻辑实现。

3.控制器可能会在接到视图操作时,指定视图做某些改变。

4.当模型的状态发生改变时,将通过某种方式通知视图。

5.视图可以从模型获取状态,从而改变自己的显示。

接下来就是我们的主题了

1.首先我们可以看到,视图和模型是有直接交互的,也就是上面的4、5两点。这就奇怪了:这两个不同层面的业务,都有自己各自的封装,甚至是由不同人员开发管的,即它们相互并不知道对方是做什么的、有什么属性、有什么方法,但是它们能交互。这是怎么做到的呢?因为它们个各知道对方实现了某一接口。此乃面向接口思想一大作用:使相互不认识的类进行交互。这样做是很有好处的,首先它们之间的耦合度大大降低,其次双方都可以进行替换,只要实现了相同的接口,就没有问题。

这里,要引入一个设计模式,叫观察着(Observer)模式。整个模式中有两种实体:观察者和被观察者,它们分别实现一个接口,这里我们姑且叫做IObserver与IObserverSubject。IObserver只有一个方法,例如叫Update,当被观察者状态改变时,调用这个方法,用来通知观察者。IObserverSubject接口有两个方法,都是供观察者调用。一个用来将观察者注册为此被观察者的观察对象,另一个用于将观察者移除。一般情况下,一个被观察者对应多个观察者,耦合的双方都依赖于抽象而不是依赖于具体实现,这样使得各自实现的改变都不会导致另一方需要修改实现。

在MVC中,视图是观察者,模型是被观察者,当模型状态改变时,调用所有观察者的Update方法,通知视图模型有变,视图在Update方法里写下响应代码,完成操作。通过这个方法,视图和模型就可以在仅依赖接口的情形下进行交互,而不必强耦合,而且在模型不变的情况下,视图可以随意替换。(只要实现了IObserver)

2. 在MVC中另一个使用接口的地方就是控制器,这里要首先引入一个设计模式:策略模式(Strategy)。在MVC中,控制器就使用了这个模式。

刚才说过,视图负责与用户交互,但是,它只负责界面显示部分,至于当用户做了某个操作(如单击某个按钮)后系统应该怎么反应,视图并不负责,它只是将这个动作交给控制器,控制器根据内置的策略,将用户操作翻译成模型的逻辑。这就是说,同一个视图、同一种操作,模型可以做出不同的反应,这取决与控制器的内置策略。所以,我们的系统中可以有很多控制器,它们有不同的策略,当视图希望改变策略时,它可以更换控制器。怎么实现呢?这就需要视图不能和具体控制器耦合,而是要仅依赖一个控制器接口(如IController),并聚合一个IController的实例。当希望更改策略时,可以在系统运行时动态更换Controller,这就是策略模式的实现。

关于MVC的接口思想就先介绍到这里。其实MVC中还有很多地方用到面向接口,由于本文不是专门介绍MVC或设计模式的,所以对用到的模式没有做详解,而是把重点放在其中的面向接口思想上。如果没有设计模式的基础,读上文可能会有些困难,建议可以找些关于设计模式的书籍阅读,因为设计模式也是日常开发中相当重要的一部分知识。

二、分层架构的面向接口思想

我们知道,在做大一点的系统应用时(特别是B/S架构),比较好的方法是分层架构。所谓分层架构,是指将系统从职责上分成若干层,每层各司其职,上层依赖下层完成操作。

比较经典的分层架构是三层架构,从下到上依次是:数据访问层、业务逻辑层、表示层。各层职责如下:

数据访问层:负责与数据源交互,完成数据访问等一系列操作。

业务逻辑层:完成与系统业务有关的逻辑操作。

表示层:负责与用户交互、呈现数据等一切与系统表示有关的操作。

刚才我们说过,分层架构是向下依赖的,也就是业务逻辑层要调用数据访问层完成与数据源有关的操作,而表示层调用业务逻辑层完成业务逻辑工作。但是,表示层对数据访问层是没有依赖的。

在这个架构中,每一层都不是一个类,而是一个类族,例如,在一个CMS系统中,数据访问层可能会有一系列的类,分别负责用户、文章、评论等业务实体的数据访问操作,而业务逻辑层也一样。如果我们直接依赖,即业务逻辑层实例化数据访问层的类,表示层再实例化业务逻辑层的类,会造成强耦合。如果我想把数据库从SQLServer换成MySQL,则要改变整个业务逻辑层代码,这是个不好的设计。(还记得“开放-关闭”原则吗)所以,一般的做法是,为数据访问层和业务逻辑层分别定义一族接口,业务逻辑层不依赖具体的数据访问层,而是仅依赖数据访问层的接口族,表示层也一样,依赖业务逻辑层的接口族。如此一来,当要更换数据库时,我们就不必改写整个业务逻辑层,因为业务逻辑层里根本没有任何数据访问层中的具体类,而全是通过接口实现的。只要配合配置文件和反射机制,再运用Abstract Factory设计模式,就可以实现“依赖注入”,即在不改动代码的情况下根据配置选择相应的层次组件。这样,我们就可以为不通数据库分别实现数据访问层,也可以编写ORM的数据访问层,甚至是基于XML的,只要实现了数据访问层接口族,就可以和业务逻辑层无缝连接,从而极大提高了软件的灵活性和可维护性。当然要更改业务逻辑层也是一样。

第一个例子是从微观的角度对面向接口编程进行解析,第二个例子则是从宏观的角度来解析 。

最后欢迎各位大神发表自己的见解

原创粉丝点击