auto commit
This commit is contained in:
parent
dc31eaa0d7
commit
a22f778f32
57
notes/重构.md
57
notes/重构.md
|
@ -109,37 +109,68 @@
|
||||||
|
|
||||||
在重构前,需要先构建好可靠的测试环境,确保安全地重构。
|
在重构前,需要先构建好可靠的测试环境,确保安全地重构。
|
||||||
|
|
||||||
重构是以微小的步伐修改程序,如果犯下错误,很容易便可以发现它。
|
重构需要以微小的步伐修改程序,如果重构过程发生错误,很容易就能发现错误。
|
||||||
|
|
||||||
**案例分析**
|
**案例分析**
|
||||||
|
|
||||||
影片出租店应用程序,包括三个类:Movie、Rental 和 Customer,Rental 包含租赁的 Movie 以及天数。
|
影片出租店应用程序,需要计算每位顾客的消费金额。
|
||||||
|
|
||||||
<br><div align="center"> <img src="https://github.com/CyC2018/InterviewNotes/blob/master/pics//a758c8b2-0ac7-438f-90c2-3923ffad6328.png"/> </div><br>
|
包括三个类:Movie、Rental 和 Customer,Rental 包含租赁的 Movie 以及天数。
|
||||||
|
|
||||||
最开始的实现是把所有的计费代码都放在 Customer 类中,在变化发生时,需要对这部分代码进行更改。本案例中可能发生的变化有:一种类别的计费方式发生改变;添加新的电影类别。考虑到计费代码可能存在于多处,一旦发生改变时,就需要对所有计费代码进行修改。
|
<br><div align="center"> <img src="https://github.com/CyC2018/InterviewNotes/blob/master/pics//25d6d3d4-4726-47b1-a9cb-3316d1ff5dd5.png"/> </div><br>
|
||||||
|
|
||||||
<br><div align="center"> <img src="https://github.com/CyC2018/InterviewNotes/blob/master/pics//9e5e3cc6-3107-4051-b584-8ff077638fe6.png"/> </div><br>
|
最开始的实现是把所有的计费代码都放在 Customer 类中。
|
||||||
|
|
||||||
以下是继承 Movie 的多态方案。但是由于一部 Movie 的类别会动态改变,因此这种方案不可行。
|
可以发现,该代码没有使用 Customer 类中的任何信息,更多的是使用 Rental 类的信息,因此第一个可以重构的点就是把具体计费的代码移到 Rental 类中,然后 Customer 类的 getTotalCharge() 方法只需要调用 Rental 类中的计费方法即可。
|
||||||
|
|
||||||
<br><div align="center"> <img src="https://github.com/CyC2018/InterviewNotes/blob/master/pics//2a502516-5d34-4eef-8f39-916298a60035.png"/> </div><br>
|
|
||||||
|
|
||||||
引入 Price 来反应类别信息,通过组合的方式在 Movie 中加入 Price 对象,这样每种类别的计费方式都封装在不同的 Price 子类中,并且 Movie 对象也可以动态改变类别。这种方式可以很好地适应上述提到的变化。
|
```java
|
||||||
|
class Customer...
|
||||||
|
double getTotalCharge() {
|
||||||
|
while (rentals.hasMoreElements()) {
|
||||||
|
double thisAmount = 0;
|
||||||
|
Rental each = (Rental) rentals.nextElement();
|
||||||
|
switch (each.getMovie().getPriceCode()) {
|
||||||
|
case Movie.REGULAR:
|
||||||
|
thisAmount += 2;
|
||||||
|
if (each.getDaysRented() > 2)
|
||||||
|
thisAmount += (each.getDaysRented() - 2) * 1.5;
|
||||||
|
break;
|
||||||
|
case Movie.NEW_RELEASE:
|
||||||
|
thisAmount += each.getDaysRented() * 3;
|
||||||
|
break;
|
||||||
|
case Movie.CHILDRENS:
|
||||||
|
thisAmount += 1.5;
|
||||||
|
if (each.getDaysRented() > 3)
|
||||||
|
thisAmount += (each.getDaysRented() - 3) * 1.5;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
<br><div align="center"> <img src="https://github.com/CyC2018/InterviewNotes/blob/master/pics//c02a83b8-a6b9-4d00-a509-6f0516beaf5e.png"/> </div><br>
|
使用 switch 的准则是:只能在对象自己的数据上使用,而不能在另一个对象的数据基础上使用。解释如下:switch 使用的数据通常是一组相关的数据,例如上面的代码使用了 Movie 的多种类别数据。当这组类别的数据发生改变时,例如上面的代码中增加 Movie 的类别或者修改一种 Movie 类别的计费方法,就需要修改 switch 代码。如果允许违反了准则,就会有多个地方的 switch 使用了这部分的数据,那么需要打开所有的 switch 代码进行修改。
|
||||||
|
|
||||||
重构后的时序图和类图:
|
以下是继承 Movie 的多态解决方案,这种方案可以解决上述的 switch 问题,因为每种电影类别的计费方式都被放到了对应 Movie 子类中,当变化发生时,只需要去修改对应子类中的代码即可。
|
||||||
|
|
||||||
<br><div align="center"> <img src="https://github.com/CyC2018/InterviewNotes/blob/master/pics//95f4559c-3d2a-4176-b365-4fbc46c76cf1.png"/> </div><br>
|
<br><div align="center"> <img src="https://github.com/CyC2018/InterviewNotes/blob/master/pics//76b48b4c-8999-4967-893b-832602e73285.png"/> </div><br>
|
||||||
|
|
||||||
<br><div align="center"> <img src="https://github.com/CyC2018/InterviewNotes/blob/master/pics//293b9326-02fc-4ad8-8c79-b4a7b5ba60d3.png"/> </div><br>
|
但是由于 Movie 可以在其生命周期内修改自己的类别,一个对象却不能在生命周期内修改自己所属的内,因此这种方案不可行。可以通过使用策略模式来解决这种问题(原书写的是使用状态模式,但是这里应该为策略模式,具体可以参考设计模式内容)。
|
||||||
|
|
||||||
|
下图中,Price 有多种实现,Movie 组合了一个 Price 对象,并且在运行时可以改变组合的 Price 对象,从而使得它的计费方式发生改变。
|
||||||
|
|
||||||
|
<br><div align="center"> <img src="https://github.com/CyC2018/InterviewNotes/blob/master/pics//2a842a14-e4ab-4f37-83fa-f82c206fe426.png"/> </div><br>
|
||||||
|
|
||||||
|
重构后整体的类图和时序图如下:
|
||||||
|
|
||||||
|
<br><div align="center"> <img src="https://github.com/CyC2018/InterviewNotes/blob/master/pics//4440ad24-625b-489a-96c1-e5ab1b06a30f.png"/> </div><br>
|
||||||
|
|
||||||
|
<br><div align="center"> <img src="https://github.com/CyC2018/InterviewNotes/blob/master/pics//8c139711-3500-4f71-8456-c1adaf429ad0.png"/> </div><br>
|
||||||
|
|
||||||
# 第二章 重构原则
|
# 第二章 重构原则
|
||||||
|
|
||||||
重构是对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。
|
重构是对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。
|
||||||
|
|
||||||
重构的好处:改进软件设计;使软件更容易理解;帮助找到 bug;提高编程速度。
|
重构的好处:改进软件设计;使软件更容易理解;帮助找到 Bug;提高编程速度。
|
||||||
|
|
||||||
三次法则:第一次做某件事时只管去做;第二次做类似事情时可以去做;第三次再做类似的事,就应该重构。
|
三次法则:第一次做某件事时只管去做;第二次做类似事情时可以去做;第三次再做类似的事,就应该重构。
|
||||||
|
|
||||||
|
|
BIN
pics/25d6d3d4-4726-47b1-a9cb-3316d1ff5dd5.png
Normal file
BIN
pics/25d6d3d4-4726-47b1-a9cb-3316d1ff5dd5.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.0 KiB |
BIN
pics/2a842a14-e4ab-4f37-83fa-f82c206fe426.png
Normal file
BIN
pics/2a842a14-e4ab-4f37-83fa-f82c206fe426.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
BIN
pics/4440ad24-625b-489a-96c1-e5ab1b06a30f.png
Normal file
BIN
pics/4440ad24-625b-489a-96c1-e5ab1b06a30f.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
BIN
pics/76b48b4c-8999-4967-893b-832602e73285.png
Normal file
BIN
pics/76b48b4c-8999-4967-893b-832602e73285.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.0 KiB |
BIN
pics/8c139711-3500-4f71-8456-c1adaf429ad0.png
Normal file
BIN
pics/8c139711-3500-4f71-8456-c1adaf429ad0.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
Loading…
Reference in New Issue
Block a user