zoukankan      html  css  js  c++  java
  • 引入间接隔离变化(二)

    抽象建立的这层间接性,解除了调用者与实现类之间的具体依赖,使得实现类可以单独变化,而不会影响到调用者。例如,当我们需要为元数据的读取操作定义对象时,好的编码习惯是为其定义一个接口:

    public interface MetadataReaderService {
    public MetadataObject getMetadataObject(String metadataName);
    public MetadataField getMetadataField(
    String tableName,String fieldName);
    public MetadataRelation getMetadatarelation(
    long objectId,long relateObjectId);
    }

    MetadataReaderServiceImpl类实现了MetadataReaderService接口,在实现中通过注入数据访问对象,完成对元数据信息的读取:

    public class MetadataReaderServiceImpl implements 
    MetadataReaderService {

    private MetadataObjectDAO metadataObjectDAO;
    private MetadataFieldDAO metadataFieldDAO;
    private MetadataRelationDAO metadataRelationDAO;

    public MetadataObjectDAO getMetadataObjectDAO() {
    return metadataObjectDAO;
    }
    public void setMetadataObjectDAO(
    MetadataObjectDAO metadataObjectDAO) {
    this.metadataObjectDAO = metadataObjectDAO;
    }
    public MetadataFieldDAO getMetadataFieldDAO() {
    return metadataFieldDAO;
    }

    public MetadataObject getMetadataObject(String metadataName) {
    return this.metadataObjectDAO.getMetadataObject(metadataName);
    }
    }

    为何一定要定义MetadataReaderService接口?因为接口可以使得它与调用者的依赖降到最弱。例如调用者QuerySqlStatementImpl类:

    public class QuerySqlStatementImpl {
    public MetadataReaderService getMetadataReader() {
    return this.metadataReaderService;
    }

    public void setMetadataReader(MetadataReaderService reader) {
    this.metadataReaderService = reader;
    }

    private String generateJoinStatement(TableNode rootTableNode){
    String metadataName = rootTableNode.getMetadataName();
    MetadataObject rootMetadataObject =
    this.getMetadataReader().getMetadataObject(metadataName);
    }
    }

    imageQuerySqlStatementImpl与MetadataReaderServiceImpl之间不存在任何依赖关系,它只依赖于MetadataReaderService接口,而该接口是抽象的,因而是相对稳定的。例如,我们考虑到系统中的元数据信息较为稳定,但却可能被频繁地读取。为了提高系统的性能,有必要引入元数据信息的缓存。

    显然,访问元数据信息的实现发生了变化。它不再是直接通过数据访问对象读取存储在数据库中的元数据信息,而是先读取内存中的缓存。如果缓存中的元数据已经存在,则直接返回,否则,访问数据库。现在,我们可以修改MetadataReaderServiceImpl的实现,却不会影响到调用者QuerySqlStatement,因为MetadataReaderService接口没有发生任何变化。

    不过,根据封装的原理,即使没有定义MetadataReaderService接口,在修改MetadataReaderServiceImpl类时,只要我们不改变它所公开的方法,即使调用者依赖于它,似乎也不会受到影响才对。这样的推断并没有错误,错在我们假设的前提。假若我们直接修改MetadataReaderServiceImpl类的实现,如果面对如下两个问题,应该如何应对?
    1、 MetadataReaderServiceImpl类的源代码不允许修改;
    2、 读取元数据信息的其他调用者不需要缓存功能。

    第一个问题锁上了修改的大门,第二个问题又添上了另一把锁。它明确地告诉那些希望走捷径的设计者:此路不通!或许,禁止修改源代码的需求过于苛刻,除非使用第三方代码;然而,第二个问题在实际的项目开发中确实存在。我们在实现元数据信息的编辑器时,同样需要调用MetadataReaderServiceImpl类。由于编辑器主要针对元数据进行CRUD操作,因此,元数据信息的变化是很频繁的,此时使用缓存,无异于画蛇添足。

    根据OCP原则,好的设计应该对修改是封闭的,对扩展是开放的。接口的引入使得扩展成为可能。我们可以定义一个缓存对象去实现MetadataReaderService接口,它提供了缓存的功能。缓存对象除了拥有管理和访问缓存的职责之外,它还需要读取数据库,以应付缓存未包含指定信息的情况。我们需要在缓存对象中再提供读取数据库的实现逻辑吗?设计与开发切忌浪费,信奉“拿来主义”。在实现某个功能之前,首先应考察是否已有现成的实现,如果有,直接拿来重用即可。目前,MetadataReaderServiceImpl类已经实现了读取数据库的功能,因此,最简单的办法就是将其传递给缓存对象。考虑到MetadataReaderServiceImpl才是真正实现读取的对象,而缓存对象不过是对其包裹了一层外衣而已,为了更好地表达对象的意图,最好将MetadataReaderServiceImpl更名为RealMetadataReaderService,缓存对象则命名为CachedMetadataReaderService:image

    public class CachedMetadataReaderService implements 
    MetadataReaderService{
    private MetadataReaderService readerService;
    private MetadataCacheManager cacheManager;

    @Override
    public MetadataObject getMetadataObject(String metadataName) {
    Serializable obj = cacheManager.getObjectCached(metadataName);
    if(obj!=null){
    return (MetadataObject) obj;
    }else{
    MetadataObject metadataObject = readerService.
    getMetadataObject(metadataName);
    cacheManager.putObjectCached(metadataName,
    (Serializable) metadataObject);
    return metadataObject;
    }
    }
    }

    CachedMetadataReaderService类相当于RealMetadataReaderService的代理,由它决定是从缓存获取,还是从数据库中获取。现在,我们可以让调用者访问CachedMetadataReaderService类。因为它与RealMetadataReaderService同属于一个共同的接口,所以调用者察觉不到变化。为了解除创建具体对象的依赖,我们还可以使用IoC容器,利用依赖注入的方式注入调用者需要的对象。正是因为MetadataReaderService接口的存在,当面对需求发生变化时,我们只做了两件事:扩展增加了CachedMetadataReaderService类;修改Spring的配置文件。对于原有的代码,无论是服务的提供者还是调用者,都没有受到任何影响。接口引入的间接性,无疑是能够拥抱变化的。

  • 相关阅读:
    strdup和strndup函数
    c# 获取客户端IP地址方法
    The 'Microsoft.Jet.OLEDB.4.0' provider is not registered on the local machine.报错解决办法
    C#将Excel数据表导入SQL数据库的两种方法(转)
    Winform 无法监听方向键(向上,向下,向左,向右)
    一个优秀的.net程序员必须要学会的技能 (转)-----参照学习目标
    iTextSharp 使用详解(转)
    mac 快捷键
    mvc 项目运行报错检查web.config
    C语言堆栈入门——堆和栈的区别 -- 转
  • 原文地址:https://www.cnblogs.com/wayfarer/p/1937140.html
Copyright © 2011-2022 走看看