一个体现Java接口及工厂模式优点的例子

本文涉及的产品
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群版 2核4GB 100GB
推荐场景:
搭建个人博客
简介:
原2004年初发于CSDN,后图片丢失,现补上。陈刚 from  www.chengang.com.cn


     随着模式概念的普及,了解模式和使用模式的程序员越来越多,很多人在学习模式的时候,都会有这样一种疑惑:“有必要搞得这么复杂吗?”。的确,因为教程的例子过于简单化(这样方便读者学习),或者是作者选例子的时候并没有很好体现所讲模式的优点,很多情况下如果仅就其例子的问题来说,用模式是太复杂了。因此才导致这样的误解:“模式就是把简单的问题复杂化吗?”。当然不是,随着你开发实践的不断丰富,你终会发现模式强大威力,而且模式也并非贵族化的编程方式,它就是一些经过提炼了的解决问题的方法技巧。
 
      通过学习模式,程序员开始告别过去准直线式的代码方式,模式开扩了我们的视野,强化了我们面向对象编程的思维方式。然而现在又出现了另一个普遍的问题,盲目应用模式。模式是问题的解决方案,先有问题才有模式,模式是依附于所要解决的问题的而生的。必须了解模式在很多情况下是以提高代码的复杂度为代价来增强灵活性、可复用性。如果在自已的代码中使用某一模式仅只提高了代码的复杂度,而其它方面收效甚微,或者某部份代码根本就不存在灵活性及高复用性的需求,那么我们就没有必要为使用模式而放弃更直观简单的代码写法。
 
      一流的高手90%精力关注问题的解决方案,因为找到了好的解决方案,再写起代码会很轻松代码也简洁流畅,看这样的代码是一种享受和提高;二流的熟手90%精力关注代码实现,因为问题的解决方案并非最佳,实现的代码也会比较复杂;三流菜鸟记流水帐,90%精力在敲键盘,常常做了大半才发现行不通,回过头来再用90%的时间敲键盘,根本不会用到任何模式,写出来的代码的只有他自已才能看懂。做出来的软件也是支离破碎,做一丁点改动都要大费周折,而且你还不知道改动后会产生什么问题,大有住危房里的感觉。
 
      在这里还是举一个滥用模式的例子吧。我曾参与过一个大集团公司OA系统的第二期开发,开发沿用原有代码架构并增加新的功能模块。文档很少我读原代码时就被它程序里的代码转来转去搞得头大如斗,最后读懂了:原代码架构总体采用工厂模式,而且是最复杂的抽象工厂模式。它把所有模块类都通过工厂生成还工厂套工厂,并且每一个模块类都有一个接口,每个接口也只有一个模块现实类,因为涉及权限控制还用了代理(proxy)模式。 读懂代码后我开始嵌入代码,发现每新增一个类,都要到六个Java文件中去增加相应代码,而在类中每增加一个方法,也要到它的接口等四个Java文件中去增加相应代码。天呀!!!记得当时我的小姆指常会不听使唤,就是因为频繁的使用Ctrl+C 、Ctrl+V,小姆指按着Ctrl键给累的。整个项目组苦不堪言,真烦透了。项目结束后我回顾发现:代理模式用得还对(现在针对权限这类横向控制有AOP编程这种新的解决办法了)但工厂模式在这里根本就是画蛇添足,不仅没有解决什么问题,反而增加代码复杂度和耦合性,降低了开发效率连维护难度都提高了。而且那种每个类简单的加一个接口的方式,更是没有道理,这让我很想说周星驰说过的一句话:“球~~~不是这么踢~~~~的,接口~~~不是这么用~~~的”。言归正传,我们先来看这样一个常见问题:某系统需要支持多种类型的数据库。用过Oracle、MSSQL等数据库的人都知道,它们的SQL编写方式都各有些不同。比如说Oracle的唯一标识自动+1字段用的是序列,MSSQL改一下字段属性就成了,还有各种各自特有的SQL用法。为了支持多数据库,难道我们要开发多套系统?当然NO。请看下面的解决方案。
 
      即然数据库存在多种,我们可以将系统中所有对数据库的操作抽象出来,写成一个个方法组合到一个类中,有几种数据库我们就写几个这样的类。具体设计类图如下:
20060910.gif
 
 
简要说明:
  • OracleDataOperate、SqlserverDataOperate、MysqlDataOperate,分别代表Oracle、Sqlserver、Mysql这三种数据库的操作类。继承自AbstractDataOperate
  • AbstractDataOperate是一个抽象类,包含了那些不同种类数据库都是一样代码的操作方法。继承自DataOperate
  • DataOperate是上面说的数据操作类的统一接口,只有两个方法:取得一条记录、插入一条记录。
  • DataOperateFactory是一个工厂方法,统一用它的方法来得到数据库操作类的实例。
  • SampleClass是我们系统的某个功能模块的类。
  • People是一个实体类,代表一条记录。三个字段 oid唯一标识符、name姓名、date生日。
 
详细说明:
1、所有系统功能模块类只认DataOperat这个接口还不必管具体的实现类是OracleDataOperate还SqlserverDataOperate。DataOperate源代码如下:
public interface DataOperate {
    //根据记录的唯一标识取出一条记录
    People getPeople(String oid);
    //插入一条记录
     boolean insertPeople(People people);
}
 
2、AbstractDataOperate、OracleDataOperate、SqlserverDataOperate、MysqlDataOperate都是继承DataOperate接口的,没什么好说的,省略。
 
3、DataOperateFactory。我们看看工厂方法怎么写的。
public class DataOperateFactory {
     public static final int ORACLE = 0; //定义三个表示数据库类型的常量
     public static final int MYSQL = 1;
     public static final int SQLSERVER = 2;
 
     private static DataOperate db;
     private static int dataType = MYSQL;
    /**
     * 根据数据库类型(dataType)取得一个数据库操作类的实例,
     * 这里对DataOperate使用了单例模式,因为OracelDataOperate等都是无状态的工具类,
     * 所以整个系统只保留一个实例就行了。
     *
     *  @return 返回的是接口,客户端不必关心具体是用那个实现类
     */
     public static DataOperate getInstance() {
         if (db ==  null) {
             if (dataType == ORACLE) //根据dateType返回相应的实现类
                 return new OracelDataOperate();
             if (dataType == MYSQL)
                 return new MysqlDataOperate();
             if (dataType == SQLSERVER)
                 return new SqlserverDataOperate();
        }
         return db;
    }
}
 
4、接下来就看看使用端是如何调用工厂方法和使用数据操作类的。
/**
 * 系统某个功能类
 */
public class SampleClass {
private DataOperate db; //声明一个数据库操作类,注意这里用的是接口噢
    /**某方法*/
     public void sampleMethod() {
        db = DataOperateFactory.getInstance();//得到单一实例
        People p = db.getPeople("123"); //取得一条记录
        db.insertPeople(p);//再插回去
    }
}
 
  我们发现SampleClass中根本没有出现OracelDataOperate、MysqlDataOperate等的影子,这就是接口的威力。客户端不必针对OracelDataOperate等写不同的代码,它只关心DataOperate即可,具体要取那个类的逻辑就由DataOperateFactory负责了。
 
总结:
  • 从例子中我们可以看到什么是面向接口的编程方式。SampleClass使用数据操作类可以不必关心具体是那个类,只要是符合接口的都行
  • 要实例?只须调用DataOperateFactory.getInstance()即可,其它的交于DataOperateFactory这个工厂来做吧,使用端什么都不用关心。
  • 我们要支持新的数据库类型,只须要象OracelDataOperate那样,再写一个继承AbstractDataOperate的类即可,比如SysbaseDataOperate。然后到DataOperateFactory中加入相应代码即可。
  • 如果我们想要可配置性更高,可以用 private static int dataType = MYSQL;中的值设置到一个文本文件中。
  对于开发支持多种数据库的系统,强烈建议使用hibernate,我现在做的系统就是用hibernate的,开发时用Mysql,到要给客户时将数据库换了DB2,程序不用做任何改动,真正的无逢移植。不过这样,本文所提到的方法就没什么用了.
相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
26天前
|
运维 监控 算法
java实现一个动态监控系统,监控接口请求超时的趋势
java实现一个动态监控系统,监控接口请求超时的趋势
40 2
|
12天前
|
数据采集 Java API
java接口防爬虫
7月更文挑战第4天
|
7天前
|
Java 开发者
Java中的Lambda表达式与函数式接口
【7月更文挑战第20天】本文深入探讨Java 8引入的Lambda表达式及其在函数式编程中的应用。我们将分析Lambda表达式如何简化代码、提高可读性,以及它与传统匿名内部类的区别。文章还将介绍函数式接口的概念,并通过实际示例展示如何利用Lambda表达式和函数式接口优化Java代码。
|
8天前
|
并行计算 Java API
深入理解Java中的Lambda表达式与函数式接口
【7月更文挑战第19天】在Java 8中引入的Lambda表达式,不仅简化了代码编写,还为函数式编程提供了支持。本文将探讨Lambda表达式的核心概念、其与函数式接口的关系以及如何在Java中高效利用这一特性来提升代码的简洁性和可读性。我们将通过实例分析Lambda表达式的语法规则和常见用法,同时解释函数式接口的设计原则及其在Java标准库中的应用,旨在帮助开发者更好地理解和运用这一强大的工具。
|
9天前
|
前端开发 JavaScript 安全
|
16天前
|
设计模式 安全 Java
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
33 1
|
16天前
|
设计模式 缓存 安全
Java面试题:工厂模式与内存泄漏防范?线程安全与volatile关键字的适用性?并发集合与线程池管理问题
Java面试题:工厂模式与内存泄漏防范?线程安全与volatile关键字的适用性?并发集合与线程池管理问题
24 1
|
18天前
|
并行计算 Java 大数据
Java中的函数式接口与流式编程
Java中的函数式接口与流式编程
|
26天前
|
Java
Java面向对象 ( 多态 | final关键字 | 接口 )
Java面向对象 ( 多态 | final关键字 | 接口 )
|
26天前
|
Java API 数据处理
Java中的Lambda表达式和函数式接口
【6月更文挑战第29天】在Java的世界里,Lambda表达式的引入标志着编程范式的一次革新。它不仅简化了代码,还提高了开发效率。本文将通过探索Lambda表达式的本质、优势以及如何与函数式接口协同工作,带领读者理解这一特性的强大之处。我们将从基础概念出发,逐步深入到实际应用,揭示Lambda表达式如何让Java代码更加简洁、灵活。
20 3