一次重构经历

最近做了挺多从不同的网页抓取数据的工作,重复多了之后,有了重构的想法,使用的语言是java。

1. 以前的做法:

因为是一个功能性程序,所以把它当做了过称式程序,没有建立特别的类:

public static void main(String[] args) throws IOException, SQLException {
  fetchData("http://...", "A");
}

private static void fetchData(String url, String timePoint) throws IOException, SQLException {
  String content = getHttpContent(url);   // 网页内容
  Date dataDate = getDataDate(content);   // 时间
  List<MainBoard> boardList = getBoardList(content, dataDate); // 解析数据集
  storeDB(boardList, dataDate, timePoint);  // 写入数据库
}

而一些变量值也写死在程序中:

Connection dbConn = null;
dbConn = DriverManager.getConnection("jdbc:sqlserver://192.168.1.1:123;databaseName=AAAA", "12345", "12345");
Statement stmt = dbConn.createStatement();

用于获取时间的getBoardList()函数内部,通过正则表达式和遍历比较取出数据,返回相关的数据类。

storeDB函数负责写入数据库:

String sql = "delete from TABLE where dataDate=‘" + simpleDateFormat.format(date) + "‘ and timePoint=‘" + timePoint + "‘";
stmt.execute(sql);

for (MainBoard board : boardList) {
  String ss = "insert into TABLE values" + "(‘" + board.getCode() + "‘," +
  "‘" + board.getStockName() + "‘," +
  board.getSH() + "," +
  board.getDollar() + "," +
  "‘" + timePoint + "‘," +
  "‘" + simpleDateFormat.format(board.getDataDate()) + "‘," +
    "‘" + timeFormat.format(board.getUpdateTime()) + "‘)";
  stmt.executeUpdate(ss);
}

最初的这个结构基本上可以看成是纯过程化,且没有根据功能放进不同的类文件。如果后续需求需要抓取更多的不同类型的网页,则代码会臃肿、混乱。

每有一个新的格式,上述的getDataDate、getBoardList、storeDB和MainBoard都需要更换,并且会产生空数据类。

2. 重构,抽象:

重复的多了之后,就有了提高代码架构的需求。先补充了理论知识,《重构》和《Head First》结合着看。

在《重构》中看到这么一段描述:

将过程化设计转向对象设计:
1.针对每一个记录类型,转变为只含访问函数的哑数据对象。
2.针对每一处过程化风格,将该处代码提炼到一个独立类中。
3.针对每一段长长的程序,将它分解,再将分解后的函数分别移到它所相关的哑数据类中。
4.重复上述步骤。

原来对象设计是以数据对象为基础,再把与此数据相关的操作或代码提炼成函数,放入此数据对象中。

以前编写程序时,是以过程化为主。

所以思维上的第一个变化是:

把程序抽象的看成是一个数据加工厂,加工厂由许多个模块/部门组合而成。数据看成是一个流,流过程序这个加工厂,被不同部门处理,被转换,但是最终都会有一个存储和展示形式(也就是载体)。

   从更高的层次观察数据的处理是优化结构的重要方式,把相同的步骤/动作抽象出来,把具体的实现细节留给不同的类。

所以,程序的运行可以分为 数据流 和 对数据流的处理。每次有需求变更时,因为模块的接口是定义好的,修改不同模块的实现即可。

隐约有了抽象的思想后,再次结合程序重新思考代码的组织方式。

观察到哑数据对象,也就是只含有数据变量和相应取值/设值方法的类。结合实践发现把和此数据类相关的操作放入哑数据类中,确实是比较好的组织方式。

比如,上面的storeDB函数接收哑数据Data类作为参数,构造数据库语句。那么这里就可以把这个函数放入Data类中:

class Data {
    // ...
    public String generateInsertSql() {
     String ss = "insert into TABLE values" + "(‘" + board.getCode() + "‘," +
        "‘" + board.getStockName() + "‘," +
        board.getSH() + "," +
        board.getDollar() + "," +
        "‘" + timePoint + "‘," +
        "‘" + simpleDateFormat.format(board.getDataDate()) + "‘," +
         "‘" + timeFormat.format(board.getUpdateTime()) + "‘)";
      return ss;
   }
}

思考为什么这个组织方式好于以前的形式?原因之一就是这样更符合人的思维方式。

再以这样的方式思考组织程序,继而思维产生了第二个变化:

把类看成以数据为中心,附属着对这个数据的各种操作作为函数。而程序就可以看成是各个附带着行为的数据之间的交互。这样以前总结出的数据流就分化为一个个的个体,对数据的加工操作附属于不同的个体。

感觉似乎摸到了面向对象的门道,决定再去知乎上看看大家的讨论,发现https://www.zhihu.com/question/19701980这篇蛮具有启发意义的。

进而更进一步认识了面向对象:

一个操作或一件事由谁来完成,强调的是“谁”。由此,程序变化为一群“活物”之间的交互。

回到程序实践,从过程化结构中抽象出三个主体:UrlConstructor,HttpService,SqlConstructor。分别代表产生url字符串、读取网页内容、构造sql语句。UrlConstructor还有一个功能是从网页内容里解析提取目标数据。

这时流程变为,UrlConstructor构造出url,然后HttpService接收url并取得网页数据,交由UrlConstructor解析处理,并由SqlConstructor产生sql语句,最后又DB对象写入数据库。

但是这时又产生了一个新的困惑:虽然结构上比以前抽象的一些,但是感觉依然需要一些结构化的语句来处理对象间的交互。如何消除这部分影响?继续学习,发现一个讨论http://bbs.csdn.net/topics/40441744算是解释了心中的疑惑。

思维再次发生了变化:

面向对象是一种思维,和语言无关。不是写了顺序执行的代码就是面向过程,面向对象强调的是以什么样的思维来组织程序。

用c也可以写出面向对象的程序,而组织的不好,用Java写出来的也会是面向过程的程序。所以,如果组织的好,有顺序执行代码也是面向对象的。

比较常见的例子就是全局变量,在函数中使用了全局变量也就破坏了类的封装性,就不是面向对象编程了。好的做法是,全局变量都作为参数传入成员函数中,实现封装。这么做也可以方便单元测试,和提高清晰度。

上述思维也很好的解释了面向对象的三大要素:封装,继承,多态。封装即把数据和操作当成一个整体,对外只暴露接口。继承和多态是的程序可以方便扩展,调用者无需关注实现细节,进而灵活应对需求变更。这方面的讨论可以看下https://www.zhihu.com/question/20275578里面尤其是“invalid s”的回答。

至此,终于理解了依赖倒置,依赖注入还有控制反转等一些以前没有领悟的概念。见https://www.zhihu.com/question/31021366核心思想即是面向接口编程。

在理清了面向对象思想之后,才算是可以初窥设计模式的门径。如装饰器模式、工厂模式、观察者模式等,从面向对象的角度去理解会非常快速。设计模式有几个原则:1.面向接口编程;2.对扩展开放,对修改封闭;3.组合大于继承。使用设计模式往往会遇到一些问题需要权衡各方面做决定。

但是反过来说,并不是面向对象编程就一定要往设计模式上面靠。“设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结”,设计模式一般都有一个适用场景,超出这个范围,也不见得还有效。

设计模式之上就是框架的设计,这个暂时不做深究。

接着再说说重构,重构我认为也可以算是一种思维,需要固化在脑中,某种意义上编程==重构。

重构我认为有几个关键思想:1.重复代码移动到统一的地方;2.如果需要修改,目标是只修改某一个地方;3.一个变化只影响一个类;4.一个类只受一个变化的影响。等等~~

因为“代码首先是为人写的,其次才是为计算机写的”。

最重要的是努力实践,现在我对面向对象思维的理解也才刚开始,以后肯定会回过头反复思考再实践。希望能越来越熟练,高效。

最后借着这次重构,记录下关于java正则性能的感想。

因为写正则表达式一般都会使用到通配符如:

<td .*><a.*>(.*)</a></td>

而正则并不意味着查找效率就高。通配符可能会导致匹配时间增加,所以一些简单的表达式使用诸如indexOf()这类的函数自己实现的话性能可能会提高一些。从测试结果看,简单表达式自己时间会提高一小部分性能,但是对比读取网页的实践微乎其微。所以如何使用正则?是自己实现还是用复杂的表达式,需要先进行测试,再做决定。

时间: 2024-10-28 10:50:10

一次重构经历的相关文章

从MySpace基于.NET平台的六次重构经历感受分布式

它们拥有的用户和fans之多,大家都很清楚. Myspace是一个基于.NET平台的,而Facebook更多是基于LAMP的.我们来看看MySpace配合.NET+Windows Server 2003+Sql Server 2000/2005+IIS怎么创造传奇的 文章正文如下: 在每个里程碑,站点负担都会超过底层系统部分组件的最大载荷,特别是数据库和存储系统.接着,功能出现问题,用户失声尖叫.最后,技术团队必须为此修订系统策略.虽然自2005年早期,站点账户数超过7百万后,系统架构到目前为止

代码重构实例之数据聚集

敏捷开发强调,要经常重构代码.在开发过程中,往往是开发和重构交替进行.短暂的重构,可以使得后续的开发维护更加容易.我觉得,代码重构可以分为逻辑重构和数据结构重构.数据结构的重构往往需要对代码进行多处改动:但是,数据结构的重构也可以为后续的开发维护带来更大的便利.这里就是一个数据结构重构的例子. 这是以前的一次代码重构经历,今天想起了,就记下来,帮助自己记忆.当然,既然是重构,总得承认自己写的第一版丑陋的代码. 为了方便描述,采用javascript来进行说明. 故事是这样的.刚开始,任务是画一些

纪录我的iOS代码重构的之路

参考资料的网址 田伟宇(Casa Taloyum)有几篇介绍iOS架构的文章,一级棒!原博客链接. iOS应用架构谈 开篇 iOS应用架构谈 view层的组织和调用方案 iOS应用架构谈 网络层设计方案 objc china上的第一期文章讲viewcontroller的,很棒!objc china入口. #1 更轻量的 View Controllers 我的重构经历 从2015年8月初买了mac后学习iOS已经有两个月,直接上手项目的.过段时间应该会上线.但是由于野路子出生,写完后,发现自己代码

系统重构的道与术

最近参与了很多重构项目,有以提高服务器资源利用率为目标的Gateway网关.AMAPS等服务的重构,也有以提升架构合理性和研发效率为目标的共享业务服务化拆分,借此机会把相关内容梳理一下,是分享更是自我总结和学习.准备以重构工作中容易产生误区的地方或容易被忽视的重点来聊聊,既不重复网上千篇一律的各种方案资料,也对重构工作有参考价值. 什么是“道和术”?个人简单的理解,道就是思想,术是方法.可谓有道无术,术尚可求也:有术无道,止于术.分别从重构的基本思路和原则,以及常见重构方案的应用来分别讲讲系统重

为什么浮点型的计算不准确?

要想自己写点技术博客也真是尴尬呢.野路子出身,对所有东西都是只知道皮毛. 简单回顾下自己吧,毕业于磨子桥技术学院种猪选育专业,读大学的时候为了做游戏进入了软件行业.尽管做出来的项目耦合的辣眼睛,崩溃性失败,还是希望好好在这个行业里经营下去. 最近报了个机构,从头开始系统的提升自己的技术能力,以期早日达到专业水平.在学习过程中遇到一些引起自己思考的点,大概都是些很蠢的问题.记录到博客里,算是总结自己的技术能力重构经历吧. 嗯,下面说正题. 培训班刚开不久,还在学习一些基础的编程思想方面的东西.讲到

Alpha阶段第七次Scrum Meeting

情况简述 Alpha阶段第七次Scrum Meeting 敏捷开发起始时间 2016/10/28 00:00 敏捷开发终止时间 2016/10/29 00:00 会议基本内容摘要 跟助教进行了交流,明确了一些目标和预期,向助教寻求了一些帮助 参与讨论人员 全体参与 讨论时长 2016/10/27 20:30-21:00 30M 特别说明 无 任务分配及完成情况(截止27日晚九点) 团队成员 已完成 任务概述 预计耗时 预计成果 陈鸿超 #41 与石浩然实现RN与后端的数据连接 #42 搭建后端框

致敬2016

工作: 马上就到7月12号,从现在开始,我把这一天设定为我的工作日.在这一天,我选择离开我的家人,离开我的小伙伴,离开我的温馨的故土.踏上火车,看过人生的五彩缤纷,到达外人看来是梦想的天堂,而对于我,这里是我的战场,并没有你们想的那么美好. 一年前面试的经历,现在我依然清晰的记得.关于面试,我想说:“面试就像谈恋爱,需要彼此互相的欣赏.同时面试还是一场博弈,需要互相的隐瞒.经历过了,你才知道.你很真诚,但对方不一定.彼此都是这样.” 工作中,做好自己的工作即可,有特殊情况了,你可以施展一下自己的

我们需要重构吗

当我开始写这篇文章的时候 ,我的思想还处于斗争阶段,多年来我也在程序开发的第一线,经历了很多项目,也编写了很多代码,但是从心里说我几乎没有用到重构这一方法,但最近看一本书名字叫<重构 改善既有代码的设计> ,它让我感觉我这些年的迷惑终于找到了明灯. 我以前认为代码只要实现了就OK了,不用再去动他了,不管当时实现时绕了多少个圈,用了多少冗余代码.有时候想想也是既然已经实现了,干嘛还要化那么多精力去装饰那些代码,而且保证系统功能正常运行和项目按时完成是第一要务,剩下的一切都变得不在重要了.当我开始

小酌重构系列[3]&mdash;&mdash;方法、字段的提升和降低

本文要介绍的是4种重构策略,它们分别是提升方法.降低方法.提升字段和降低字段.由于这4种重构策略具有一定的相通性,所以我将它们放到一篇来讲解. 定义 以下是这4种策略的定义 提升方法:当子类的方法描述了相同的行为时,应将这样的方法提升到基类.降低方法:在基类中的行为仅和个别子类相关时,应将这样的行为降低到子类.提升字段:当子类中的字段描述着相同的信息时,应将这样的字段提升到基类.降低字段:当基类中的字段仅仅用于个别子类时,应将这样的字段降低到子类. 以上的定义是较为为枯燥无趣的,各位读者大可不必