重构:运用Java反射加多态 “干掉” switch
前言:本篇文章主要描述我是如何通过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相关推荐
- [java] 反射和多态实现原理详解以及对比
Table of Contents 反射和多态 多态 什么是多态 java里多态的具体用法 多态的实现原理 反射 什么是反射 反射的实现原理 反射的应用 反射的弊端 反射相关类 反射应用实例 一些问题 ...
- java 反射加实例化内部类
2019独角兽企业重金招聘Python工程师标准>>> 在Java中,使用反射的方法加载类的具体代码是: Class<?> cls = Class.forName(&qu ...
- Java动态加载类(对反射的基本理解)
目录 基本概念 演示 基本概念 这里演示下Java的动态加载类.因为在编译的时候,加载类是静态加载,运行时加载是动态加载. 在此我对反射有了如下的理解: 如果有一个class A,可以使用new A( ...
- Cathy学习Java——反射和类的加载
工厂设计模式 工厂方法模式 概述 工厂:就是生产特点产品的 实现方式 1>创建一个抽象工厂类,声明抽象方法 2>写一个具体抽象工厂类的子类,由子类负责对象的创建 优点:后期容易维护,增强了 ...
- Java 反射将配置文件数据加载到对象属性中
Java 反射将配置文件数据加载到对象属性中 Java 反射 可以根据类名找到相应的类,也可以将配置文件中的值加载到对应属性中. 需要用到的包:spring-core-3.1.2.Release.ja ...
- Java反射-静态/动态加载类
title: Java反射-静态/动态加载类 date: 2019-05-28 18:50:00Java反射-静态/动态加载类 Class 类是对象,是java.lang.Class类的实例对象.任何 ...
- JAVA反射机制、Class类及动态加载、成员变量构造方法其他方法的反射与调用、代理模式AOP
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法: 对于任意一个对象,都能够调用它的任意一个方法和属性: 这种动态获取的信息以及动态调用对象的方法的功能称为java语言 ...
- Java反射机制实现与原理
本文介绍Android反射机制实现与原理,在介绍之前,要和Java进行比较,所以先看下Java中的反射相关知识: 一.反射的概念及在Java中的类反射 反射主要是指程序可以访问.检测和修改它本身状态或 ...
- Java基础知识第二讲:Java开发手册/JVM/集合框架/异常体系/Java反射/语法知识/Java IO
Java基础知识第二讲(Java编程规范/JVM/集合框架/异常体系/Java反射/语法知识/Java IO/码出高效) 分享在java学习及工作中,常使用的一些基础知识,本文从JVM出发,讲解了JV ...
最新文章
- 皮一皮:可怜的西瓜...
- Python面向对象---类的基本使用
- 《京东618实践:一元抢宝系统的数据库架构优化》阅读笔记
- 想要快速搭建开发测试环境?这么做就可以!
- UVA - 572 Oil Deposits-dfs找连通块
- linux中修改字符编码
- php 依赖注入框架,依赖注入模式(Dependency Injection)
- 【半年总结】愿有岁月可回首
- 基于JSP的蛋糕销售系统设计与实现答辩ppt模板
- day04-商城后台搭建
- 禁用EnableViewState和启用EnableViewStat时请注意
- 红米K30 4G手机图纸 主板元件位号图
- 如何用PS缩小图片而清晰度不变?
- python双色球数据抓取及模拟生成高概率的号码
- RDA5856ETE系列_(1)新手入门
- 世界5G大会 大兴机场 随记
- html双击变成可编辑状态,JS实现双击编辑可修改状态的方法
- java设计模式总结1
- win10 删除删除账户,新的账户中文件资源管理器图标变白 找回方法
- 基于Android平台实现人脸识别
热门文章
- C语言实现模拟ATM机管理系统
- web服务器监控(一)
- 上帝的思想?神化斯蒂芬·霍金之症结
- Java 音频提升音量工具(附代码) | Java工具类
- 微信怎么和计算机发送文件格式,用微信怎么发送文件 手机微信发送文件、视频方法图文详解...
- STM32看门狗简述
- 在html中如何做个人微博,学习记录:爬取个人微博
- python的一些语句的意思
- dvhop的matlab画图函数,DV-Hop 源代码 matlab 出各种图
- android相册隐藏拍照按钮,你绝对不知道的4大手机“隐藏”拍照功能!学会100%惊艳朋友圈!...