快乐12开奖结果查询:BlogJava - ★四川福利彩票快乐12★快乐12开奖直播★快乐12开奖★辽宁福彩快乐12★快乐彩12选5走势图//www.ot7t.com.cn/all专注于Java技术zh-cnThu, 24 Jan 2019 09:49:46 GMTThu, 24 Jan 2019 09:49:46 GMT60《测试驱动开发》的读书笔记 - ★四川福利彩票快乐12★快乐12开奖直播★快乐12开奖★辽宁福彩快乐12★快乐彩12选5走势图//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/18/433616.html朱远翔朱远翔Fri, 18 Jan 2019 15:25:00 GMT//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/18/433616.html//www.ot7t.com.cn/zhuyuanxiang/comments/433616.html//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/18/433616.html#Feedback0//www.ot7t.com.cn/zhuyuanxiang/comments/commentRss/433616.html//www.ot7t.com.cn/zhuyuanxiang/services/trackbacks/433616.html《测试驱动开发》的读书笔记
  • 测试驱动开发的规则
  • 测试程序开发周期的阶段

      《测试驱动开发》的读书笔记

      学习基?。?/h3>

      熟悉《设计模式》的基本概念,熟悉《重构》的基本概念,熟悉基本的Java语法,熟悉Eclipse和JUnit的使用,有相对较好的英语基础。

      学习过程:

      • 第1部分,手工输入实例程序,了解TDD的方法和过程。重点是理解TDD的思路,最好的理解方式就是通过实践的方式理解。
      • 第2部分,教你如何用Python实现一个符合xUnit的测试框架。
      • 第3部分,TDD的模式,这些模式展现TDD如何与其他重要思想(例如:设计模式、重构等等)一起工作的。

      学习目的:

      编写正确的代码。

      学习感悟:

      • 道理很简单,操作也很简单,但是我仍然无法明了作者许多重构操作的意图,只是感觉作者可能是从直觉出发写出代码,再通过重构推进测试代码与产品代码之间的解耦。(知其然,不知其所以然)
      • 可以先跳过第2部分,了解xUnit如何实现固然重要,但是为此变成先学Python就有点跑题了
      • 还可以跳过第3部分,这部分都是作者从思想上对TDD的总结,俗话说“不吃亏不涨记性”,等自己在项目中吃够了亏再来回顾别人的经验,才会真正共鸣吧。

      学习代码:

      代码很简单,不需要再提供,反而最重要的是自己一定要手工跟一遍,否则无法领会作者的意图。至少,我在豆瓣上看了几个评论,大部分都是看懂了,没感觉。

      测试驱动开发的规则

      1. (P4)明确设计目标,完善测试代码
        • 消除重复设计即是消除依赖关系(测试代码与产品代码之间的依赖关系);
        • 测试驱动开发不是通过一小步一小步来完成的,而是培养一小步一小步开发软件的能力。因为简单的问题可以走快点,复杂的问题就可以走慢点。
      2. (P12)完善产品代码
        • 完成知道怎么做的产品代码;
        • 补充不知道怎么做的产品伪代码。
      3. (P14)寻找隐含的开发目标,完善测试代码
        • 利用三角法发现产品代码中的问题( 三角法:当例子不止1个的时候才完善代码??梢栽诿挥猩杓扑悸返氖焙?,换个角度思考问题)
        • 利用重构解决发现的问题
      4. (P17)通过开发的功能来重构测试代码
      5. (P33)不打断自己已经在做的工作,如果非要打断也不要在新的工作中花太长的时间,并且不能再次打断这个新的工作。(事不过三)
      6. (P34)找到自己的开发节奏。(一开始慢一点,慢慢加快,再调整回慢,直到找到自己最合适的节奏感)
      7. (P42)删除不需要的子类,会发现测试代码中存在的不需要的测试也可以删除(放弃不需要的东西,会帮助你发现你还需要什么)

      测试程序开发周期的阶段

      1. 写一个测试程序;
      2. 让测试程序编译通过;
      3. 运行测试程序,发现不能运行;
      4. 让测试程序可以运行;
      5. 消除重复设计,优化设计结构。


    • 朱远翔 2019-01-18 23:25 发表评论
      ]]>
      详解音频编解码的原理、演进和应用选型等 - ★四川福利彩票快乐12★快乐12开奖直播★快乐12开奖★辽宁福彩快乐12★快乐彩12选5走势图//www.ot7t.com.cn/jb2011/archive/2019/01/18/433615.htmlJack JiangJack JiangFri, 18 Jan 2019 14:02:00 GMT//www.ot7t.com.cn/jb2011/archive/2019/01/18/433615.html//www.ot7t.com.cn/jb2011/comments/433615.html//www.ot7t.com.cn/jb2011/archive/2019/01/18/433615.html#Feedback0//www.ot7t.com.cn/jb2011/comments/commentRss/433615.html//www.ot7t.com.cn/jb2011/services/trackbacks/433615.html阅读全文

      Jack Jiang 2019-01-18 22:02 发表评论
      ]]>
      《单元测试之道Java版》的读书笔记 - ★四川福利彩票快乐12★快乐12开奖直播★快乐12开奖★辽宁福彩快乐12★快乐彩12选5走势图//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/16/433606.html朱远翔朱远翔Wed, 16 Jan 2019 09:57:00 GMT//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/16/433606.html//www.ot7t.com.cn/zhuyuanxiang/comments/433606.html//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/16/433606.html#Feedback0//www.ot7t.com.cn/zhuyuanxiang/comments/commentRss/433606.html//www.ot7t.com.cn/zhuyuanxiang/services/trackbacks/433606.html总览
    • 第2章 首个单元测试
    • 第3章 使用JUnit编写测试
    • 4. 测试什么?
    • 5.CORRECT(正确的)边界条件
    • 6.使用Mock对象
    • 7. 好的测试所具有的品质(A-TRIP)
    • 8. 在项目中进行测试
    • 9. 设计话题

        总览

        这是本相对简单的书,书中采用的JUnit的版本也是旧的,但是在新的JUnit4下稍做修改依然可以运行。重要的是通过这本书了解JUnit在Java的单元测试中是如何使用的。

        第2章 首个单元测试

        计划你的测试:测试不是无中生有的,也不是意想天开的。是根据需要一点点添加的,帮助自己尽早地发现思考上的误区。参看这章给出的例子,原来理所当然正确的,结果不一定是正确的。

        第3章 使用JUnit编写测试

        3.1 构建单元测试

        测试代码必须要做的几件事情:

        • 准备测试的条件(创建对象、分配资源等等)
        • 调用测试的方法
        • 验证测试方法的行为与期望是否相符
        • 测试结束后清理现?。ㄊ头抛试吹鹊龋?

          3.2 JUnit的各种断言

          断言:JUnit提供的辅助函数,帮助你确认被测试函数是否正确运行。

        后面还介绍了(3.5 JUnit的自定义断言)

        3.3 JUnit框架

        这章是基于JUnit3.x写的,建议了解就可以了,因为JUnit4的变化较大,使用也更方便直观,因此直接参考JUnit4的帮助。

        框架运行顺序 对应于标签
        setUpBeforeClass() @BeforeClass
           
        setUp() @Beofre
        testMethod1()  
        tearDown() @After
           
        setUp() @Before
        testMethod2()  
        tearDown() @After
           
        tearDownAfterClass() @AfterClass

        4. 测试什么?

        6个需要测试的地方(Right-BICEP):

        • Right:结果是否正确(Right);
        • B:边界(Boundary)条件是否正确(CORRECT);参考第5章
        • I:能否检查反向(Inverse)关系;
        • C:进行交叉检查(Cross-Check)的其他手段;
        • E:强制错误(Error)条件发生;使用Mock对象实现,参考第6章
        • P:满足性能(Performance)的要求。

        测试内容较多时,可以使用测试数据文件进行准备。但是使用文件后就没有测试代码看起来那么直观了,因此除非测试内容非常复杂,否则没有必要采用这样的方式。并且如果测试文件出现错误(作者书中就出现了数据错误),还会导致测试不通过,增加了维护的成本。

        5.CORRECT(正确的)边界条件

        • 一致性(Conformance):值是否符合预期的格式;
        • 有序性(Ordering):一组值是否符合对排序的要求(有序性、无序性);
        • 区间性(Range):值是否在合理取值范围内(在最小值与最大值之间);
        • 引用(Reference)-耦合性:代码是否引用了不受代码本身直接控制的外部因素;
        • 存在性(Existence):值是否存在(例如:非NULL,非零,包含于某个集合等等)
        • 基数性(Cardinality):是否恰好有足够的值;(也称为集合的势,即集合里面包含的元素个数)
        • 时间性(Time)-绝对时间和相对时间:所有的事情是否按照顺序发生?是否在正确的时间发生?是否及时发生?

        6.使用Mock对象

        Mock对象解决的问题:

        • 真实对象具有不可确定的行为(如:股票行情);
        • 真实对象很难被创建;
        • 真实对象的某些行为很难被触发(如:网络错误);
        • 真实对象令程序的运行速度很慢;
        • 真实对象有用户界面或者就是用户界面;
        • 真实对象需要被询问它是如何被调用的(如:验证某个回调函数是否被调用);
        • 真实对象实际上不存在(如:其他开发小组的接口、或者某个没有的硬件产品)。

        Mock对象解决的步骤:

        • 使用一个接口来描述这个对象;
        • 为产品代码实现这个接口;
        • 以测试为目的,在Mock对象中实现这个接口。

        注:这里的Mock不是网上已经形成框架的Mock工具,是Mock的实现原理。作者推荐的Mock工具是EasyMock。其他的Mock工具可以参考《[使用Mock进行单元测试]》(https://blog.csdn.net/u011393781/article/details/52669772)

        7. 好的测试所具有的品质(A-TRIP)

        • 自动化(Automatic):自动化地调用测试和检查结果;常用的持续集成工具
        • 彻底的(Thorough):测试了所有需求关注的情况;常用的代码覆盖工具
        • 可重复(Repeatable):每个测试应该独立于其他所有的测试,还必须独立于环境,从而可以重复地执行,并且产生相同的结果。
        • 独立的(Independent):确保一个函数只针对一样测试,并且这个测试不依赖于其他测试。
        • 专业的(Professional):测试代码应该与产品代码的编码风格和编写质量相同

        如何确保测试代码是正确的呢?

        • 对产品代码中的Bug进行修改的时候也改进测试代码;(因为这个Bug是测试代码没有发现的)
        • 在产品代码中引入Bug来验证测试代码的正确性。(确??赡芑岱⑸拇砦蟊徊馐源氩蹲降搅耍?

        8. 在项目中进行测试

        • 把测试代码与产品代码放在一个目录下;
        • 与别人共享代码的时候,需要确保你的代码可以通过所有测试;
        • 测试的时间点:
          • 编写新的函数;
          • 修正Bug;
          • 每次成功编译之后;
          • 每次对版本控制的提交;
          • 持续不断地由专门的机器来运行完整的构建和测试。
        • 测试别人的项目代码:其实就是维护别人的项目绝对是个大问题,同时也是个必须面对的问题。需要理性的态度(不批评别人的代码)、冷静的手段(不随便修改别人的代码)、持久的耐心(先从测试代码开始,慢慢重构项目代码,使之重新回到健康状态)、真正的智慧(知道什么样的项目应该达到什么样的目标,不执着于重构成一个完美的状态,也不简单放弃随之自生自灭。)
        • 测试与评审:三个臭皮匠顶个诸葛亮,放下自我的执着,接纳各种不同的意见,才能做出令自己满意的项目。

        9. 设计话题

        • 面向测试的设计:不方便测试的设计不是好的设计;说明设计过于僵化或者臃肿,需要简化或者修改使之更利用未来的扩展和维护。
        • 面向测试的重构:不方便测试的代码不是好的代码;说明业务混杂在一起,无法实现一个函数只针对一样测试,需要修改设计使业务分离。
        • 测试类的不变性:就是对类的断言必须为真。
          • 有序性。例如:sorted list类的不变性就是无论发生什么,结果都应该是有序的。
          • 结构化。例如:订单系统中每个条目必须属于一个订单,一个订单拥有一个或多个条目。
          • 数学不变性。例如:银行账号的的借贷必须平衡。
          • 数据一致性。例如:商品总数=库存数+销售数。
        • 测试驱动的设计。使你作为产品代码的用户在编码,而不是产品开发者在编码,开发结果更能反应用户的需求。
        • 测试无效的参数。当你作为产品代码的用户时,你才能真正确定哪些责任应该你来承担,而哪些是不需要的。例如:无效的参数应该由哪个函数来承担检查责任呢?


      • 朱远翔 2019-01-16 17:57 发表评论
        ]]>
        《重构》的读书笔记–方法列表 - ★四川福利彩票快乐12★快乐12开奖直播★快乐12开奖★辽宁福彩快乐12★快乐彩12选5走势图//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/16/433605.html朱远翔朱远翔Wed, 16 Jan 2019 09:50:00 GMT//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/16/433605.html//www.ot7t.com.cn/zhuyuanxiang/comments/433605.html//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/16/433605.html#Feedback0//www.ot7t.com.cn/zhuyuanxiang/comments/commentRss/433605.html//www.ot7t.com.cn/zhuyuanxiang/services/trackbacks/433605.html第5章 重构列表
      • 第6章 重新组织函数
      • 第8章 重新组织数据
      • 第9章 简化条件表达式 237
      • 第10章 简化函数调用
      • 第11章 处理概括关系 319

          第5章 重构列表

          5.1 重构的记录格式103

          这个格式可以作为自己未来记录重构手法的标准,方便查阅自己常用的和总结的重构方式:

          • 名称:重构词汇表
          • 概要:适用的情景,以及所做的事情
          • 动机:说明“为什么需要”和“什么情况不做”
          • 做法:重构的步骤(看似简单,其实很重要,因为重构不建议跳跃完成,最好保证每次一小步都是正确的)
          • 范例:通过例子正确的理解重构手法的操作方式

          5.2 寻找引用点105

          引用点:就是那些被重构的代码被哪些代码调用了。现在的重构工具已经可以简化这些操作,但是工具永远都是有限的,除了工具还应该熟悉一些基本的操作手段。

          5.3 这些重构手法有多成熟106

          学习和使用这些重构仅仅是个起点,业务在变、工具在变、语言在变、对事物的理解也在变,因此完全挺有自己的重构手段才是最终的目标。

          第6章 重新组织函数

          • Extract Method是本章的重点,用于解决Long Methods问题;
          • Extract Method的困难是处理局部变量,特别是局部变量中的临时变量,为此需要本章中的其他方法作为补充。

          6.1 (P110)Extract Method(提炼函数)

          动机:Long Methods问题或者代码需要Comments问题;
          方法:(可以使用IDE提供的重构工具,下面的具体操作的原理)

          • 创造一个新函数,根据这个函数的意图来对它命名;
          • 将提炼的代码拷贝到新建函数中;
          • 检查提炼的代码是否引用了“作用域限于原函数”的变量(局部变量、原函数参数)
          • 检查提炼的代码是否包含了“仅用于被提炼代码段”的临时变量
            • 如果有,在目标函数中将之声明为局部变量
          • 检查被提炼代码段,看看是否有任何局部变量的值被他改变,
            • 如果临时变量的值被修改了,尝试将提炼的代码变为一个查询,将结果返回给相关变量;
            • 如果不好操作,或者被修改的变量多于一个,可以尝试(分解临时变量/以查询替换变量)等手段了。
          • 将被提炼代码段中需要被读取的局部变量,当参数传给目标函数
          • 处理完所有局部变量后,编译,检查
          • 在源函数中,将被提炼的代码替换为对目标函数的调用
          • 编译,检查,测试

          6.2 Inline Method(内联函数)117

          动机:当函数内部的代码与其名称一样易懂,可以在函数调用点插入函数本体,移除该函数。
          补充:对有一群不太合理的函数,可以先内联为一个长函数,然后再提炼出合理的小函数

          6.3 Inline Temp(内联临时变量)119

          动机:当临时变量只被一个简单的表达式赋值一次,而且它妨碍其他重构方法时,才需要重构
          条件:Inline Temp多半是为Replace Temp with Query(以查询取代临时变量)准备
          方法:将所有对该变量的引用动作替代成对它赋值的表达式本身。

          6.4 Replace Temp with Query(以查询取代临时变量)120

          动机:你的程序以一个临时变量保存一个表达式的计算结果
          方法:将表达式提炼出独立的函数,然后临时变量的调用替换成新函数的调用。此后新函数也能被调用。
          具体方法: 将提炼出来的函数用private修饰,如果独立函数有副作用,那对它进行Separate Query from Modifier(将查询函数和修改函数分离)

          6.5 Introduce Explaining Variable(引入解释性变量)124

          将复杂表达式(或者其中一部分)的结果赋值给一个临时变量,用临时变量名称来解释表达式的用途

          6.6 Split Temporary Variable(分解临时变量)128

          临时变量被赋值超过一次,但是它既不是循环变量也不是被用于收集计算结果
          原因:一个变量应该承担一个责任,如果被赋值多次很可能承担了多个责任
          方法:针对每次赋值,创建新的临时变量

          6.7 Remove Assignments to Parameters(移除对参数的赋值)131

          java是值传递,对参数的任何修改都不会对调用端产生影响,所以对于用过引用传递的人可能会发生理解错误
          参数应该仅表示“被传递过来的东西”

          6.8 Replace Method with Method Object(以函数对象取代函数)135

          动机:在大型函数内,对局部变量的使用导致难以使用Extract Method(提炼函数)进行重构
          方法:将这个函数放入一个对象里,局部变量变成对象成员变量,然后可以在同一对象中将这个大型函数分解为多个小型函数。
          原因:局部变量会增加分解函数的困难度

          6.9 Substitute Algorithm(替换算法)139

          把某个算法替换成更清晰的方法(算法)。

          第7章 在对象之间搬移特性141

          本章的重点是搬移(Move)?!熬龆ò言鹑畏旁谀睦铩笔敲嫦蚨韵笊杓浦凶钪匾氖虑橹?,但是刚开始设计时,由于技术和业务知识的不足无法保证做出的决定是正确的,那么后期调整中将“责任”搬移到正确的对象中就是重要的重构手段。而Move Method(142)和Move Field(146)就是搬移“责任”过程中最基本的两个方法。

          7.1 Move Method(搬移函数)142

          动机:类中某个函数与其他类交互过多
          方法:将该函数搬移到交互最多的类里面,将旧函数变成委托函数或者删除。
          具体方法:

          • 检查源类中被源函数使用的一切特性,如果特性被其他函数使用,考虑这些函数一起搬移
          • 检查源类的子类和超类,看看是否有该函数的声明,如果出现,很可能不能搬移。
          • 目标类需要使用源类的特性:
            1. 将该特性转移到目标类;
            2. 建立目标类到源类之间引用。
            3. 将源类作为参数传给目标类
            4. 将该特性作为参数传给目标类
          • 如果源函数包含异常处理,需要考虑是在目标类还是源函数处理

          7.2 Move Field(搬移字段)146

          动机:类中某个字段被其他类频繁使用(包括:传参数、调用取值函数、调用设值函数)
          方法:将该字段搬移到目标类
          具体方法:

          • 先封装这个字段;
          • 在目标类建立这个字段,并且封装;
          • 设定目标对象;
          • 替换对源字段的引用为目标类的取值函数

          7.3 Extract Class(提炼类)149

          动机:一个类做了两个类的事
          方法:

          • 建立新类,将相应的字段和函数放到新类
          • 使用Move Field重构;
          • 使用Move Method重构;
          • 判断是否需要公开新类。

          7.4 Inline Class(将类内联化)154

          动机:某个类功能太少,与Extract Class(提炼类)相反 方法:将这个类的所有特性搬移到另一类中,移除该类。
          原因:多次Extract Class后,原类大部分功能被移走,将这个萎缩类与其他相近的类合并

          7.5 Hide Delegate(隐藏“委托关系”)157

          动机:客户端通过委托类来取得另一个对象的信息
          方法:在服务类上建立客户端所需数据的函数,然后隐藏委托关系
          依据:符合“封装”的特性。当委托类发生变化不会对客户端造成影响,减少客户端与调用者之间的耦合性。

          7.6 Remove Middle Man(移除中间人)160

          动机:某个类做了过多的委托动作
          方法:让客户端直接调用委托类,与Hide Delegate(隐藏“委托关系”)相反 依据:当原委托类的特性越来越多,服务类的委托函数将越来越长,需要让客户端直接调用,避免服务类沦为中间人。

          7.7 Introduce Foreign Method(引入外加函数)162

          动机:使用的类无法提供某个功能,但是又不能修改该类
          方法:新建函数,并将服务类的对象实例作为参数传入。
          具体动机:如果需要为服务类增加大量的方法,请考虑使用Introduce Local Extension(引入本地扩展)

          7.8 Introduce Local Extension(引入本地扩展)164

          动机:使用的类无法提供多个功能,但是又不能修改该类
          方法:建立新的类,在新类中建立需要的功能函数,可以作为服务类的子类实现新的类,也可以包装服务类实现新的类。
          具体情况:

          • 首选子类,工作量最小
            • 但是必须在对象创建期实施,如果不行就只能选择包装类;
            • 子类的对象不能修改父类的数据,否则建议选择包装类,因为会导致父类对象与子类对象的数据可能不一致
          • 包装类需要实现被包装对象的所有接口,工作量很大。

          第8章 重新组织数据

          本章重点是如何更好地封装各种类型的数据。最常用的手段就是Self Encapsulate Field(171)

          8.1 Self Encapsulate Field(自封装字段)171

          动机:直接访问一个字段,但是字段之间的耦合关系逐渐变得笨拙。
          方法:自封装就是在对于类内部的字段也封装一个设值取值的函数。
          争论:字段访问方式是直接访问还是间接访问一致争论不断
          间接访问的好处:

          • 子类可以通过覆盖一个函数来改变获取数据的途径;
          • 支持更灵活的数据管理,如延迟加载(需要用到才加载)等。直接访问的好处:代码容易读懂,理解不需要转换为取值函数。

          8.2 Replace Data Value with Object(以对象取代数据值)175

          动机:假如一个数据项需要与其他数据一起使用才有意义。数据已经不仅仅由一条简单的数据项组成,例如:电话号码
          方法:将数据变成对象。

          8.3 Change Value to Reference(将值对象改为引用对象)179

          动机:一个类有许多相等的实例,希望把这些相等的实例统一为一个对象,方便统一修改或者进行相等性比较
          方法:将值对象变成引用对象
          “引用对象”与“值对象”的区别:

          • 每个引用对象代表着现实中一个对象,使用对象的一致性用来检测两个对象是否相等,即(==)
          • 值对象完全由其自身的值来相互区分,需要重写一些方法用来检测两个对象是否相等。(重写equals()和hashcode()方法)具体方法:
          • 需要使用工厂模式来创建对象
          • 需要另一个对象(或者是自身)作为访问点来访问定义的引用对象,对象用Dictionary或者HashTable来保存对象
          • 决定对象是预先创建还是动态创建

          8.4 Change Reference to Value(将引用对象改为值对象)183

          动机:引用对象,很小且不可变,而且不易管理

          • 很?。捍唇ㄐ矶嘁膊换嵯奶嗄诖?
          • 不可变:不需要复杂的管理代码,也不需要考虑同步问题,还会造成别名问题具体方法:
          • 检查重构目标是否是不可变对象或者可修改成不可变对象
            • 使用Remove Setting Method变成不可变对象
            • 如果无法修改成不可变对象,就放弃重构
          • 重写hashCode和equals()方法
          • 取消使用的工厂模式,并将对象的构造函数设为public

          8.5 Replace Array with Object(以对象取代数组)186

          动机:如果数据存储的值代表不同的东西。
          方法:将数组变成对象,数组的每个元素用字段表示

          8.6 Duplicate Observed Data(复制“被监视数据”)189

          动机: 有业务数据置身于GUI控件中,而与业务相关的函数需要访问这些数据
          方法:将业务数据复制到业务类中。建立Observer模式,同步UI和业务类的数据。

          8.7 Change Unidirectional Association to Bidirectional(将单向关联改为双向关联)197

          动机:两个类相互之间都需要对方的数据,但是相互之间只有一条单向的连接
          这个重构需要添加测试,因为“反向指针”很容易造成混乱。
          具体方法:

          • 在被引用类添加字段,保存引用类的指针;
          • 判断由哪个类来控制关联关系;
            • 如果两者都是引用对象,且关联关系为“一对多”的关系,那么就由“拥有单一引用”的对象作为控制者;
            • 如果A对象是B对象的部件,则由B对象负责控制关系;
            • 如果两者都是引用对象,且关联关系为“多对多”的关系,那么随意确定一个对象作为控制者。
          • 在被控端建立辅助函数,命名清晰地描述其用途;
            • 如果修改函数在控制端,则由其负责更新反向指针;
            • 如果修改函数在被控制端,则在控制端建立一个修改反射指针的函数,由修改函数调用其修改反向指针。
            • 两者是一对多关系,有单一引用承??刂乒亓叵翟鹑?

          8.8 Change Bidirectional Association to Unidirectional(将双向关联改为单向关联)200

          动机:两个类有双向关联,但是一个类不再需要另一个类的特性
          原因:

          • 双向关联可能造成僵尸对象,不能被清除释放内存。
          • 使两个类存在耦合关系,一个类的变化会导致另一类的变化。方法:去除双向关联
            困难:检查可行性

          8.9 Replace Magic Number with Symbolic Constant(以字面常量取代魔法数)204

          动机:有一个字面常量(除了0和1之外)
          方法:创建常量赋值以该字面常量,给予命名。

          8.10 Encapsulate Field(封装字段)206

          动机:一个类有public字段
          将它声明为private,并提供相应的访问函数

          8.11 Encapsulate Collection(封装集合)208

          动机:类中使用集合,但是集合不能提供给用户直接操作,而是提供函数操作集合,降低用户与集合之间的耦合度
          方法:提供函数返回集合的只读副本,并提供增加和删除集合元素的函数
          具体方法:

          • Java2:封装Set
          • Java1.1:封装Vector
          • 封装数组

          8.12 Replace Record with Data Class(以数据类取代记录)217

          动机:面对旧程序中Record数据结构,新建类取代它
          方法:为该记录创建一个“哑”数据对象。

          Type Code(类型码)

          常见于过去的C语言编程中,因为没有枚举,所以采用类型码的方式标注。这个重构遇到的机会比较小

          8.13 Replace Type Code with Class(以类取代类型码)218

          动机:类中的数值类型码不影响类的行为
          方法:以一个新类替代类型码

          8.14 Replace Type Code with Subclasses(以子类取代类型码)223

          动机:有一个不可变的类型码且影响类的行为
          标志:switch或者if-then-else类的条件表达式,这个重构是Replace Conditional with Polymorphism的准备工具
          方法:以子类取代这个类型码

          8.15 Replace Type Code with State/Strategy(以State/Strategy取代类型码)227

          动机:有一个类型码且影响类的行为,但是无法通过继承消除(类型码可变化)
          方法:以状态对象取代。

          8.16 Replace Subclass with Fields(以字段取代子类)232

          动机:各个子类唯一区别只在“返回常量的数据”的函数上
          方法:修改这些函数使它们返回超类的某个(新增)字段,然后销毁子类。

          第9章 简化条件表达式 237

          条件逻辑非常复杂,也非常难以理解,通过重构将复杂的逻辑展现为简单的逻辑块。
          有些重构方法看起来非常简单,因为重构最重要的思想不是方法有多精妙,而是传达了一个小步快走的理念。就是一次只完成一个小重构,然后测试确保没有错误。然后,再进行下一个小重构和测试。从而整个大重构通过多个简单的小重构完成,避免大重构出错后需要全部回滚的问题。

          9.1 Decompose Conditional(分解条件表达式)238

          动机:if-then-else语句,不同分支做不同事动机成大型函数,本身就难以阅读,尤其在带有复杂条件的逻辑中。方法:

          • 将if语句提炼为函数
          • 将then和else段落提炼为函数
          • 对于存在嵌套的条件逻辑,先判断是否可以用Replace Nested Conditional with Guard Clauses(以卫语句取代嵌套条件表达式)消除。不行再分解每个条件

          9.2 Consolidate Conditional Expression(合并条件表达式)240

          动机:有一系列条件判断都服务于共同的目标
          方法:将这些条件判断合并为同一个表达式,再将这个表达式提炼为独立函数
          原因:

          • 只是一次条件检查,只是存在多个并列条件需要检查而已
          • 为Extract Method(提炼函数)做准备,通过函数名告知“为什么这么做”

          9.3 Consolidate Duplicate Conditional Fragments(合并重复的条件片段)243

          动机:在条件表达式的不同分支中存在相同的代码
          方法:将这些重复代码搬移到条件表达式之外,多行代码还可以再提炼为独立函数。
          例如:当try和catch执行相同代码,可以将代码移到final区段。

          9.4 Remove Control Flag(移除控制标记)245

          动机:在循环执行的程序段中,某个变量定义为判断条件中的控制标记(control flag),增加了代码理解的复杂度
          方法:

          • 以break或者continue代替;
          • 也可以通过函数调用和return语句来实现。

          9.5 Replace Nested Conditional with Guard Clauses(以卫语句取代嵌套条件表达式)250

          卫语句:如果某个条件极其罕见,就应该单独检查该条件,并在该条件为真时立刻从函数中返回,这样的单独检查被称为“卫语句”(guard clauses)
          动机:函数中的条件逻辑使人难以看清正确的执行路径。
          方法:使用卫语句表现所有的特殊情况

          9.6 Replace Conditional with Polymorphism(以多态取代条件表达式)255

          动机:存在条件表达式根据对象的类型不同选择不同的行为
          方法:将表达式分支放进不同子类,然后重写方法,将原始函数提炼为抽象函数。

          9.7 Introduce Null Object(引入Null对象)260

          动机:需要再三检查对象是否为null
          方法:将null值替代为null对象,如果原始类不允许修改可以使用Null接口来检查“对象是否为Null”。

          9.8 Introduce Assertion(引入断言)267

          动机:某段代码需要对程序状态显式地表明某种假设
          方法:以断言明确表现这种假设
          具体方法: 断言在 发布的时候统统 被跳过

          第10章 简化函数调用

          使接口变得更加简洁易用的重构方法。

          • 修改函数名称,使之容易理解;
          • 缩短参数列表;
          • 不同的功能分离到不同的函数中;
          • 隐藏函数,提升接口的质量。

          10.1 Rename Method(函数改名)273

          动机:函数的名称不能说明函数的用途
          方法:将旧函数代码搬移到新函数,旧函数跳转到新函数。

          10.2 Add Parameter(添加参数)275

          动机:被调用的函数需要从调用函数中得到更多的信息
          方法:为被调用的函数添加参数
          抉择:

          • 现有参数是否提供足够的信息?
          • 这个函数是否应该移动到拥有该信息的对象中?
          • 加入新参数是否合适?
          • 如果需要的参数过多,是否需要使用Introduce Parameter Object(引入参数对象)?

          10.3 Remove Parameter(移除参数)277

          动机:函数不需要某个参数(不需要了就放弃,保留也需要付出代价)
          方法:

          • 如果是独立的函数,直接将该参数移除
          • 如果是多态函数,不能移除,就增加一个新的没有这个参数的函数,使调用者的工作得到简化

          10.4 Separate Query from Modifier(将查询函数和修改函数分离)279

          动机:某个函数既修改对象状态,又返回对象状态值。(使调用者担心误操作修改了不应该修改的数据,增加调用者的操作负担)
          本质:函数功能简洁、明确,如果一个函数具备多个功能,就把它们分离成多个函数。
          方法:建立两个不同的函数,其中一个负责查询,另一个负责修改。
          原则:

          • 任何一个有返回值的函数都不应该有看得到的副作用。
          • 编码中主要考虑的不是代码的效率,而是代码的易读性,效率可以在未来上线的时候再根据实际需要调整。多线程:将修改和查询函数封装在一个同步函数中分开调用。

          10.5 Parameterize Method(令函数携带参数)283

          动机:几个函数,做了类似的工作,只是代码中的系数不同
          方法:建立单一函数,以参数作为系数

          10.6 Replace Parameter with Explicit Methods(以明确函数取代参数)285

          动机:函数依赖于参数值的不同而采取不同的行为
          方法:针对该参数的每个可能值,建立独立函数。
          对比:与Parameterize Method(令函数携带参数)相反,但是目的都是把复杂的逻辑判断消除 目的:提供清晰的入口。
          如果参数值对函数行为影响不大,不应该采用此方法。

          10.7 Preserve Whole Object(保持对象完整)288

          动机:从某个对象取若干个值,把他们作为参数传给函数
          方法:改为调用整个对象
          目的:避免过长参数列表
          缺陷:如果传递的是值,那么函数只依赖那些值;如果传递的是对象,函数则依赖对象,会导致耦合
          注意:有时候函数使用了很多来自某个对象的数据,那么应该考虑使用(Move Method)将这个函数移到关系密切的对象中

          10.8 Replace Parameter with Methods(以函数取代参数)292

          动机:对象调用某个函数,并将所得结果作为参数传递给另一个函数,而接受该参数的函数本身也能够调用前一个函数
          方法:让参数接受者去除该项参数,并直接调用前一个函数

          10.9 Introduce Parameter Object(引入参数对象)295

          动机:有些参数总是自然地同时出现
          方法:用一个对象把这些参数包装起来进行传递
          目的:

          • 缩短参数列表长度;
          • 函数具有一致性,降低理解和修改代码的难度

          10.10 Remove Setting Method(移除设值函数)300

          动机:类的某个字段应该对象创建的时候被设置,然后不再改变
          方法:去掉该字段的设置函数

          • 如果对参数的运算很简单,而且只有一个构造函数,就可以直接在构造函数中初始化。
          • 如果修改复杂,或者有多个函数试图改变这个字段,那么就需要提供一个独立函数,并给予独立函数一个清楚表达用途的名字
          • 如果是子类希望修改超类的字段
            • 那么最好是使用超类的构造器实现改变;
            • 或者通过拥有能够清楚表达用途的名字的函数来实现。
          • 如果修改集合字段,请使用Encapsulate Collection(208)实现。

          10.11 Hide Method(隐藏函数)303

          动机:有一个函数,从来没有被任何类调用
          方法:将该函数设为private
          补充:函数可见度不够,在编译的时候就可以发现;而函数过见度过高,则需要通过一些工具(Lint)来辅助检查。

          10.12 Replace Constructor with Factory Method(以工厂函数取代构造函数)304

          动机:创建对象时不仅仅是做简单的构建动作方法:将构造函数替换为工厂模式范例:

          • 根据整数(实际是类型码)创建对象;
          • 根据字符串创建子类对象;
          • 以函数创建子类;

          10.13 Encapsulate Downcast(封装向下转型)308

          动机:某个函数返回的对象,需要由函数调用者执行向下转型(downcast)
          方法:将向下转型移到函数中

          10.14 Replace Error Code with Exception(以异常取代错误码)310

          动机:某个函数返回一个特定的代码,表示某个错误的情况
          方法:取消那个代码判断,改用抛出异常
          范例:

          • 非受控异常:使用守卫语句检查这个异常情况;
          • 受控异常:需要修改的调用者函数和被调用者函数,步骤太大,容易出错??梢韵却唇ㄒ桓隽偈钡闹屑浜?,保留原函数,使所有的调用都改为新函数后,删除原函数,再修改新函数名称,即可。

          10.15 Replace Exception with Test(以测试取代异常)315

          动机:本该由调用者自行检查的条件,由被调用者抛出了一个可控异常。
          方法:修改调用者,使它在调用函数之前做检查。
          补充:异常就应该放在可能发生异常的地方使用。即可以预测的,可以通过检查避免的,那就是错误,不该发生;不能预测的,无法通过检查避免的,那就是异常。例如:账户余额小于取钱数目,申请取钱这个就是错误;账户余额大于取钱数目,取不出钱来就是异常。

          第11章 处理概括关系 319

          概括关系(generalization,即继承关系、泛化关系)

          11.1 Pull Up Field(字段上移)320

          动机:两个子类拥有相同的字段
          方法:

          • 将该字段移动到超类,去除重复数据声明;
          • 将使用该字段的行为搬移到超类,去除关于这个字段的重复行为。
          • 考虑对超类的该字段使用Self Encapsulate Field(171)

          11.2 Pull Up Method(函数上移)322

          动机:有些函数,在各个子类产生相同的结果。
          方法:

          • 将该函数移动到超类
          • 如果被提升的函数引用了子类中的函数
            • 如果可以将引用函数提升,就一起提升
            • 如果不可以将引用函数提升,可以在超类里面那个抽象函数

          11.3 Pull Up Constructor Body(构造函数本体上移)325

          动机:你在各个子类拥有一些构造函数,它们的本地几乎完全一致
          方法:在超类新建一个构造函数,并在子类构造函数中调用它。
          具体方法:

          • 将共同代码放在子类构造函数起始处,然后再复制到超类构造函数中。
          • 将子类构造函数中共同代码删除,改用调用新建的超类构造函数。

          11.4 Push Down Method(函数下移)328

          动机:超类中的某个函数只与部分而非全部子类有关
          方法:将这个函数移到相关的子类去。

          11.5 Push Down Field(字段下移)329

          动机:超类中的某个字段只被部分而非全部子类使用
          方法:将这个字段移到需要它的那些子类去。

          11.6 Extract Subclass(提炼子类)330

          动机:类中的某些特性只被部分实例用到。
          方法:新建一个子类,将上面所说的那一部分特性移到子类中。
          具体情况:

          • 并不是出现类型码就表示需要用到子类,可以在委托和继承之间做选择。
          • 为子类新建构造函数,
            • 子类构造函数与超类构造函数拥有相同的参数列表,并且直接调用超类构造函数
            • 如果需要隐藏子类,可使用Replace Constructor with Factory Method(以工厂函数取代构造函数)
          • 找出超类调用点
            • 如果超类构造函数与子类不同,通过rename method方法可以解决。
            • 如果不需要超类实例,可以将超类声明为抽象类。
          • 逐一使用函数下移和字段下移将源类的特性移动到子类。

          11.7 Extract Superclass(提炼超类)336

          动机:两个类有相似特性。
          方法:为两个类建立一个超类,将相同特性移至超类。
          补充:Extract Class,Extract Subclass,Extract Superclass对比学习。

          11.8 Extract Interface(提炼接口)341

          动机:多个用户只使用类接口中的同一子集,或者两个类的接口有部分相同。
          方法:将相同子集提炼到独立的接口中。
          区别:提炼超类是提炼共同代码,提炼接口时提炼共同接口。
          具体动机:如果某个类在不同环境下扮演截然不同的角色,使用接口就是个好主意。接口还能帮助类隐藏一些对外的函数接口。

          11.9 Collapse Hierarchy(折叠继承体系)344

          动机:超类和子类之间区别不大。
          方法:将它们合为一体。

          11.10 Form TemPlate Method(塑造模板函数)344

          动机:你有一些子类,其中相应的函数以相同顺序执行类似的操作,但各个操作的细节有所不同。
          方法:将这些小操作分别放进独立函数中,并保持它们都有相同的签名,于是原函数也变得相同了。然后将原函数上移至超类,运用多态来避免重复代码。这样的原函数就是Template Method。
          原因:虽然使用了继承,但是函数重复应尽量避免。

          11.11 Replace inherited with Delegation(以委托取代继承)352

          动机:某个子类只使用超类接口中一部分,或是根本不需要继承而来的数据方法:在子类中新建一个字段用以保存超类,调整子类函数,令它委托超类,然后去掉两者之间的继承关系。

          11.12 Replace Delegation with Inherited(以继承取代委托)352

          动机:在两个类之间使用委托关系,并经常为整个接口编写许多极简单的委托函数,方法:让委托类继承受托类。注意:

          • 如果并没有使用受托类的所有函数,那么就不要使用这个方法。因为子类应该总是遵循超类的接口,如果委托过多可以通过Remove Middle Man(160)方法让客户端调用受托函数,或者Extract Superclass(336)让两个类的接口提炼到超类中;还可以使用Extract Interface(341)方法。
          • 如果受托对象被不止一个其他对象共享,而且受托对象是可变的时候,那么这种情况下,不能将委托关系替换为继承关系,因为这样就无法共享数据了。数据共享是委托关系的一种重要功能。


        • 朱远翔 2019-01-16 17:50 发表评论
          ]]>
          《重构》的读书笔记 - ★四川福利彩票快乐12★快乐12开奖直播★快乐12开奖★辽宁福彩快乐12★快乐彩12选5走势图//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/16/433604.html朱远翔朱远翔Wed, 16 Jan 2019 09:48:00 GMT//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/16/433604.html//www.ot7t.com.cn/zhuyuanxiang/comments/433604.html//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/16/433604.html#Feedback0//www.ot7t.com.cn/zhuyuanxiang/comments/commentRss/433604.html//www.ot7t.com.cn/zhuyuanxiang/services/trackbacks/433604.html总览
        • 第1章 重构,第一个案例
        • 第2章 重构原则
        • 第3章 代码的坏味道
        • 第4章 构筑测试体系
        • 第12章 大型重构359
        • 第13章 重构,复用与现实379
        • 第14章 重构工具401

            《重构》的读书笔记

            学习基?。?/h3>

            熟悉《设计模式》的基本概念,熟悉基本的Java语法,熟悉Eclipse和JUnit的使用,有相对较好的英语基础。

            学习过程:

            • 先看第1章,手工输入实例程序,了解重构的方法和过程。重点是理解重构的思路,最好的理解方式就是通过实践的方式理解。
            • 再看第2~4章,内容为选择性阅读,没兴趣或者看不懂的都可以跳过,因为后面还可以回头再读。
            • 接着第5~12章,最好按顺序把代码一个个输入,再按照作者的步骤重构操作一次,并结合自己以往工作中的实践来理解。

            学习目的:

            使自己编写的代码更容易被人读懂。

            学习感悟:

            • 代码的重构应该是一步步完成的,每次重构的部分不要超过自己的理解能力的5%。虽然这样操作略显繁琐,但是可以减轻头脑重构过程中的记忆强度,减少代码出错的机会。
            • 代码的重构一定要配合JUnit(TDD,测试驱动开发)完成,再加上Git(版本管理)和Eclipse(IDE的重构工具)那就事半功倍了。

            学习代码:

            Refactored-MartinFowler

            总览

            • 第1章(必读),从实例程序出发,了解重构的方法和过程。
            • 第2章,讨论重构的一般性原则、定义和进行重构的原因。
            • 第3章,介绍如何判断问题代码,以及如何用重构改善它们。
            • 第4章,在代码中构建java的测试环境
            • 第5~12章,具体面对的问题和重构的方法。
            • 第13章,Bill Opdyke在商业开发中应用重构
            • 第14章,自动化重构工具(今天看来,已经不是太大问题,Eclipse的Refactor已经非常好用)
            • 第15章,重构的哲学思想

            第1章 重构,第一个案例

            1.1 (P1)起点

            因为代码的结构无法满足添加新功能的需要,因此先进行重构,使代码方便添加新功能,然后再添加新功能。

            1.2 (P7)重构的第一步

            首先确认自己拥有一套可靠的测试机制,因为重构有可能引入问题,通过测试保证重构没有改变程序功能。

            第2章 重构原则

            2.1 (P53)何谓重构

            重构(名词):对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高理解性和降低修改成本。
            重构(动词):使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。
            定义的扩展:

            • 重构让软件更容易理解和修改
            • 重构不会改变软件的可观察行为,即使改变也只能是微小的影响,软件功能一如既往。

            重构的目标:只改变程序内部结构,不添加新的功能

            不要一心二用:

            • 添加新功能的时候,不重构已有代码。
            • 重构代码时,不增加新功能。

            2.2 (P55)为何重构

            • 重构改进软件设计:
              • 程序的设计质量在没有持续重构的情况下逐渐变差,功能的增加或者修改都可能使代码越来越难以理解和维护,就越难保证最初的设计目标
              • 消除重复的代码一方面是程序运行更快,一方面是方便未来的修改,例如:重构减少代码重复,避免功能的改变需要修改多处代码。
            • 重构使软件更容易理解:
              • 及时填补“想要它做什么”和“告诉它做什么”之间的缝隙。重构的核心就是要“准确说出我所要的”
              • 重新阅读代码的人有可能是自己,也可能是他人。
              • 通过重构可以把自己不熟悉的代码的用途梳理一遍,加深对代码的理解
            • 重构帮助找出bug:
              • 这个是建立在代码容易理解之上的
            • 重构提高编程速度:
              • 重构达到良好的设计,而良好的设计更容易修改代码、增加功能、问题调试。

            2.3 (P57)何时重构

            重构的三次法则:

            • 第一次开发某个功能的时候以实现为目标。
            • 第二次开发相同功能的时候,克制自己的反感,继续重复实现。
            • 第三次开发相同功能的时候,应该重构。

            重构的时间点:

            • 添加功能时重构:
              • 一方面可以帮助理解需要修改的代码
              • 一方面是使现在以及未来增加新功能更加容易。
            • 修补错误时重构:
              • 出现bug的时候,难以找出问题所在的时候,很有可能是代码不清晰导致查找bug的困难。
            • 复审代码时重构:
              • 复审代码有助于知识的传播,有利于代码被编写者之外的人理解。
              • 重构加深了对代码的理解,有利于提升复审代码的能力

            复审团队:只要代码作者和一个审查者者。较大的项目可以通过UML图去展示代码的逻辑。

            程序难以修改的原因:

            • 难以阅读的程序
            • 逻辑重复的程序
            • 添加新特性需要修改已有代码的程序
            • 带复杂逻辑判断的程序

            重构的目标:

            • 代码容易阅读
            • 所有逻辑都只有唯一地点指定
            • 新的改动不会危及现有行为
            • 尽可能简单表达逻辑

            2.4 (P60)怎么对经理说

            • 懂技术的经理,很容易沟通;
            • 追求质量的经理,介绍重构对质量的帮助;
            • 追求进度的经理,则自己安静地重构。因为重构可以最快的完成任务,就是对经理最大的帮助。

            间接访问

            很多时候重构都为程序引入间接访问:

            • 把大型对象拆分成小对象
            • 把大型函数拆分为小型函数。

            间接访问的价值:

            • 允许逻辑共享:一个函数在不同地点被调用。子类共享超类的方法。
            • 分开解释意图和实现:通过类名和函数名解释自己的意图
            • 隔离变化:在不同地方使用同一个对象,需要修改一处逻辑,那么可以做出子类,并在需要的时候修改这个子类。
            • 封装条件逻辑:运用多态。将条件逻辑转化为消息模式。

            减少间接层的条件:当间接层只在一处使用,那么需要将其消除。

            2.5 (P62)重构的难题

            数据库重构:

            • 存在问题:
              • 程序与数据库耦合在一起。
              • 数据迁移。
            • 解决方案:
              • 在非关系型数据库,可以在数据库和对象模型中插入一个分离层,隔离两者之间的变化

            接口重构

            • 对于已经发布的接口需要可能需要维护旧接口和新接口,用deprecated修饰旧接口。
            • 不发布新接口,在旧接口中调用新接口。
            • 假如新接口抛出编译时异常,那么可以在旧接口中调用新接口并将编译时异常转化为运行时异常。

            不重构的条件:

            • 重构之前,代码在大部分情况下都能够正常运行,就可以重构,否则应该是重写。
            • 到了Deadline,应该避免重构。

            2.6 (P66)重构与设计

            重构与设计是彼此互补的:

            • 设计应该在编码之前,但是设计总有缺陷,随着对问题认识的逐渐深入,通过重构可以改善设计的质量。
            • 重构减轻了设计的难度和压力,在程序不断修改的过程中逐步完善程序的设计。

            2.7 (P69)重构与性能

            重构是有可能导致程序运行变慢的,但是不需要在设计和编码时就考虑性能问题。例如:实时程序的编写:

            • 首先写出可调的程序
            • 然后调整它以达到性能的要求。
              • 经过分析大部分程序的主要时间是消耗在小部分代码上,所以不用对所有代码进行优化。
              • 性能优化放在开发的后期,利用分析工具找出消耗大量时间的代码,然后集中优化。

            第3章 代码的坏味道

            3.1 (P76)Duplicated Code(重复代码)

            • 同个类两个函数存在相同表达式:Extract Method(提炼函数)
            • 互为兄弟类内存在相同表达式:
              • Extract Method→PullUp Method(函数上移)
              • 如果代码只是相似:先运用Extract Method(提炼函数)分开再Form TemPlate Method(塑造模板函数)
            • 两个毫不相干的类存在重复代码:Extract Class(提炼类)

            3.2 (P76)Long Method(过长函数)

            原则:尽量利用函数名称来解释用途,而不是注释。
            关键:代码主要用来描述“做什么”,而不是描述“怎么做”。例如:getAge()表达获取年龄,而today-birthday就增加了理解的间接性,虽然看代码的人也能明白含义,但是就需要多想一下,并且birthday有可能表达的不是某个人的出生日期呢,而是某个买回来的产品的呢?那可能表达的就是使用时长了。
            具体情况:

            • 函数有大量参数和临时变量:Extract Method(提炼函数)
            • 用Replace Temp with Query(以查询取代临时变量)消除临时变量
            • 用Introduce Parameter Object(引入参数对象)或者Preserve Whole Object(保持对象完整)来将多长的参数列表变得简洁一点。
            • 如果按照上述步骤还存在太多变量和参数就需要用到Replace Method with Method Object(以函数对象取代函数)
            • 条件表达式可以用Decompose Conditional(分解条件表达式)解决
            • 可以将循环内的代码提炼为函数。

            3.3 (P78)Large Class(过大的类)

            有时候类并非在所有时刻都使用实例变量:使用Extract Method和Extract Subclass(提炼子类)

            类中有太多代码:

            • Extract Class(提炼类)
            • Extract Subclass(提炼子类)
            • Extract Interface(提供接口)分解类的行为。存在GUI的时候,可以Duplicate Observed Data(复制“被监视数据”),分离数据和行为到业务模型中去。

            3.4 (P78)Long Parameter List(过长参数列)

            • 如果可以调用已有对象获取的话可以使用Replace Parameter with Methods(以函数取代参数)
            • 将来自同一对象的数据收集起来,以该对象替代:Preserve Whole Object(保持对象完整)
            • 如果几个参数总是同时出现,那么可以考虑Introduce Parameter Object(引入参数对象)

            3.5 (P79)Divergent Change(发散式变化)

            不同的变化影响着相同的类发生改变,即变化的认知有分歧(Divergent)。通过Extract Class把不同的功能封装到不同的类中,使每个类只因一种变化而需要修改

            3.6 (P80)Shotgun Surgery(霰弹式修改)

            相同的变化会涉及到多个类发生修改,类似霰弹枪射击的效果。
            可以通过Extract Method,Move Method,Inline Class把一种变化产生的多个修改移到同一个类中。

            对比:

            • Divergent Change(发散式变化)是一个类受到的多个变化影响;
            • Shotgun Surgery(霰弹式修改)是一个变化引起多个类需要修改。

            3.7 (P80)Feature Envy(依恋情结)

            类中的某个函数对其他类的依赖度过高,则应该通过Move Method(移动函数)将它搬移到合适的类中。

            3.8 (P81)Data Clumps(数据泥团)

            数据项总是成群结队出现,通过Extract Class将它们提炼到一个独立对象中,从而缩短参数列表,简化函数调用。

            判断数据项是否相关的方法:如果这些数据项不在一起时就失去了明确的含义,那么就可以把它们提炼成一个新的对象。

            3.9 (P81)Primitive Obsession(基本类型偏执)

            • 有些字段可以用对象表示更准确Replace Data Value with Object(以对象取代数据值)
            • 对于不影响行为的类型码可以Replace Type Code with Class(以类取代类型码)
            • 影响行为的类型码可以Replace Type Code with Subclasses(以子类取代类型码),类型码在运行时会变化就用Replace Type Code with State/Strategy(以State/Strategy取代类型码)

            3.10 (P82)Switch Statements(switch惊悚现身)

            • 使用Replace Type Code with Subclasses(以子类取代类型码)或者Replace Type Code with State/Strategy(以State/Strategy取代类型码)
            • 轻量级的解决方法:Replace Parameter with Explicit Methods(以明确函数取代参数)

            3.11 (P83)Parallel Inheritance Hierarchies(平行继承体系)

            每当为一个类增加子类必须也为另外一个类增加一个子类,那么就让一个继承体系的实例引用另一个继承体系的实例。

            3.12 (P83)Lazy Class(冗赘类)

            没用的类,使用Inline Class(内联类)或者Collapse Hierarchy(折叠继承体系)来解决

            3.13 (P83)Speculative Generality(夸夸其谈未来性)

            • 为未来设计的类,使用Inline Class(内联类)或者Collapse Hierarchy(折叠继承体系)来解决
            • 为未来设计的函数参数,使用Remove Parameter(移除参数)
            • 函数名称啰嗦,使用Rename Method(函数改名)

            3.14 (P84)Temporary Field(令人迷惑的暂时字段)

            对象中某个字段仅为特定情况而设。使用Extract Class(提炼类)将这个字段提取出来

            3.15 (P84)Message Chains(过度耦合的消息链)

            消息链:用户通过一个对象获取另一个对象,再通过获取的对象请求另一个对象,如此操作就是消息链。采取这种方式意味着客户代码将与查找过程中的导航结构紧密耦合,可以使用Hide Delegate(隐藏“委托关系”)进行重构。但是谨慎处理!

            3.16 (P85)Middle Man(中间人)

            过度委托形成中间人:Remove Middle Man(移除中间人)

            如果中间人还有其他行为,Replace Delegation with Inherited(以继承取代委托)

            3.17 (P85)Inappropriate Intimacy(狎昵关系)

            • 两个类相互依赖过多,花费大量时间去获取对方的private成员内容,使用Move Field(移动字段)和Move Method(移动方法)减少耦合性,或用Change Bidirectional Association to Unidirectional(将双向关联改为单向关联)
            • 如果两个类无法移动相同数据和函数,可以使用Extract Class(提炼类),让他们使用新类进行交互。

            3.18 (P85)Alternative Classes with Different Interfaces(异曲同工的类)

            两个函数做了相同的事情却有不同的函数名称

            3.19 (P86)Incomplete Library Class(不完美的库类)

            库函数功能不足,需要增加一些自定义的功能:

            • 需要加入少量操作,使用Introduce Foreign Method(引入外加函数)
            • 需要加入大量操作,使用Introduce Local Extension(引入本地扩展)

            3.20 (P86)Data Class(幼稚的数据类)

            幼稚的数据类:只有数据没有行为的类,其他类需要对该类的数据进行取值设值操作

            • 使用Encapsulate Field(封装字段)和Encapsulate Collection(封装集合)对字段进行合理地封装
            • 对于不该被其他类修改的字段:Remove Setting Method(移除设值函数)

            3.21 (P87)Refused Bequest(被拒绝的遗赠)

            如果子类不愿意接受超类的所有定义,应该使用Replace inherited with Delegation(以委托取代继承)来处理子类

            3.22 Comments(过多的注释)87

            使用Extract Method(提炼方法)来解决注释过多问题,注释更多应该说明的是“怎么做”,而不是“做什么”,例如:对一个排序函数说明其采用二分法排序,而不是说明它是个排序函数,因为这个说明在函数名称中已经具备。

            第4章 构筑测试体系

            4.1 自测试代码的价值89

            • 确保所有测试都完全自动化,让它们检查自己的测试结果;
            • 一套测试就是一个强大的bug探测器,能够大大缩减查找bug所需要花费的时间。
              • 因为代码刚刚写完,测试出现问题后,心里很清楚自己修改或者添加了哪些东西,可能会在哪里出现了问题。

                4.2 JUnit测试框架91

            • 频繁地运行测试;
            • 每次编译前都进行一次测试;
            • 每天至少执行一次所有的测试。

              4.3 添加更多测试97

            • 编写一个测试并运行起来,好过将所有的测试编好了一起运行。
            • 测试特别需要注意可能出错的边界条件;
            • 对于可能出错的地方,还需要检查是否抛出了预期的异常;
            • 测试不能解决所有bug,但是可以大大减少bug的数量。

            第12章 大型重构359

            大型重构是程序开发必将遇到的,只是不知道在什么时间,用什么样的方式经历。例如:随着时间的推移,河道必定会被水草和垃圾所堵塞,你可以固定时间清淤,也可以放任自流直到崩溃。崩溃后依然会面临总结经验教训,再次重构系统。
            大型重构很难给出具体的操作案例,因为每个大型案例相对于自身来说都是惟一的,是无法复制和重现的??梢愿粗朴胫叵值亩际钦庑┐笮椭毓怪性毯木咛宓南附?,因此这章主要讲的是思想和理念上的内容。
            四个大型重构:

            • Tease Apart Inheritance(362)用于处理混乱的继承体系
              • 某个继承体系同时承担两项责任
              • 建立两个继承体系,其中一个通过委托调用另一个
            • Convert Procedural Design to Objects(368)如何重构过时的编码技术遗留下来的程序
              • 传统过程化风格的代码
              • 将数据记录变成对象,将大块的行为分成小块,再将它们移入到相关对象中
            • Separate Domain from Presentation(370)将业务逻辑与用户界面分隔开来
              • 用户界面类中包含了业务逻辑
              • 将业务逻辑剥离到业务类中,参考:MVC模式
            • Extract Hierarchy(375)将复杂的类转化为一群简单的子类,从而简化系统。
              • 某个类做了太多工作
              • 某个类的部分工作是由大量的条件表达式完成的
              • 建立继承体系,使用子类表示每一种特殊情况

            第13章 重构,复用与现实379

            作为一个博士写的内容,仍然具有学术性较强的风格,可以当作历史资料了解一下重构的发展过程,也可以对重构的思想有更多理论上的认识。

            安全重构(391)

            安全重构的四条篱笆:

            • 相信你自己的编码能力;
            • 相信你的编译器能捕捉你遗漏的错误;
            • 相信你的测试套件能捕捉你和编译器都遗漏的错误;
            • 相信代码复审能捕捉你、编译器和测试套件都遗漏的错误。注:没有100%安全的重构,但是可以通过以上的条件满足你对安全性的最低要求。

            重构工具

            • Eclipse(或其他IDE)自带的重构工具:Refactor;
            • Java(或其他编译器)自带的分析工具:lint;
            • JUnit等自动化的测试工具。

            第14章 重构工具401

            相对于10多年前写的内容,现在许多IDE都已经提供了对大部分重构功能的支持。但是了解重构的基本理念,对于正确地使用重构工具会有很大的帮助。因为成功的重构不依赖于工具,而决定于人,当人做出了正确的决定,合理地使用重构工具辅助自己,才能保证重构的完成。



          • 朱远翔 2019-01-16 17:48 发表评论
            ]]>
            《重构》的读书重点 - ★四川福利彩票快乐12★快乐12开奖直播★快乐12开奖★辽宁福彩快乐12★快乐彩12选5走势图//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/16/433603.html朱远翔朱远翔Wed, 16 Jan 2019 09:46:00 GMT//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/16/433603.html//www.ot7t.com.cn/zhuyuanxiang/comments/433603.html//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/16/433603.html#Feedback0//www.ot7t.com.cn/zhuyuanxiang/comments/commentRss/433603.html//www.ot7t.com.cn/zhuyuanxiang/services/trackbacks/433603.html重构的原因:
            • 改善软件的设计水平;
            • 使软件更加容易被理解;
            • 使软件更加容易寻找错误;
            • 使软件开发速度更快;
            • 使软件代码更好维护。

            重构的时间:

            • 增加函数的时候重构;
            • 修改错误的时候重构;
            • 代码审查的时候重构。

            重构的主要方法

            • Extract/Inline
              • Method
              • Class
                • Subclass
                • Superclass
                • Interface
              • Hierarchy
            • Move
              • Field
              • Method
              • Variable
            • Rename
              • Method
              • Variable
              • Class
            • Pull Up
              • Field
              • Method
              • Constructor Body
            • Push Down
              • Field
              • Method
              • Constructor Body

            重构的工具

            • IDE提供了自动化的代码重构工具;
            • JUnit提供了自动化的测试框架;
            • 设计模式提供了重构的方向;
            • 极限编程提供了完整的开发过程,包括:结队编程、自动化测试、代码审查、及时反馈、代码重构等等。


            朱远翔 2019-01-16 17:46 发表评论
            ]]>
            《重构》的读后感 - ★四川福利彩票快乐12★快乐12开奖直播★快乐12开奖★辽宁福彩快乐12★快乐彩12选5走势图//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/16/433602.html朱远翔朱远翔Wed, 16 Jan 2019 09:43:00 GMT//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/16/433602.html//www.ot7t.com.cn/zhuyuanxiang/comments/433602.html//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/16/433602.html#Feedback0//www.ot7t.com.cn/zhuyuanxiang/comments/commentRss/433602.html//www.ot7t.com.cn/zhuyuanxiang/services/trackbacks/433602.html重构是本好书

            作者优秀,作品优秀,翻译也很优秀。但是,无论多么好的翻译也无法完整传达作者的原意。因此,读之前最好准备英文和中文两个版本,中文读不懂的地方就换英文,英文读的累的地方就换中文。充分利用自己在两种语言上知识储备,可以使读这本书产生事半功倍的效果。

            重构是由需求驱动的

            为什么要重构?不仅仅是个人或团体的喜好(感性驱动),还应该是由客户的需求变更导致项目迭代出现困难,而重构正是解决困难的好办法,于是推动重构(理性驱动)。

            当然,作为重构刚刚入门的程序员一定会到处使用这个大杀器,但是随着技术和经验的成熟,应该走向顺应需求的重构,满足客户需求才是项目的根本。

            重构要有具体的目标

            目标明确,拒绝诱惑。重构的过程也是熟悉业务的过程,检查错误的过程

            重构要把大目标分解成许多个小目标

            • 因为每个小目标才不会超出自己的控制能力,出现错误后也更容易回退。
            • 重构的每个小目标最好能够具备有效地检测机制
            • 重构的大目标必须提供检验机制

            重构最好能使用GIT、JUnit等等好的重构工具辅助

            熟悉重构的理论知识,善用重构的工具。对于工具的理解可参考《工具的意义》



            朱远翔 2019-01-16 17:43 发表评论
            ]]>
            Windows7下chm文件打不开 - ★四川福利彩票快乐12★快乐12开奖直播★快乐12开奖★辽宁福彩快乐12★快乐彩12选5走势图//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/15/433599.html朱远翔朱远翔Tue, 15 Jan 2019 04:05:00 GMT//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/15/433599.html//www.ot7t.com.cn/zhuyuanxiang/comments/433599.html//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/15/433599.html#Feedback0//www.ot7t.com.cn/zhuyuanxiang/comments/commentRss/433599.html//www.ot7t.com.cn/zhuyuanxiang/services/trackbacks/433599.html从网上下载的CHM文件在Windows7系统中无法显示内容,是因为Windows7系统中的浏览器下载的文件是被默认为锁定的,所以打开以后是无法显示里面的具体内容的,解决的办法:

            • 选中这个CHM文件;
            • 点右键,选择“属性”,在“常规”标签栏的最下面,有个“解除锁定”的按钮,点击以后
            • 再正常打开chm文件就可以浏览到里面的内容了。


            朱远翔 2019-01-15 12:05 发表评论
            ]]>
            爱奇艺技术分享:爱奇艺Android客户端启动速度优化实践总结 - ★四川福利彩票快乐12★快乐12开奖直播★快乐12开奖★辽宁福彩快乐12★快乐彩12选5走势图//www.ot7t.com.cn/jb2011/archive/2019/01/14/433598.htmlJack JiangJack JiangMon, 14 Jan 2019 03:53:00 GMT//www.ot7t.com.cn/jb2011/archive/2019/01/14/433598.html//www.ot7t.com.cn/jb2011/comments/433598.html//www.ot7t.com.cn/jb2011/archive/2019/01/14/433598.html#Feedback0//www.ot7t.com.cn/jb2011/comments/commentRss/433598.html//www.ot7t.com.cn/jb2011/services/trackbacks/433598.html阅读全文

            Jack Jiang 2019-01-14 11:53 发表评论
            ]]>
            docker zookeeper kafka安装 - ★四川福利彩票快乐12★快乐12开奖直播★快乐12开奖★辽宁福彩快乐12★快乐彩12选5走势图//www.ot7t.com.cn/lhbjava/archive/2019/01/10/433592.html林的林的Thu, 10 Jan 2019 09:19:00 GMT//www.ot7t.com.cn/lhbjava/archive/2019/01/10/433592.html//www.ot7t.com.cn/lhbjava/comments/433592.html//www.ot7t.com.cn/lhbjava/archive/2019/01/10/433592.html#Feedback0//www.ot7t.com.cn/lhbjava/comments/commentRss/433592.html//www.ot7t.com.cn/lhbjava/services/trackbacks/433592.html阅读全文

            林的 2019-01-10 17:19 发表评论
            ]]>
            Android程序员必知必会的网络通信传输层协议——UDP和TCP - ★四川福利彩票快乐12★快乐12开奖直播★快乐12开奖★辽宁福彩快乐12★快乐彩12选5走势图//www.ot7t.com.cn/jb2011/archive/2019/01/10/433591.htmlJack JiangJack JiangThu, 10 Jan 2019 03:15:00 GMT//www.ot7t.com.cn/jb2011/archive/2019/01/10/433591.html//www.ot7t.com.cn/jb2011/comments/433591.html//www.ot7t.com.cn/jb2011/archive/2019/01/10/433591.html#Feedback0//www.ot7t.com.cn/jb2011/comments/commentRss/433591.html//www.ot7t.com.cn/jb2011/services/trackbacks/433591.html阅读全文

            Jack Jiang 2019-01-10 11:15 发表评论
            ]]>
            重构的意义 - ★四川福利彩票快乐12★快乐12开奖直播★快乐12开奖★辽宁福彩快乐12★快乐彩12选5走势图//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/08/433590.html朱远翔朱远翔Tue, 08 Jan 2019 05:11:00 GMT//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/08/433590.html//www.ot7t.com.cn/zhuyuanxiang/comments/433590.html//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/08/433590.html#Feedback0//www.ot7t.com.cn/zhuyuanxiang/comments/commentRss/433590.html//www.ot7t.com.cn/zhuyuanxiang/services/trackbacks/433590.html重构是什么?

              重构(Refactoring)就是通过调整程序代码改善软件的质量、性能,使其程序的设计模式和架构更趋合理,提高软件的扩展性和维护性。

              但是重构不仅适用于计算机编程。比如:这篇文章就可以依据重构的思路完成,现在是不成熟的版本,不考虑用词,不考虑逻辑,只关注自己内心的想法是否已经表达出来,以后再通过重构使之易读易懂。

            重构能做什么?

              重构最重要的思想就是普通程序也能写出优秀的程序。因为作者把优化代码质量的过程拆解成一个个小的步骤,这样重构一个项目的巨大工作量就变成修改变量名、提取函数、抽取接口等等简单的工作目标。作为一个普通的程序就可以通过实现这些易完成的工作目标来提升自己的编码能力,加深自己的项目认识,从而为最高层次的重构打下基础。

              同时,作者还指出高层次的重构依然是由无数个小目标构成,而不是长时间、大规模地去实现。

            重构应该怎么操作?

              重构本质是极限编程的一部分,完整地实现极限编程才能最大化地发挥重构的价值。而极限编程本身就提倡拥抱变化,增强适应性,因此裁剪极限编程中的功能去适应项目的需求、适应团队的现状才是最好的操作模式。

              比如:这篇文章无法像软件编程一样提供测试案例,也无法实时与读者交流。但是可以通过列出提纲作为总体的测试案例,从而方便在写的过程中不断关注是否偏离主题。同时,尽早集成整篇文章并提交到网站上,可以尽早地与读者进行反馈。

              文章的措辞先不进行反复修改,而只求表达清楚,因为读者的不同会对遣词造句的方式有不同的要求,因此后期可以根据读者的反馈再进行适当地修改。文章的提纲也会在未来根据自己的理解进一步细化,从而增加了更为细致的测试案例,保证文章的论述更加聚焦于主题。



            朱远翔 2019-01-08 13:11 发表评论
            ]]>
            工具的意义 - ★四川福利彩票快乐12★快乐12开奖直播★快乐12开奖★辽宁福彩快乐12★快乐彩12选5走势图//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/08/433589.html朱远翔朱远翔Tue, 08 Jan 2019 03:59:00 GMT//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/08/433589.html//www.ot7t.com.cn/zhuyuanxiang/comments/433589.html//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/08/433589.html#Feedback0//www.ot7t.com.cn/zhuyuanxiang/comments/commentRss/433589.html//www.ot7t.com.cn/zhuyuanxiang/services/trackbacks/433589.html工具是什么?

              工具是人手的延伸,人对工具这个“手”掌握的越好,人就越能随心所欲地使用工具达到目标。

            工具能够做什么?

              优秀的作品应该先产生于内心中,再借助于工具实现它。因此在学习工具的过程中只要把握住学习的目标,满足于达成目标时需要的技术的理解,可以放过那些一时不懂的细节。

            工具应该怎么操作?

              作为普通的程序员需要学会使用工具,增强理解能力,从而真正理解别人的想法,再基于自己的能力帮助他们实现这些想法。

            工具与人应该如何相处?

              工具无所谓先进还是落后,适合应用的需要就是最好的。努力学习新的技术(只要是自己不懂的,工作需要的,都可以统称为新的技术),但是不用焦虑技术的日新月异,因为现实的需要总是有限的,适应当下的需要就是个合格的程序员。

            工具的意义!

              好的电脑,好的开发平台,好的编程语言,好的算法等等不能必然导致好的软件产品诞生,而好的想法才会驱动使用那些好的工具产生优秀的作品。



            朱远翔 2019-01-08 11:59 发表评论
            ]]>
            全面解密QQ红包技术方案:架构、技术实现、移动端优化、创新玩法等 - ★四川福利彩票快乐12★快乐12开奖直播★快乐12开奖★辽宁福彩快乐12★快乐彩12选5走势图//www.ot7t.com.cn/jb2011/archive/2019/01/07/433586.htmlJack JiangJack JiangMon, 07 Jan 2019 04:10:00 GMT//www.ot7t.com.cn/jb2011/archive/2019/01/07/433586.html//www.ot7t.com.cn/jb2011/comments/433586.html//www.ot7t.com.cn/jb2011/archive/2019/01/07/433586.html#Feedback0//www.ot7t.com.cn/jb2011/comments/commentRss/433586.html//www.ot7t.com.cn/jb2011/services/trackbacks/433586.html阅读全文

            Jack Jiang 2019-01-07 12:10 发表评论
            ]]>
            Github上写Blog - ★四川福利彩票快乐12★快乐12开奖直播★快乐12开奖★辽宁福彩快乐12★快乐彩12选5走势图//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/06/433584.html朱远翔朱远翔Sun, 06 Jan 2019 04:47:00 GMT//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/06/433584.html//www.ot7t.com.cn/zhuyuanxiang/comments/433584.html//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/06/433584.html#Feedback0//www.ot7t.com.cn/zhuyuanxiang/comments/commentRss/433584.html//www.ot7t.com.cn/zhuyuanxiang/services/trackbacks/433584.html预备知识:

            必须:Git,GitHub,Jekyll,Markdown,YAML

            可?。篐TML,JavaScript,CSS,XML

            工具:

            可?。篤SCode+Markdown Preview Github Styling,GitHub Desktop

            操作:

            1. 注册一个GitHub的账号,可以使用GitHub Desktop或者GitHub网站;
            2. 创建一个空的项目
              • 在项目里面创建index.md,或者index.html,提交后,博客首页就建好了。
                • 注:index.html的优先级比index.md的高,系统会默认使用index.html显示。(系统需要编译,编译的进度请参考注1)
              • 进入项目的Settings,找到Github Pages,修改Source的选项,默认是None(也就是不开启主页显示模式),改为master branch,点save。页面重新刷新,再找到Source就会出现系统提示的显示地址。(编译没有通过的项目是无法正常显示的)
              • 如果还想换个漂亮的皮肤,就点theme就可以了。
            3. 如果不甘心使用这样简单的主页,你就需要fork一个别人的项目barryclark/jekyll-now;
              • 修改_config.yml文件
                • name:博客的名字
                • description:显示在浏览器上的标题栏的名字
                • avatar:博主的照片(可以从Git的Profile里面取照片的地址)
                • # Flags below are optional:表示以下的内容可以选择性的修改
                  • email,github是我有的,其他的就看个人喜欢,不填内容就不会在网页上显示,也不会出错
                  • sina,douban等国内的需要修改代码才能实现(人懒就先放一下了)
                • permalink在前面加个#把它屏蔽掉,因为默认的方式似乎无法正常显示。
              • Settings设置参考前面的说明。
              • 按照“_post”目录下的“2014-3-3-Hello-World.md”的模式,依据“YYYY-MM-DD-文件名称.md”的标准创建一个新文件,就是你发表的新文章了。
                • 打开主页,就会看到文章的主题出现在主页,点进去就可以看到文章的内容。(系统需要编译,编译的进度请参考注1)
            4. 如果还想更漂亮的的页面,更完整功能可以fork这个项目mmistakes/minimal-mistakes
              • 具体修改可以fork我的项目zhuyuanxiang.github.io,改了中文的地方就是我修改的,并且将需要的一些文件从docs目录下移到了根目录下。

            注1:了解编译的进度,可以点Code的commits,进去后会看到自己本次提交的记录,如果后面有个绿色的就是编译成功了,如果是个红色的×就是编译失败了,失败了主页就无法显示你想要展示的内容。

            注2:如果不喜欢远程编译结果出来后才知道有没有问题,可以自己在本机安装Jekyll,具体方法可以参考其他文章。



            朱远翔 2019-01-06 12:47 发表评论
            ]]>
            《重构与模式》读书笔记 - ★四川福利彩票快乐12★快乐12开奖直播★快乐12开奖★辽宁福彩快乐12★快乐彩12选5走势图//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/02/433579.html朱远翔朱远翔Wed, 02 Jan 2019 03:25:00 GMT//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/02/433579.html//www.ot7t.com.cn/zhuyuanxiang/comments/433579.html//www.ot7t.com.cn/zhuyuanxiang/archive/2019/01/02/433579.html#Feedback0//www.ot7t.com.cn/zhuyuanxiang/comments/commentRss/433579.html//www.ot7t.com.cn/zhuyuanxiang/services/trackbacks/433579.html学习基?。菏煜ぁ渡杓颇J健泛汀吨毓埂返母拍?,熟悉基本的Java语法和XML语法,熟悉Eclipse和JUnit的使用,有相对较好的英语基础。

            学习过程:

            先按P40的学习顺序读完序号1~9。理解每个重构模式的动机,尝试读懂示例中的代码(实在不懂就放过,找机会上机也能明白)。

            在时间允许的条件下,可以重读和对书中代码重构,从而更加充分地理解重构与模式这两个重要的概念和实践方法。

            学习目的:使自己编写的代码更容易被人读懂。

            学习感悟:

            代码的重构应该是一步步完成的,每次重构的部分不要超过自己的理解能力的5%。虽然这样操作略显繁琐,但是可以减轻头脑重构过程中的记忆强度,减少代码出错的机会。

            代码的重构一定要配合JUnit(TDD,测试驱动开发)完成,再加上Git(版本管理)和Eclipse(IDE的重构工具)那就事半功倍了。

            快乐12开奖结果查询:一、构造函数

            (一)、构造函数的重构:

            6.1. (P43)用Creation Method替换构造函数:用明确的创建方法代替类中的多个构造函数,避免创建新类时客户无法正确理解和使用构造函数的意图。

            优点与缺点:

            + 相比构造函数能够更好地表达所创建的实例的意图;

            + 避免了构造函数的局限性,例如:两个构造函数不能通过相同的参数和不同的返回类型进行重载;

            + 更容易发现无用的创建代码;

            - 非标准化的创建方式,例如:不是统一用new的方式实例化。

            11.1.(P275) 构造函数链:把构造函数链接起来,减少代码的重复。特殊的构造函数调用更通用的构造函数,直到全包含构造函数(处理所有的构造函数调用)。

            当构造函数数量过多时,可以使用Creation Method(6.1节)重构。

            (二)、Factory的使用

            6.3. (P60)用Factory封装类:将同一个包里面、实现了相同接口的多个类的实例化交由工厂完成。

            优点与缺点:

            + 通过Creation Method简化不同种类实例的创建;

            + 通过隐藏类减少包结构的“概念重量”;

            + 通过共享公共接口实践“面向接口编程,而不要面向实现编程”的理念;

            - 增加新的种类的实例时,必须修改或者增加新的Creation Method;

            - 当客户只能获得Factory的二进制代码而不是源代码时,对Factory的制定将受到限制。

            6.4. (P67)用Factory Method引入多态创建:将同样的超类,不同的创建过程统一交由工厂完成。

            优点与缺点:

            + 减少因创建自定义对象而产生的重复代码;

            + 有效地表达了对象创建发生的位置,以及如何重写对象的创建;

            + 强制Factory Method使用的类必须实现统一的接口;

            - 可能会向Factory Method的一些实现者传递不必要的参数。

            算法重构

            Strategy

            7.2.(P102) 用Strategy替换条件逻辑

            优点与缺点:

            + 通过减少或者去除条件逻辑使算法变得清晰易懂;

            + 通过把算法的变体搬移到类层次中简化了类;

            + 允许在运行时用一种算法替换另一种算法;

            - 增加设计的复杂度;

            - 增加了算法获取数据的复杂度。

            Template Method

            8.1. (P166)形成Template Method:通过将不同方法中相同的顺序执行步骤提取出来从而实现泛化。

            优点与缺点:

            + 通过把不变行为搬移到超类,去除子类中的重复代码;

            + 简化并有效地表达了一个通用算法的步骤;

            + 允许子类很容易地定制一个算法;

            - 当为了生成算法导致子类必须实现很多方法的时候,就增加了设计的复杂度。

            Composite

             

            XMLBuilder的例子依据的有(7.5)TagNode,(6.5)TagBuilder,(10.1)TagNode,代码中存在一些小问题,可以试着自己修改。

            7.1. (P97)Composed Method(组合方法)模式:将代码组合成名字简单易懂的方法,再将这些方法组合成更大的方法,从而方便读懂代码。

            优点与缺点:

            + 清晰地描述了一个方法所实现的功能以及如何实现;

            + 把方法分解成命名良好的、处在细节的同一层面上的行为???,以此来简化方法;

            - 可能会产生过多的小方法;

            - 可能会使调试变得困难,因为程序的逻辑分散在许多小方法中。

            7.5. (P143)Composite模式:隐式地构造出树形结构。

            优点与缺点:

            + 封装重复的指令,如格式化、添加或删除结点;

            + 提供了处理相似逻辑增长的一般性方法;

            + 简化了客户代码的构造职责;

            - 构造的隐式的树结构越简单,设计的时候就越复杂。

            6.5. (P74)用Builder封装Composite:因为构造Composite有许多重复工作,并且工作流程是复杂而且容易出错的,通过Builder来处理构造细节来简化构造过程。

            优点与缺点:

            + 简化了构造Composite的客户代码;

            + 减少了创建Composite的重复和易出错的问题;

            + 在客户代码和Composite之间实现了松耦合;

            + 允许对已经封装的Composite或复杂的对象创建不同的表示;

            - 接口可能无法清楚地表达其本质意图。

            10.1. Collecing Parameter(聚集参数)模式:是把一个对象传入到不同的方法中,从而在这些方法中收集信息。

            经常与Composed Method模式一起使用。

            优点与缺点:

            + 可以把很大的方法转换成更上的、更简单的方法;

            - 少量地影响结果代码的运行速度。

            8.2.(P172) 提取Composite:将同一个超类下的多个子类实现的同样的Composite提取到超类中。

            优点与缺点:

            + 去除重复的类存储逻辑和类处理逻辑;

            + 能够有效地表达类处理逻辑的可继承性。

            8.3.(P180) 用Composite替换一个对象和多个对象的分别:用Composite实现了既可以处理一个对象,也可以处理多个对象的同样的函数接口。

            优点与缺点:

            + 去除了处理一个对象的方法和多个对象的方法中重复的代码;

            + 提供了处理一个对象和多个对象的统一函数方法;

            + 提供了处理多个对象的更丰富的方法(例如:OR表达式);

            - 可能会在Composite的构造过程中要求类型安全的运行时检查。

            Command

            7.6.(P155)用Command替换条件调度程序:为动作创建Command,将这些Command存储在集合中,再通过集合取出对应的Command执行。

            缺点与优点:

            + 提供了用统一方法执行不同行为的简单机制;

            + 允许在运行时改变所处理的请求,以及如何处理请求;

            + 仅仅需要很少的代码实现;

            - 会增加设计的复杂度,有时还不如条件调度程序简单。

            Adapter

            8.6.(P208)提取Adapter:一个类适配了多个版本的组件、类库、API或其他实体,可以通过提取Adapter来统一接口。

            缺点与优点:

            + 隔离不同版本的组件、类库或API之间的区别;

            + 使类只负责适配代码的一个版本;

            + 避免了频繁地修改代码;

            - 因为Adapter带来的限制,阻止了客户调用Adapter没有提供的功能。

            8.5.(P99)通过Adapter统一接口:使用客户代码与多个类交互时,遵循统一接口完成。

            优点与缺点:

            + 客户通过相同的接口与不同的类交互;

            + 客户通过公共的接口与多个对象交互;

            + 客户与不同的类交互方式相同;

            - 当类的接口可以改变的时候,增加了设计的复杂度。

            Adapter模式与Facade模式的区别

            Adapter用来适配对象;

            Facade用来适配整个子系统;通常用来与遗留系统进行交互。

            它们都可以使代码易于使用,但是它们的应用级别不同。

            State

            9.1.(P231)用类替换类型代码:?;ぷ侄未佣苊獠徽坊蛘卟话踩母持?。

            采用类替换类型代码而不用枚举,是因为类可以考虑未来行为的扩展,但是现在Java的枚举功能更加强大了,所以可以根据自己的习惯来选择。

            当重构过程中产生的类需要扩展包含更多行为时,就可以考虑(7.4.State替换状态改变条件语句)进行重构了。

            缺点与优点:

            + 避免非法赋值和比较;

            - 比使用不安全类型需要更多的代码。

            7.4. 用State替换状态改变条件语句:简化复杂的状态转换逻辑

            优点与缺点:

            + 减少复杂的状态转换条件逻辑;

            + 简化复杂的状态改变逻辑;

            + 提供观察状态改变逻辑的角度;

            - 增加设计的复杂度。

            9.3. 引入Null Object:替换掉null的判断逻辑,提供了对null的正确处理。

            优点与缺点:

            + 不需要重复的null逻辑就可以避免null错误;

            + 通过最小化null测试简化了代码;

            - 当系统不太需要null测试的时候,会增加设计的复杂度;

            - 如果程序员不知道Null Object的存在,就会产生多余的null测试;

            - 使维护变得复杂,拥有超类的Null Object必须重写所有新继承到的公共方法。

            Singleton

            6.6. 将Singleton内联化:把Singleton的功能搬移到需要它的类中,然后删除Singleton。

            优点与缺点:

            + 使对象的协作变得更加明显和明确;

            + ?;ち说ヒ坏氖道?,而且不需要特别的代码;

            - 当在许多层次间传递对象实例比较困难的时候,增加了设计的复杂度。

            9.2. 用Singleton限制实例化:减少内存的占用,提高运行的速度。

            优点与缺点:

            + 改进性能;

            - 变成全局访问;

            - 当对象含有不能共享的状态时,重构无法进行。

            Observer

            8.4.(P190)用Observer替换硬编码的通知:

            优点与缺点:

            + 使主题及其观察者访问松散耦合;

            + 支持一个或多个观察者;

            - 增加设计的复杂度;

            - 面对串联通知的时候,会进一步增加设计的复杂度;

            - 当观察者没有从主题中删除的时候,会造成内存泄漏。

            注:例子不熟悉,所以代码没太能领悟

            Decorator

            7.3. (P115)将装饰功能搬移到Decorator:

            优点与缺点:

            + 将装饰功能从类中搬移去除可以简化类;

            + 有效地将类的核心职责与装饰功能区分开;

            + 可以去除几个相关类中重复的装饰逻辑;

            - 改变了被装饰对象的对象类型;

            - 会使代码变得难以理解与调试;

            - 当Decorator组合产生负责影响的时候会增加设计的复杂度。

            注:这部分的代码也不完备,理解起来有困难,建议大致了解作者思路就好了,Decorator应该是个应用比较广泛的模式,以后自己可以通过实践探索。

            Decorator与Strategy的区别:

            + 都可以去除与特殊情况或选择性行为相关联的条件逻辑;

            + 都通过把条件逻辑搬移不对劲新的类中达到这一目的;

            - Decorator主要把自己包装在一个对象之外

            - Strategy则用在一个对象之中。

            11.2.(P278)统一接口:找出所有子类的公共方法,复制到超类中,在超类中执行空行为。

            11.3.(P280)提取参数:通过客户代码提供的参数对字段进行赋值。

            注:这两节都是辅助Decorator进行重构的。

            6.2.(P51)将创建知识搬移到Factory:避免创建代码到处蔓延。

            优点与缺点:

            + 合并创建逻辑和实例化配置选项;

            + 将客户代码与创建逻辑解耦;

            - 如果可以直接实例化,用Factory就会增加设计复杂度。

            注:可能作者认为采用的是HTMLParser的原因,所以给出的代码不全,不利于理解重构的含义。

            Visitor

            10.2.(P259)将聚集操作搬移到Visitor:适用于从多个对象中聚集信息,适用于从不同的对象中聚集信息。使用起来难度较大,首选应该是Collecting Parameter方法(P253,10.1.)。

            优点与缺点:

            + 调节多个算法,使其余适用于不同的对象结构;

            + 访问相同或不同继承结构中的类;

            + 调用不同类上的类型特定方法,无需类型转换;

            - 新的可访问类需要新的接收方法,每个Visitor中需要新的访问方法;

            - 可能会破坏被访问类的封装性;

            - 增加了代码的复杂度。

            注:

            1. 尽量使用通用接口把互不相同的类转变成相似的类,而少用Visitor模式。
            2. Ralph Johnson:大多时候并不需要Visitor,有些时候则是必须要用,别无选择。
            3. 可能作者认为采用的是HTMLParser的原因,所以给出的代码不全,不利于理解重构的含义。

            8.7.(P217)用Interpreter替换隐式语言

            优点与缺点:

            + 比隐式语言更好的支持语言元素的组合;

            + 不需要新的代码来支持语言元素的新组合;

            + 允许行为的运行时配置;

            - 会产生定义语法和修改客户代码的开销;

            - 如果语言很复杂,编程工作量较大;

            - 如果语言很简单,不需要考虑使用这个模式,否则会增加设计的复杂度。

            2019.1.2.晨,结束阅读。学习代码下载

            注:这种读书笔记对他人帮助不大,因为这个只是为了进一步理解作者的意图而进行的工作,通过输入关键点来确认自己是否真正领悟了作者想表达的意思。

            当输入这些优点与缺点时并没有领悟,然后再通过输入代码来理解作者如何从一段不好的代码重构成一段好代码的,重构的过程中得到了什么、失去了什么,自己未来编写程序中应该如何平衡。

            补充1:不要在意输入代码中出现的错误,那是因为代码不完备,许多类或者函数没有提供源代码。只要输入的代码可以排版和使用自动提示工具,就说明代码符合Java的语法规范,那么在阅读全书的过程中可以一点点把代码补全,或者根据自己的理解补全(这也是个理解重构和提高编码能力的机会)。

            补充2:如果需要下载HTMLParser,请注意是1.3的版本才是作者使用的。但是与HTMLParser相关的例子与原代码并不完全相同,而作者并没有在自己的例子中把逻辑构造完整,因此建议跟HTMLParser相关的例子还是跳过吧,否则为了调试通过消耗时间太多。(我现在开始渴望直接看《重构》那本书了。)

            代码目录:

            贷款风险估算程序(6.1,7.2,8.1,11.1)

            HTMLParser(6.2,7.3,8.2,10.1,10.2,11.2,11.3)

            对象-关系数据映射(6.3)

            XMLBuilder(6.4,6.5,7.5,8.5)

            二十一点游戏(6.6)

            集合类库(7.1)

            SystemPermission(7.4,9.1,9.2)

            Product(8.3,8.7)

            CatalogApp(7.6)

            JUnit(8.4)

            数据库查询(8.6)

            Applet(9.2)



            朱远翔 2019-01-02 11:25 发表评论
            ]]>
            全面盘点当前Android后台?;罘桨傅恼媸翟诵行Чń刂?019年前) - ★四川福利彩票快乐12★快乐12开奖直播★快乐12开奖★辽宁福彩快乐12★快乐彩12选5走势图//www.ot7t.com.cn/jb2011/archive/2018/12/27/433567.htmlJack JiangJack JiangThu, 27 Dec 2018 07:08:00 GMT//www.ot7t.com.cn/jb2011/archive/2018/12/27/433567.html//www.ot7t.com.cn/jb2011/comments/433567.html//www.ot7t.com.cn/jb2011/archive/2018/12/27/433567.html#Feedback0//www.ot7t.com.cn/jb2011/comments/commentRss/433567.html//www.ot7t.com.cn/jb2011/services/trackbacks/433567.html阅读全文

            Jack Jiang 2018-12-27 15:08 发表评论
            ]]>
            ValSprr - ★四川福利彩票快乐12★快乐12开奖直播★快乐12开奖★辽宁福彩快乐12★快乐彩12选5走势图//www.ot7t.com.cn/cyhboy/archive/2018/12/27/433564.html萍水相逢萍水相逢Thu, 27 Dec 2018 02:24:00 GMT//www.ot7t.com.cn/cyhboy/archive/2018/12/27/433564.html//www.ot7t.com.cn/cyhboy/comments/433564.html//www.ot7t.com.cn/cyhboy/archive/2018/12/27/433564.html#Feedback0//www.ot7t.com.cn/cyhboy/comments/commentRss/433564.html//www.ot7t.com.cn/cyhboy/services/trackbacks/433564.html str2 Then chkResult = chkResult And False 'MsgBox str1 'MsgBox str2 End If If Abs(mailDate - cmdDate) > 2 Then 'MsgBox "fail2" chkResult = chkResult And False End If 'Cells(currentRow, 21) = runAnyTime If chkResult Then MyMsgBox "Jira checking completed with positive result.", 3 Else MyMsgBox "Failed to Jira validation.", 3 End If End Sub

            萍水相逢 2018-12-27 10:24 发表评论
            ]]>
            Rdoc - ★四川福利彩票快乐12★快乐12开奖直播★快乐12开奖★辽宁福彩快乐12★快乐彩12选5走势图//www.ot7t.com.cn/cyhboy/archive/2018/12/27/433563.html萍水相逢萍水相逢Thu, 27 Dec 2018 02:21:00 GMT//www.ot7t.com.cn/cyhboy/archive/2018/12/27/433563.html//www.ot7t.com.cn/cyhboy/comments/433563.html//www.ot7t.com.cn/cyhboy/archive/2018/12/27/433563.html#Feedback0//www.ot7t.com.cn/cyhboy/comments/commentRss/433563.html//www.ot7t.com.cn/cyhboy/services/trackbacks/433563.html 0 Then filePath = Cells(currentRow, 9) & Cells(currentRow, 11) Set wa = CreateObject("Word.Application") wa.Visible = False Set wd = wa.Documents.Open(filePath) Cells(currentRow, 10) = wd.Content.text wd.Close savechanges:=False wa.Quit Set wa = Nothing Else Dim wildcard As String wildcard = "*.doc*" Dim fileText As String Dim fso As Object Dim objFolder As Object Dim myFolder As Object Dim myFile As Object Set fso = CreateObject("Scripting.FileSystemObject") If fso.FolderExists(localPath) Then Set objFolder = fso.getfolder(localPath) For Each myFolder In objFolder.SubFolders For Each myFile In myFolder.files If myFile.Name Like wildcard Or myFile.Name = wildcard Then 'MsgBox myFile.path Set wa = CreateObject("Word.Application") wa.Visible = False Set wd = wa.Documents.Open(myFile.path) fileText = fileText & wd.Content.text wd.Close savechanges:=False wa.Quit Set wa = Nothing End If Next Next Set objFolder = Nothing End If Set fso = Nothing Cells(currentRow, 10) = Cells(currentRow, 10) & fileText End If ErrorHandler: If Err.Number <> 0 Then MyMsgBox Err.Number & " " & Err.Description, 10 End If End Sub

            萍水相逢 2018-12-27 10:21 发表评论
            ]]>
            MySQL 小心使用 replace into【转】 - ★四川福利彩票快乐12★快乐12开奖直播★快乐12开奖★辽宁福彩快乐12★快乐彩12选5走势图//www.ot7t.com.cn/xiaomage234/archive/2018/12/25/433561.html小马歌小马歌Tue, 25 Dec 2018 11:19:00 GMT//www.ot7t.com.cn/xiaomage234/archive/2018/12/25/433561.html//www.ot7t.com.cn/xiaomage234/comments/433561.html//www.ot7t.com.cn/xiaomage234/archive/2018/12/25/433561.html#Feedback0//www.ot7t.com.cn/xiaomage234/comments/commentRss/433561.html//www.ot7t.com.cn/xiaomage234/services/trackbacks/433561.html摘要: MySQL replace into 错误案例 背景 * MySQL5.7 * ROW模式 * 表结构 CREATE TABLE `test` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `col_1` varc

            MySQL replace into 错误案例

            背景

            * MySQL5.7  * ROW模式   * 表结构 CREATE TABLE `test` (   `id` int(10) unsigned NOT NULL AUTO_INCREMENT,   `col_1` varchar(100) DEFAULT NULL,   `col_2` varchar(100) DEFAULT NULL,   `col_3` varchar(100) DEFAULT NULL,   PRIMARY KEY (`id`),   UNIQUE KEY `col_1` (`col_1`) ) ENGINE=InnoDB  DEFAULT CHARSET=utf8 

            错误场景一

            其他字段value莫名其妙的没了

            • step1 初始化记录
            mater:lc> REPLACE INTO test (col_1,col_2,col_3) values('a','a','a'); Query OK, 1 row affected (0.00 sec) --注意,这里是影响了1条记录  master:lc> REPLACE INTO test (col_1,col_2,col_3) values('b','b','b'); Query OK, 1 row affected (0.00 sec) --注意,这里是影响了1条记录  master:lc> REPLACE INTO test (col_1,col_2,col_3) values('c','c','c'); Query OK, 1 row affected (0.00 sec) --注意,这里是影响了1条记录   master > show create table test  | test  | CREATE TABLE `test` (   `id` int(10) unsigned NOT NULL AUTO_INCREMENT,   `col_1` varchar(100) DEFAULT NULL,   `col_2` varchar(100) DEFAULT NULL,   `col_3` varchar(100) DEFAULT NULL,   PRIMARY KEY (`id`),   UNIQUE KEY `col_1` (`col_1`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 |   mater > select * from test; +----+-------+-------+-------+ | id | col_1 | col_2 | col_3 | +----+-------+-------+-------+ |  1 | a     | a     | a     | |  2 | b     | b     | b     | |  3 | c     | c     | c     | +----+-------+-------+-------+ 3 rows in set (0.00 sec)  
            • step2 构造错误场景
            master:lc> replace into test(col_1,col_2) values('c','cc'); Query OK, 2 rows affected (0.00 sec)  dba:lc> select * from test; +----+-------+-------+-------+ | id | col_1 | col_2 | col_3 | +----+-------+-------+-------+ |  1 | a     | a     | a     | |  2 | b     | b     | b     | |  4 | c     | cc    | NULL  | +----+-------+-------+-------+ 3 rows in set (0.00 sec)  
            • 总结
            1. col_3 的值,从原来的c,变成了NULL,天呐,数据不见了。 id 也变了。
            2. 用户原本的需求,应该是如果col_1='c' 存在,那么就改变col_2='cc',其余的记录保持不变,结果id,col_3都变化了
            3. 解决方案就是:将replace into 改成 INSERT INTO … ON DUPLICATE KEY UPDATE

            但是你以为这样就完美的解决了吗? 马上就会带来另外一场灾难,请看下面的错误场景

            错误场景二

            ERROR 1062 (23000): Duplicate entry 'x' for key 'PRIMARY'

            • step1 初始化记录
             mater:lc> REPLACE INTO test (col_1,col_2) values('a','a'); Query OK, 1 row affected (0.00 sec) --注意,这里是影响了1条记录  master:lc> REPLACE INTO test (col_1,col_2) values('b','b'); Query OK, 1 row affected (0.00 sec) --注意,这里是影响了1条记录  master:lc> REPLACE INTO test (col_1,col_2) values('c','c'); Query OK, 1 row affected (0.00 sec) --注意,这里是影响了1条记录   master > show create table test  | test  | CREATE TABLE `test` (   `id` int(10) unsigned NOT NULL AUTO_INCREMENT,   `col_1` varchar(100) DEFAULT NULL,   `col_2` varchar(100) DEFAULT NULL,   PRIMARY KEY (`id`),   UNIQUE KEY `col_1` (`col_1`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 |   slave > show create table test  | test  | CREATE TABLE `test` (   `id` int(10) unsigned NOT NULL AUTO_INCREMENT,   `col_1` varchar(100) DEFAULT NULL,   `col_2` varchar(100) DEFAULT NULL,   PRIMARY KEY (`id`),   UNIQUE KEY `col_1` (`col_1`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 |
            • step2 构造错误场景
            * master  mater:lc> REPLACE INTO test (col_1,col_2) values('c','cc'); Query OK, 2 rows affected (0.00 sec)  --注意,这里是影响了两条记录  mater:lc> show create table test  | test  | CREATE TABLE `test` (   `id` int(10) unsigned NOT NULL AUTO_INCREMENT,   `col_1` varchar(100) DEFAULT NULL,   `col_2` varchar(100) DEFAULT NULL,   PRIMARY KEY (`id`),   UNIQUE KEY `col_1` (`col_1`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 |  master:lc> select * from test +----+-------+-------+ | id | col_1 | col_2 | +----+-------+-------+ |  1 | a     | a     | |  2 | b     | b     | |  4 | c     | cc    | +----+-------+-------+ 3 rows in set (0.00 sec)  * slave  slave:lc> show create table test  | test  | CREATE TABLE `test` (   `id` int(10) unsigned NOT NULL AUTO_INCREMENT,   `col_1` varchar(100) DEFAULT NULL,   `col_2` varchar(100) DEFAULT NULL,   PRIMARY KEY (`id`),   UNIQUE KEY `col_1` (`col_1`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 |  slave:lc> select * from test +----+-------+-------+ | id | col_1 | col_2 | +----+-------+-------+ |  1 | a     | a     | |  2 | b     | b     | |  4 | c     | cc    | +----+-------+-------+ 3 rows in set (0.00 sec) 
            • step3 错误案例产生
            * 假设有一天,master 挂了, 由slave 提升为 new mater  原slave:lc> show create table test  | test  | CREATE TABLE `test` (   `id` int(10) unsigned NOT NULL AUTO_INCREMENT,   `col_1` varchar(100) DEFAULT NULL,   `col_2` varchar(100) DEFAULT NULL,   PRIMARY KEY (`id`),   UNIQUE KEY `col_1` (`col_1`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 |  原slave:lc> select * from test +----+-------+-------+ | id | col_1 | col_2 | +----+-------+-------+ |  1 | a     | a     | |  2 | b     | b     | |  4 | c     | cc    | +----+-------+-------+ 3 rows in set (0.00 sec)   ===注意==  root:lc> REPLACE INTO test (col_1,col_2) values('d','d'); ERROR 1062 (23000): Duplicate entry '4' for key 'PRIMARY'  
            • 总结
            * Row 模式,主从情况下,replace into 和 INSERT INTO … ON DUPLICATE KEY UPDATE 都会导致以上问题的发生 * 解决方案: 最后可以通过alter table auto_increment值解决,但是这样已经造成mater的表很长时间没有写入了。。。

            最后总结

            • replace with unique key
            1. 禁止 replace into (错误一,错误二 都会发生) 2. 禁止 INSERT INTOON DUPLICATE KEY UPDATE (错误二 会发生)
            • replace with primary key
            1. 禁止 replace into (会发生错误场景一的案例,丢失部分字段数据) 2. 可以使用INSERT INTOON DUPLICATE KEY UPDATE 代替 replace into


            四川福利彩票快乐12 2018-12-25 19:19 发表评论
            ]]>