前言:本篇文章主要描述我是如何通过Java的反射加多态干掉 swtich这个代码的坏味道


目录

  • 代码的坏味道
  • 《重构》曰
  • 遭遇switch
  • 利剑:多态加反射
  • 结束战斗

代码的坏味道

有这么一句话:普通的代码是写给机器看的,优秀的代码是写给人看的,在软件开发的过程中,不知道大家有没有遇到过if-else或switch满天飞,函数长的看都不想看这种情况,先不说软件的扩展性怎么样,这样的代码就算是我们自己写的,回头看时也会发现逻辑很乱,这些都属于“坏味道”,在我们进行开发中,如闻到这股“坏味道”,应该立刻解决,而不是放纵它发展,否则,后面会遇到其他很多的问题,比如需求改了代码不能改?增加新功能?又或者是改自己的代码发现看不懂了?添加注释,不是个好办法,如果我们能够通过其他手段来改善这种情况,能很清晰看清代码的结构和含义并加强它的拓展性,为什么还要大片的注释来解释它呢?
你可能会有问题,我用if-else或者switch,写一个长长的函数多简单,调用开销什么的都没了,这也是我原来的疑问,在开发过程中,代码的高质量才是我们首要追求的,性能不是这时考虑的,二八定理(原谅我又搬出来了- -),你的常常调用的代码也就全部的20%,性能瓶颈常常是在IO等其他方面,而且现在的编译器函数调用开销耗费不了多少性能,我们应该在将来测试时确定性能瓶颈在那里,再进行性能优化,如果瓶颈真的在这里,很简单展开就好。
代码的坏味道可不止这些,《重构》里大约说了近百种,感兴趣可以去看看这本书~


《重构》曰

《重构》里描述过:面向对象程序的一个最明显特征就是:少用switch(或case)语句,从本质上说,switch语句的问题在于重复。你常会发现同样的switch语句散布于不同地地点。如果要为它添加一个新的case子句,就必须找到所有的switch语句并修改它们,面向对象中的多态概念能可为此带来优雅的解决方案。多态最根本的好处就是:如果你需要根据对象的不同类型而采取不同的行为,多太使你不必编写明显的条件表达式

来看看我的经历!


遭遇 switch

最近和小伙伴写一个项目,客户端发请求,服务端根据请求的不同处理后返回给客户端,我在原先是这么干的,给客户端的每个请求一个整型值当作标记,服务端接受后,用switch根据标记值mark来判断请求的类型,接着对应的case处理,不要嘲笑我- -,相信很多人一开始都是这么干的,这样做的结果是什么?

当客户端再次增添请求时,我有些受不了了,一方面代码写的很长,逼迫我用了很多注释,每增添一个case我都要添加许多注释生怕自己下次看不懂,这样导致代码整体看着很乱,另一方面需求和需求之间耦合性太强,就在一个函数里。
这时代码没有什么面向对象思维,感觉就像c with class

原先的代码(我删掉了逻辑,不需要仔细看

public class ParseJson {/* 待解析的json对象 */JSONObject js;/* 构造函数,获取json */ParseJson(JSONObject json) {this.js  = json;}/* 解析json并获取相应的结果 */String Parse() throws SQLException {/* 结果 */String result = "";int iret = 0;int mark = 0;/* 获得mark,创建对应的Executesql对象 */try {mark = js.getInt("mark");}catch (Exception e){result = "{\"error\":0, \"status\":\"success\", \"date\":\"2015-08\", " +"\"result\":{\"requestPhoneNum\":\"\", \"IsSuccess\":\"failure\"," +"\"mark\":0, \"ResultINFO\":\"json解析失败,您的输入有误\"}}";return result;}switch (mark){/* mark == 1 检测此帐号是否正确且被注册过 */case 1:break;/* mark == 2  帐号注册*/case 2:break;/* mark == 3 忘记密码 注意:应该验证密宝后返回用户一个key值,mark4根据key值来改新密码 */case  3:break;/* mark ==4 用户更新密码 */case 4:break;/* mark ==5 用户更新信息* 自己的name,头像等* 一般用户点击详细信息会有更新操作 */case 5:break;/* mark ==6* 登记新的联系方式* 需要修改UserFriend 好友and me 的 is update* 每次好友登录需要检查好友的isupdate* 看谁换联系方式了 */case 6:break;/* mark == 7*  说明本地没有数据,客户端需要发送全部数据 */case 7:break;/* mark == 8*  说明本地有数据,只更新发送好友的数据*  相当于下拉刷新 *//**  先通过account 获得用户 uid*  uid 获取 friendId*  friendId and uid 获取不为0的 isUpdate数据*  分析isUpdate数据,找出标记,组装好友更新的数据,返回*/case 8:break;/* mark == 9*  添加联系人,UserFriend中添加数据,注意要返回好友的信息 */case 9:break;/* mark == 10*  生成二维码加好友 */case 10:break;/* mark == 11*  生成二维码*  客户端生成一张二维码发送给服务器服务器保存 */case 11:break;/* mark ==12* 登录*/case 12:break;/* mark == 13*  删除联系人 */case 13:break;/* mark == 14* 用户发送所有的个人信息,保存到数据库* */case 14:break;}return result;}
}

怎么样,大大的switch加上大片注释,看上去很难受,而且原本的类中ParseJson函数更长,大约1000行!
来看看缺点

客户端请求类型很多,switch所在的函数很长

客户端新增加请求时,必须去改switch,增加case语句

代码重复,请求中总有一些类似的

变量满天飞,变量名词不达意

看看怎么解决


利剑:多态加反射

相信很多人包括我也一样,觉得“多态”是很高贵的词,其实多态的好处就是:如果你需要根据对象的不同类型采取不同的行为,多态使你不必编写明显的条件表达式

改变

首先,我提取出一个公共的基类,因为所有的请求都有类似的地方,比如每个请求都要响应函数。
代码仅用来举例

class request{/* 构造函数 */public request(int _mark, String _account){mark = _mark;account = _account;}/* 响应函数 */public String response(){...}.../* 共有字段 */int mark;String account;
}

接着我拆分了这个大大的switch,把它每个case语句换成了一个对象,对象继承基类request

class AccountRegister extends request{/* 构造函数 */public AccountRegister(){}/* 覆盖response()函数 */public String reponse(){...}.../* 属于自己的字段 */String secret;...
}
class AddFriendByAccount extends request{/* 构造函数 */public AddFriendByAccount(){}/* 属于自己的response()函数 */public String reponse(){...}.../* 属于自己的字段 */String FriendId;...
}

还有很多

这样,每种请求变成一个对象,对象名字就是请求内容,非常清晰,不需要要大片的注释,公共的部分在基类,减少了代码重复,扩展性变得很强,客户端要增加新请求?没关系,我新添加一个类继承基类就好

放一张修改后的类

每个请求是一个类,是不是清爽了很多?,每个类都不长,根据类名就能清楚的看懂类含义。

代码是清爽了,我怎么用呢?原先根据switch值后case就好了,现在全是对象,我怎么知道客户端的请求对应是哪个对象呢,也就是说没办法选择了。

别急,反射来了!!
不熟悉反射的小伙伴可以了解下 Thinking in Java – 类型信息RTTI
我这篇文章很简单的描述了下。

简单的说就是我可以通过类的名字来实例化类对象,这么棒!!通过名字就可以创建类对象?是的,就是这么简单。
反射例子

/* 基类 */
public class aaa {public void func(){return;}public static void main(String[] args) throws Exception {/* 反射,通过类的名字 */Class c = Class.forName("tryCode.ccc");/* 实例化对象 */aaa b = (aaa) c.newInstance();/* output: bbb */b.func();}
}/* 派生类bbb */
public class bbb extends aaa {public void func(){System.out.println("bbb");}
}/* 派生类bbb */
public class ccc extends aaa {public void func(){System.out.println("ccc");}
}

用反射的结果呢?不再需要魔幻数字1, 2, 3…外加长长的注释来标记这是干什么的,名字很清楚的表达了我们的请求,客户端发来的登录请求不是 1 了,而是LogIn,我们仅需要几行代码就动态创建了客户端请求对应的对象,接着调用对象方法,编译器会根据派生类对象来执行对应的response, 太神奇了!!!

于是我的代码变成了这样

除去注释,看看它有几行吧~


结束战斗

呼呼~,战斗结束了,不得不说Java反射和多态真的非常有用,我这里也仅是一个参考,或许大家有更好的方法来改进。也有可能我们根本不需要做这样的改进(也许客户端这辈子不会增加请求了呢?代码写完就交给别人了呢- -这样不对)
但我想要说的是:从开始我们就这样做岂不是更好?别让代码的坏味道弥漫了你整个程序



By XiyouLinuxGroup –wwh

重构:运用Java反射加多态 “干掉” switch相关推荐

  1. [java] 反射和多态实现原理详解以及对比

    Table of Contents 反射和多态 多态 什么是多态 java里多态的具体用法 多态的实现原理 反射 什么是反射 反射的实现原理 反射的应用 反射的弊端 反射相关类 反射应用实例 一些问题 ...

  2. java 反射加实例化内部类

    2019独角兽企业重金招聘Python工程师标准>>> 在Java中,使用反射的方法加载类的具体代码是: Class<?> cls = Class.forName(&qu ...

  3. Java动态加载类(对反射的基本理解)

    目录 基本概念 演示 基本概念 这里演示下Java的动态加载类.因为在编译的时候,加载类是静态加载,运行时加载是动态加载. 在此我对反射有了如下的理解: 如果有一个class A,可以使用new A( ...

  4. Cathy学习Java——反射和类的加载

    工厂设计模式 工厂方法模式 概述 工厂:就是生产特点产品的 实现方式 1>创建一个抽象工厂类,声明抽象方法 2>写一个具体抽象工厂类的子类,由子类负责对象的创建 优点:后期容易维护,增强了 ...

  5. Java 反射将配置文件数据加载到对象属性中

    Java 反射将配置文件数据加载到对象属性中 Java 反射 可以根据类名找到相应的类,也可以将配置文件中的值加载到对应属性中. 需要用到的包:spring-core-3.1.2.Release.ja ...

  6. Java反射-静态/动态加载类

    title: Java反射-静态/动态加载类 date: 2019-05-28 18:50:00Java反射-静态/动态加载类 Class 类是对象,是java.lang.Class类的实例对象.任何 ...

  7. JAVA反射机制、Class类及动态加载、成员变量构造方法其他方法的反射与调用、代理模式AOP

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法: 对于任意一个对象,都能够调用它的任意一个方法和属性: 这种动态获取的信息以及动态调用对象的方法的功能称为java语言 ...

  8. Java反射机制实现与原理

    本文介绍Android反射机制实现与原理,在介绍之前,要和Java进行比较,所以先看下Java中的反射相关知识: 一.反射的概念及在Java中的类反射 反射主要是指程序可以访问.检测和修改它本身状态或 ...

  9. Java基础知识第二讲:Java开发手册/JVM/集合框架/异常体系/Java反射/语法知识/Java IO

    Java基础知识第二讲(Java编程规范/JVM/集合框架/异常体系/Java反射/语法知识/Java IO/码出高效) 分享在java学习及工作中,常使用的一些基础知识,本文从JVM出发,讲解了JV ...

最新文章

  1. 皮一皮:可怜的西瓜...
  2. Python面向对象---类的基本使用
  3. 《京东618实践:一元抢宝系统的数据库架构优化》阅读笔记
  4. 想要快速搭建开发测试环境?这么做就可以!
  5. UVA - 572 Oil Deposits-dfs找连通块
  6. linux中修改字符编码
  7. php 依赖注入框架,依赖注入模式(Dependency Injection)
  8. 【半年总结】愿有岁月可回首
  9. 基于JSP的蛋糕销售系统设计与实现答辩ppt模板
  10. day04-商城后台搭建
  11. 禁用EnableViewState和启用EnableViewStat时请注意
  12. 红米K30 4G手机图纸 主板元件位号图
  13. 如何用PS缩小图片而清晰度不变?
  14. python双色球数据抓取及模拟生成高概率的号码
  15. RDA5856ETE系列_(1)新手入门
  16. 世界5G大会 大兴机场 随记
  17. html双击变成可编辑状态,JS实现双击编辑可修改状态的方法
  18. java设计模式总结1
  19. win10 删除删除账户,新的账户中文件资源管理器图标变白 找回方法
  20. 基于Android平台实现人脸识别

热门文章

  1. C语言实现模拟ATM机管理系统
  2. web服务器监控(一)
  3. 上帝的思想?神化斯蒂芬·霍金之症结
  4. Java 音频提升音量工具(附代码) | Java工具类
  5. 微信怎么和计算机发送文件格式,用微信怎么发送文件 手机微信发送文件、视频方法图文详解...
  6. STM32看门狗简述
  7. 在html中如何做个人微博,学习记录:爬取个人微博
  8. python的一些语句的意思
  9. dvhop的matlab画图函数,DV-Hop 源代码 matlab 出各种图
  10. android相册隐藏拍照按钮,你绝对不知道的4大手机“隐藏”拍照功能!学会100%惊艳朋友圈!...