名词:类型码 类型码的上层建筑 重构方法 1.使用子类代替类型码 2.使用状态/策略模式代替类型码
类中存在方法把某个字段当作条件,根据字段值的不同,进行不同的处理。(自定义概念)
则这个字段叫做:类型码。(重构-改善既有代码的设计中的概念)
这个方法叫做:类型码的上层建筑。(自定义概念)
(这种判断方法不是绝对正确,但是一般可行,类型码是重构-改善既有代码的设计的概念,但是类型码的上层建筑是自定义概念)
例子1:
Movie对象中存在影片类型字段,根据影片类型和租期计算金额的getCharge()方法和计算用户积分的getRentPoints()
getCharge()方法和getRentPoints()都以影片类型字段为条件,进行不同的处理。
所以根据上面的解释:
类型码=影片类型
类型码(影片类型)的上层建筑2个:getCharge()方法和getRentPoints()方法
1 package refactor;
2
3 public class Movie {
4 public static final int NEW_RELEASE = 0;
5 public static final int REGULAR = 1;
6 public static final int CHILDREN = 2;
7
8 private int priceCode;
9 private String title;
10
11 public Movie() {
12 }
13
14 public Movie(String title, int priceCode) {
15 this.priceCode = priceCode;
16 this.title = title;
17 }
18
19 public double getCharge(int daysRent){
20 double result = 0;
21 switch(priceCode){
22 case Movie.REGULAR:
23 result += 2;
24 if(daysRent > 2){
25 result += (daysRent - 2) * 1.5;
26 }
27 break;
28 case Movie.NEW_RELEASE:
29 result += daysRent * 3;
30 break;
31 case Movie.CHILDREN:
32 result += 1.5;
33 if(daysRent > 3){
34 result += (daysRent - 3) * 1.5;
35 }
36 break;
37 }
38 return result;
39 }
40
41 public int getRentPoints(int daysRent){
42 int rentPoints = 1;
43 if(priceCode == Movie.NEW_RELEASE
44 && daysRent > 1){
45 rentPoints++;
46 }
47 return rentPoints;
48 }
49 }
Movie直接使用类型码
当影片类型不多,分支逻辑不复杂,上层建筑不多,并且以后变化也不大时,直接使用类型码和使用多态重构区别不太大。
直接使用类型码的问题:
可是随着影片类型的迅速扩张,分支逻辑的复杂性升高,上层建筑的增多,直接使用类型码将导致
1.如果影片类型增加的非常多时,则条件语句将会非常长,并且每个上层建筑都会对应一个这样的条件语句。
2.如果影片类型增加,上层建筑增多,而此时分支逻辑复杂度也很高,则需要为了防止上层建筑过长,需要抽取每个分支逻辑为方法,将可能导致本类方法迅速膨胀。
所以:当影片类型,分支逻辑,上层建筑发生的变化较大时,则直接使用类型码会导致类过大,上层建筑过长或者产生过多的方法,条件语句随着上层建筑重复等问题,
总之就是导致代码难逻辑和结构的越来越不清晰,越来越难理解,越来越难修改,越来越容易出错。
使用重构则是较好的解决方案:
1.replace type code with subclass 对象类型码的值在生命期间不会发生变化
2.replace type code with state/strategy 对象类型码的值可能在生命期间发生变化。
好处:
1.不管原来的上层建筑有多少,重构后,只需要维护一个产生子类或者状态/策略的条件语句。
2.与类型码相关的处理都被放到相应的类中。代码结构和逻辑更清晰。
每个子类(状态/策略)与类型码的值一一对应,每个与类型码值对应的处理逻辑都被放在与该类型码值相对应的子类(状态/策略)中,代码的结构和逻辑非常清晰。
每个子类(状态/策略)与类型码的值一一对应,每个与类型码值对应的处理逻辑都被放在与该类型码值相对应的子类(状态/策略)中,代码的结构和逻辑非常清晰。
每个子类(状态/策略)与类型码的值一一对应,每个与类型码值对应的处理逻辑都被放在与该类型码值相对应的子类(状态/策略)中,代码的结构和逻辑非常清晰。
1 package shop;
2
3 public class Movie {
4 public static final int NEW_RELEASE = 0;
5 public static final int REGULAR = 1;
6 public static final int CHILDREN = 2;
7
8 private String title;
9 private Price price;
10
11 public Movie() {
12 }
13
14 public Movie(String title, int priceCode) {
15 setPriceCode(priceCode);
16 this.title = title;
17 }
18
19 public int getPriceCode() {
20 return price.getPriceCode();
21 }
22
23 //有2个上层建筑,但是程序只需要维护一个根据类型码产生state对象的条件语句
24 public void setPriceCode(int priceCode) {
25 switch (priceCode){
26 case Movie.NEW_RELEASE:
27 price = new NewPrice();
28 break;
29 case Movie.REGULAR:
30 price = new RegularPrice();
31 break;
32 case Movie.CHILDREN:
33 price = new ChildrenPrice();
34 break;
35 default:
36 throw new IllegalArgumentException("非法的影片类型:" + priceCode);
37 }
38 }
39
40 public double getCharge(int daysRent){
41 return price.getCharge(daysRent);
42 }
43
44 public int getRentPoints(int daysRent){
45 return price.getRentPoints(daysRent);
46 }
47 }
Movie使用状态模式重构类型码
1 package shop;
2 //子类或状态与类型码一一对应,相应的逻辑也全部集中在这个类中。代码的结构和逻辑更清晰
3 public class ChildrenPrice extends Price {
4 @Override
5 public int getPriceCode(){
6 return Movie.CHILDREN;
7 }
8
9 @Override
10 public double getCharge(int daysRent) {
11 double result = 1.5;
12 if(daysRent > 3){
13 result += (daysRent - 3) * 1.5;
14 }
15 return result;
16 }
17 }
ChildrenPrice子类或状态与类型码一一对应
1 package shop;
2 //子类或状态与类型码一一对应,相应的逻辑也全部集中在这个类中。代码的结构和逻辑更清晰
3 public class NewPrice extends Price{
4 @Override
5 public int getPriceCode() {
6 return Movie.NEW_RELEASE;
7 }
8
9 @Override
10 public double getCharge(int daysRent) {
11 return daysRent * 3;
12 }
13
14 @Override
15 public int getRentPoints(int daysRent) {
16 return daysRent > 1 ? 2 : 1;
17 }
18 }
NewPrice 子类或状态与类型码一一对应
1 package shop;
2 //子类或状态与类型码一一对应,相应的逻辑也全部集中在这个类中。代码的结构和逻辑更清晰
3 public class RegularPrice extends Price {
4 @Override
5 public int getPriceCode() {
6 return Movie.REGULAR;
7 }
8
9 @Override
10 public double getCharge(int daysRent) {
11 double result = 2;
12 if(daysRent > 2){
13 result += (daysRent - 2) * 1.5;
14 }
15 return result;
16 }
17 }
RegularPrice 子类或状态与类型码一一对应
类型码:类的一个字段,该类中存在方法把这个字段当作条件,根据字段值的不同,进行不同的处理。
类型码的上层建筑:某个方法,根据类型码不同的值进行不同处理。
当类型码的上层建筑有多个,则每个上层建筑都需要维护着条件判断。导致总体上基于类型码的条件判断有多个。
重构方法:
1.replace type code with subclass
2.replace type code with state/strategy
和直接使用类型码的区别:
2个重构手法避免了多个基于类型码的条件判断的产生,使基于类型码的条件判断始终只有一个。
上层建筑通过多态