auto commit
This commit is contained in:
parent
4e0646dad6
commit
9164742ff0
@ -3,7 +3,7 @@
|
|||||||
* [二、用名字表达代码含义](#二用名字表达代码含义)
|
* [二、用名字表达代码含义](#二用名字表达代码含义)
|
||||||
* [三、名字不能带来歧义](#三名字不能带来歧义)
|
* [三、名字不能带来歧义](#三名字不能带来歧义)
|
||||||
* [四、良好的代码风格](#四良好的代码风格)
|
* [四、良好的代码风格](#四良好的代码风格)
|
||||||
* [五、编写注释](#五编写注释)
|
* [五、为何编写注释](#五为何编写注释)
|
||||||
* [六、如何编写注释](#六如何编写注释)
|
* [六、如何编写注释](#六如何编写注释)
|
||||||
* [七、提高控制流的可读性](#七提高控制流的可读性)
|
* [七、提高控制流的可读性](#七提高控制流的可读性)
|
||||||
* [八、拆分长表达式](#八拆分长表达式)
|
* [八、拆分长表达式](#八拆分长表达式)
|
||||||
@ -43,12 +43,14 @@
|
|||||||
|
|
||||||
起完名字要思考一下别人会对这个名字有何解读,会不会误解了原本想表达的含义。
|
起完名字要思考一下别人会对这个名字有何解读,会不会误解了原本想表达的含义。
|
||||||
|
|
||||||
用 min、max 表示数量范围;用 first、last 表示访问空间的包含范围,begin、end 表示访问空间的排除范围,即 end 不包含尾部。
|
布尔相关的命名加上 is、can、should、has 等前缀。
|
||||||
|
|
||||||
|
- 用 min、max 表示数量范围;
|
||||||
|
- 用 first、last 表示访问空间的包含范围;
|
||||||
|
- begin、end 表示访问空间的排除范围,即 end 不包含尾部。
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//05907ab4-42c5-4b5e-9388-6617f6c97bea.jpg"/> </div><br>
|
<div align="center"> <img src="../pics//05907ab4-42c5-4b5e-9388-6617f6c97bea.jpg"/> </div><br>
|
||||||
|
|
||||||
布尔相关的命名加上 is、can、should、has 等前缀。
|
|
||||||
|
|
||||||
# 四、良好的代码风格
|
# 四、良好的代码风格
|
||||||
|
|
||||||
适当的空行和缩进。
|
适当的空行和缩进。
|
||||||
@ -61,11 +63,9 @@ int b = 11; // 注释
|
|||||||
int c = 111; // 注释
|
int c = 111; // 注释
|
||||||
```
|
```
|
||||||
|
|
||||||
语句顺序不能随意,比如与 html 表单相关联的变量的赋值应该和表单在 html 中的顺序一致;
|
语句顺序不能随意,比如与 html 表单相关联的变量的赋值应该和表单在 html 中的顺序一致。
|
||||||
|
|
||||||
把相关的代码按块组织起来放在一起。
|
# 五、为何编写注释
|
||||||
|
|
||||||
# 五、编写注释
|
|
||||||
|
|
||||||
阅读代码首先会注意到注释,如果注释没太大作用,那么就会浪费代码阅读的时间。那些能直接看出含义的代码不需要写注释,特别是并不需要为每个方法都加上注释,比如那些简单的 getter 和 setter 方法,为这些方法写注释反而让代码可读性更差。
|
阅读代码首先会注意到注释,如果注释没太大作用,那么就会浪费代码阅读的时间。那些能直接看出含义的代码不需要写注释,特别是并不需要为每个方法都加上注释,比如那些简单的 getter 和 setter 方法,为这些方法写注释反而让代码可读性更差。
|
||||||
|
|
||||||
@ -109,14 +109,6 @@ int add(int x, int y) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
在很复杂的函数调用中对每个参数标上名字:
|
|
||||||
|
|
||||||
```java
|
|
||||||
int a = 1;
|
|
||||||
int b = 2;
|
|
||||||
int num = add(\* x = *\ a, \* y = *\ b);
|
|
||||||
```
|
|
||||||
|
|
||||||
使用专业名词来缩短概念上的解释,比如用设计模式名来说明代码。
|
使用专业名词来缩短概念上的解释,比如用设计模式名来说明代码。
|
||||||
|
|
||||||
# 七、提高控制流的可读性
|
# 七、提高控制流的可读性
|
||||||
@ -128,16 +120,6 @@ if (len < 10)
|
|||||||
if (10 > len)
|
if (10 > len)
|
||||||
```
|
```
|
||||||
|
|
||||||
if / else 条件语句,逻辑的处理顺序为:① 正逻辑;② 关键逻辑;③ 简单逻辑。
|
|
||||||
|
|
||||||
```java
|
|
||||||
if (a == b) {
|
|
||||||
// 正逻辑
|
|
||||||
} else{
|
|
||||||
// 反逻辑
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
只有在逻辑简单的情况下使用 ? : 三目运算符来使代码更紧凑,否则应该拆分成 if / else;
|
只有在逻辑简单的情况下使用 ? : 三目运算符来使代码更紧凑,否则应该拆分成 if / else;
|
||||||
|
|
||||||
do / while 的条件放在后面,不够简单明了,并且会有一些迷惑的地方,最好使用 while 来代替。
|
do / while 的条件放在后面,不够简单明了,并且会有一些迷惑的地方,最好使用 while 来代替。
|
||||||
|
141
notes/重构.md
141
notes/重构.md
@ -123,24 +123,22 @@
|
|||||||
|
|
||||||
影片出租店应用程序,需要计算每位顾客的消费金额。
|
影片出租店应用程序,需要计算每位顾客的消费金额。
|
||||||
|
|
||||||
包括三个类:Movie、Rental 和 Customer,Rental 包含租赁的 Movie 以及天数。
|
包括三个类:Movie、Rental 和 Customer。
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//c2f0c8e2-da66-498c-a38f-e1176abee29e.png"/> </div><br>
|
<div align="center"> <img src="../pics//c2f0c8e2-da66-498c-a38f-e1176abee29e.png"/> </div><br>
|
||||||
|
|
||||||
最开始的实现是把所有的计费代码都放在 Customer 类中。
|
最开始的实现是把所有的计费代码都放在 Customer 类中。可以发现,该代码没有使用 Customer 类中的任何信息,更多的是使用 Rental 类的信息,因此第一个可以重构的点就是把具体计费的代码移到 Rental 类中,然后 Customer 类的 getTotalCharge() 方法只需要调用 Rental 类中的计费方法即可。
|
||||||
|
|
||||||
可以发现,该代码没有使用 Customer 类中的任何信息,更多的是使用 Rental 类的信息,因此第一个可以重构的点就是把具体计费的代码移到 Rental 类中,然后 Customer 类的 getTotalCharge() 方法只需要调用 Rental 类中的计费方法即可。
|
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class Customer {
|
class Customer {
|
||||||
|
|
||||||
private List<Rental> rentals = new ArrayList<>();
|
private List<Rental> rentals = new ArrayList<>();
|
||||||
|
|
||||||
public void addRental(Rental rental) {
|
void addRental(Rental rental) {
|
||||||
rentals.add(rental);
|
rentals.add(rental);
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getTotalCharge() {
|
double getTotalCharge() {
|
||||||
double totalCharge = 0.0;
|
double totalCharge = 0.0;
|
||||||
for (Rental rental : rentals) {
|
for (Rental rental : rentals) {
|
||||||
switch (rental.getMovie().getMovieType()) {
|
switch (rental.getMovie().getMovieType()) {
|
||||||
@ -151,7 +149,6 @@ public class Customer {
|
|||||||
totalCharge += rental.getDaysRented() * 2;
|
totalCharge += rental.getDaysRented() * 2;
|
||||||
break;
|
break;
|
||||||
case Movie.Type3:
|
case Movie.Type3:
|
||||||
totalCharge += 1.5;
|
|
||||||
totalCharge += rental.getDaysRented() * 3;
|
totalCharge += rental.getDaysRented() * 3;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -159,42 +156,41 @@ public class Customer {
|
|||||||
return totalCharge;
|
return totalCharge;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class Rental {
|
class Rental {
|
||||||
private int daysRented;
|
private int daysRented;
|
||||||
|
|
||||||
private Movie movie;
|
private Movie movie;
|
||||||
|
|
||||||
public Rental(int daysRented, Movie movie) {
|
Rental(int daysRented, Movie movie) {
|
||||||
this.daysRented = daysRented;
|
this.daysRented = daysRented;
|
||||||
this.movie = movie;
|
this.movie = movie;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Movie getMovie() {
|
Movie getMovie() {
|
||||||
return movie;
|
return movie;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getDaysRented() {
|
int getDaysRented() {
|
||||||
return daysRented;
|
return daysRented;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class Movie {
|
class Movie {
|
||||||
|
|
||||||
public static final int Type1 = 0, Type2 = 1, Type3 = 2;
|
static final int Type1 = 0, Type2 = 1, Type3 = 2;
|
||||||
|
|
||||||
private int type;
|
private int type;
|
||||||
|
|
||||||
public Movie(int type) {
|
Movie(int type) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMovieType() {
|
int getMovieType() {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -223,9 +219,9 @@ public class App {
|
|||||||
|
|
||||||
<div align="center"> <img src="../pics//41026c79-dfc1-40f7-85ae-062910fd272b.png"/> </div><br>
|
<div align="center"> <img src="../pics//41026c79-dfc1-40f7-85ae-062910fd272b.png"/> </div><br>
|
||||||
|
|
||||||
但是我们需要允许一部影片可以在运行过程中改变其所属的分类,但是上述的继承方案却不可行,因为一个对象所属的类在编译过程就确定了。
|
有一条设计原则指示应该多用组合少用继承,这是因为组合比继承具有更高的灵活性。例如上面的继承方案,一部电影要改变它的计费方式,就要改变它所属的类,但是对象所属的类在编译时期就确定了,无法在运行过程中改变。(运行时多态可以在运行过程中改变一个父类引用指向的子类对象,但是无法改变一个对象所属的类。)
|
||||||
|
|
||||||
为了解决上述的问题,需要使用策略模式。引入 Price 类,它有多种实现。Movie 组合了一个 Price 对象,并且在运行时可以改变组合的 Price 对象,从而使得它的计费方式发生改变。
|
策略模式就是使用组合替代继承的一种解决方案。引入 Price 类,它有多种实现。Movie 组合了一个 Price 对象,并且在运行时可以改变组合的 Price 对象,从而使得它的计费方式发生改变。
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//8c0b3ae1-1087-46f4-8637-8d46b4ae659c.png"/> </div><br>
|
<div align="center"> <img src="../pics//8c0b3ae1-1087-46f4-8637-8d46b4ae659c.png"/> </div><br>
|
||||||
|
|
||||||
@ -235,6 +231,107 @@ public class App {
|
|||||||
|
|
||||||
<div align="center"> <img src="../pics//3ca58a41-8794-49c1-992e-de5d579a50d1.png"/> </div><br>
|
<div align="center"> <img src="../pics//3ca58a41-8794-49c1-992e-de5d579a50d1.png"/> </div><br>
|
||||||
|
|
||||||
|
重构后的代码:
|
||||||
|
|
||||||
|
```java
|
||||||
|
class Customer {
|
||||||
|
private List<Rental> rentals = new ArrayList<>();
|
||||||
|
|
||||||
|
void addRental(Rental rental) {
|
||||||
|
rentals.add(rental);
|
||||||
|
}
|
||||||
|
|
||||||
|
double getTotalCharge() {
|
||||||
|
double totalCharge = 0.0;
|
||||||
|
for (Rental rental : rentals) {
|
||||||
|
totalCharge += rental.getCharge();
|
||||||
|
}
|
||||||
|
return totalCharge;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
class Rental {
|
||||||
|
private int daysRented;
|
||||||
|
|
||||||
|
private Movie movie;
|
||||||
|
|
||||||
|
Rental(int daysRented, Movie movie) {
|
||||||
|
this.daysRented = daysRented;
|
||||||
|
this.movie = movie;
|
||||||
|
}
|
||||||
|
|
||||||
|
double getCharge() {
|
||||||
|
return daysRented * movie.getCharge();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
interface Price {
|
||||||
|
double getCharge();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
class Price1 implements Price {
|
||||||
|
@Override
|
||||||
|
public double getCharge() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
class Price2 implements Price {
|
||||||
|
@Override
|
||||||
|
public double getCharge() {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
package imp2;
|
||||||
|
|
||||||
|
class Price3 implements Price {
|
||||||
|
@Override
|
||||||
|
public double getCharge() {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
class Movie {
|
||||||
|
|
||||||
|
private Price price;
|
||||||
|
|
||||||
|
Movie(Price price) {
|
||||||
|
this.price = price;
|
||||||
|
}
|
||||||
|
|
||||||
|
double getCharge() {
|
||||||
|
return price.getCharge();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
class App {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Customer customer = new Customer();
|
||||||
|
Rental rental1 = new Rental(1, new Movie(new Price1()));
|
||||||
|
Rental rental2 = new Rental(2, new Movie(new Price2()));
|
||||||
|
customer.addRental(rental1);
|
||||||
|
customer.addRental(rental2);
|
||||||
|
System.out.println(customer.getTotalCharge());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
# 二、重构原则
|
# 二、重构原则
|
||||||
|
|
||||||
## 定义
|
## 定义
|
||||||
@ -265,9 +362,7 @@ public class App {
|
|||||||
|
|
||||||
## 修改接口
|
## 修改接口
|
||||||
|
|
||||||
如果重构手法改变了已发布的接口,就必须维护新旧两个接口。
|
如果重构手法改变了已发布的接口,就必须维护新旧两个接口。可以保留旧接口,让旧接口去调用新接口,并且使用 Java 提供的 @deprecation 将旧接口标记为弃用。
|
||||||
|
|
||||||
可以保留旧接口,让旧接口去调用新接口,并且使用 Java 提供的 @deprecation 将旧接口标记为弃用。
|
|
||||||
|
|
||||||
可见修改接口特别麻烦,因此除非真有必要,否则不要发布接口,并且不要过早发布接口。
|
可见修改接口特别麻烦,因此除非真有必要,否则不要发布接口,并且不要过早发布接口。
|
||||||
|
|
||||||
@ -467,7 +562,7 @@ Extract Method 会把很多参数和临时变量都当做参数,可以用 Repl
|
|||||||
|
|
||||||
Java 可以使用 Junit 进行单元测试。
|
Java 可以使用 Junit 进行单元测试。
|
||||||
|
|
||||||
测试应该能够完全自动化,并能检查测试的结果。Junit 可以做到。
|
测试应该能够完全自动化,并能检查测试的结果。
|
||||||
|
|
||||||
小步修改,频繁测试。
|
小步修改,频繁测试。
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user