周末闲来写写看书总结,今天写《重构》中的3个重要手法,分别是Replace Type Code With Class、Replace Type Code With Subclass和Replace Type Code With State/Strategy。

  1、Replace Type Code With Class

  重构前的主体代码如下:

 package nelson;

 public class Person {

     public static final int O = 0;
public static final int A = 1;
public static final int B = 2;
public static final int AB = 3; private int _bloodGroup; public Person(int bloodGroup){
_bloodGroup = bloodGroup;
} public void setBloodGroup(int arg){
_bloodGroup = arg;
} public int getBloodGroup(){
return _bloodGroup;
}
}

  重构前的应用代码如下:

 package nelson;

 public class HelloJava {

     public static void main(String[] args) {

         System.out.println("Hello,Java");

         Person xiaoming = new Person(Person.A);  //小明A型血

         System.out.println("小明的血型是:"+xiaoming.getBloodGroup());
} }

  重构前的代码有什么问题呢,在Person中使用public static final修饰了几个变量,这几个变量是血型的类型值。在new对象或者setBloodGroup时可以将此作为参数传入。问题有如下:在New对象或者setBloodGroup时,可以传入其他参数,另外Person.A这样的形式不能直观反映A的含义,如果取为BloodGroup.A--血型,血型中的A型血,是不是更直接明了。

  重构后的主体代码:

 package nelson;

 public class Person {

     private BloodGroup _bloodGroup;

     public Person(BloodGroup bloodGroup){
_bloodGroup = bloodGroup;
} public void setBloodGroup(BloodGroup arg){
_bloodGroup = arg;
} public BloodGroup getBloodGroup(){
return _bloodGroup;
}
} class BloodGroup
{
public static final BloodGroup O = new BloodGroup(0);
public static final BloodGroup A = new BloodGroup(1);
public static final BloodGroup B = new BloodGroup(2);
public static final BloodGroup AB = new BloodGroup(3);
public static final BloodGroup[] _values = {O,A,B,AB}; private final int _code; private BloodGroup(int code)
{
_code = code;
} public int getCode()
{
return _code;
} public BloodGroup getBloodGroup(int arg)
{
return _values[arg];
}
}

  重构后的应用代码:

 package nelson;

 public class HelloJava {

     public static void main(String[] args) {

         System.out.println("Hello,Java");

         Person xiaoming = new Person(BloodGroup.A);  //小明A型血

         System.out.println("小明的血型是:"+xiaoming.getBloodGroup().getCode());
}
}

重构后比重构前有哪些优势呢?

new Person时需要传入BloodGroup类型参数而不再是int类型参数,这样就有参数类型检查了。参数为BloodGroup.A这样就更容易理解了。

其实以上就是用class来完成枚举enum的实现了。

  2、Replace Type Code With Subclass

     重构前的主体类Employee,代表员工,有3中类型(Type)。

 package nelson;

 public class Employee {

     private int _type;   //员工类型
public static final int ENGINEER = 0;
public static final int SALEMAN = 1;
public static final int MANAGER = 2; public Employee(int type)
{
_type = type;
} public int getType()
{
return _type;
}
}

重构前的应用代码:

 package nelson;

 import java.util.ArrayList;
import java.util.List; public class HelloJava { public static void main(String[] args) { System.out.println("Hello,Java"); Employee xiaotang = new Employee(Employee.ENGINEER); //小唐是工程师
Employee xiaoFang = new Employee(Employee.SALEMAN); //小方是销售
Employee laozhou = new Employee(Employee.MANAGER); //老周是经理 List<Employee> allStaffs = new ArrayList<Employee>(); //所有员工 allStaffs.add(xiaotang);
allStaffs.add(xiaoFang);
allStaffs.add(laozhou); //为所有员工发年终奖,喜闻乐见
System.out.println("\n为所有员工发年终奖");
for(Employee staff : allStaffs)
{
switch(staff.getType())
{
case Employee.ENGINEER: System.out.println("多发一个月工资");break;
case Employee.SALEMAN: System.out.println("多发1.5个月工资");break;
case Employee.MANAGER:System.out.println("多发2个月工资");break;
default:break;
}
} System.out.println("\n确定所有员工的春节放假时间");
for(Employee staff : allStaffs)
{
switch(staff.getType())
{
case Employee.ENGINEER: System.out.println("休息7天");break;
case Employee.SALEMAN: System.out.println("休息10天");break;
case Employee.MANAGER:System.out.println("休息5天");break;
default:break;
}
}
}
}

  这里有什么问题呢,看起来逻辑也是很清晰的。问题在于,应用代码中需要不断地判断员工类型。也可以将发放年终奖做成一个函数定义在Employee中,如PaidAnnualBonus(),将春节休假时间做成一个函数定义在Employee中,如SpringFestivalVacationTime(),可以确定是这两个函数里依然会对员工类型做判断。

  重构后的主体类:

 package nelson;

 public abstract class Employee {

     public static final int ENGINEER = 0;
public static final int SALEMAN = 1;
public static final int MANAGER = 2; public Employee()
{
} public static Employee Create(int type)
{
switch(type)
{
case ENGINEER: return new Engineer();
case SALEMAN: return new Salesman();
case MANAGER: return new Manager();
default:throw new IllegalArgumentException("Incorrect type code value");
}
} abstract int getType();
abstract void PaidAnnualBonus(); //发年终奖
abstract void SpringFestivalVacationTime(); //春节放假时间
} class Engineer extends Employee
{ public Engineer() {
super();
} public int getType()
{
return Employee.ENGINEER;
} public void PaidAnnualBonus()
{
System.out.println("我是工程师,我年终奖多发一个月工资");
} public void SpringFestivalVacationTime()
{
System.out.println("我是工程师,我春节放假7天");
}
} class Salesman extends Employee
{ public Salesman() {
super();
} public int getType()
{
return Employee.SALEMAN;
} public void PaidAnnualBonus()
{
System.out.println("我是销售员,我年终奖多发一个半月工资");
} public void SpringFestivalVacationTime()
{
System.out.println("我是销售员,我春节放假10天");
}
} class Manager extends Employee
{ public Manager() {
super();
} public int getType()
{
return Employee.MANAGER;
} public void PaidAnnualBonus()
{
System.out.println("我是经理,我年终奖多发两个月工资");
} public void SpringFestivalVacationTime()
{
System.out.println("我是经理,我春节放假5天");
}
}

  重构后的应用代码:

 package nelson;

 import java.util.ArrayList;
import java.util.List; public class HelloJava { public static void main(String[] args) { System.out.println("Hello,Java"); Employee xiaotang = Employee.Create(Employee.ENGINEER); //小唐是工程师
Employee xiaoFang = Employee.Create(Employee.SALEMAN); //小方是销售
Employee laozhou = Employee.Create(Employee.MANAGER); //老周是经理 List<Employee> allStaffs = new ArrayList<Employee>(); //所有员工 allStaffs.add(xiaotang);
allStaffs.add(xiaoFang);
allStaffs.add(laozhou); //为所有员工发年终奖,喜闻乐见
System.out.println("\n为所有员工发年终奖"); for(Employee staff : allStaffs)
{
staff.PaidAnnualBonus();
} System.out.println("\n确定所有员工的春节放假时间");
for(Employee staff : allStaffs)
{
staff.SpringFestivalVacationTime();
}
}
}

  重构后的应用代码变得很简洁,利用了Employee的Create函数,应用类根本不用知道Engineer类、Salesman类、Manager类的存在。

  3、Replace Type Code With State/Strategy

  关于这一点的动机,参照《重构》好好理解吧。参见下图:

  Replace Type Code With Subclass和Replayce Type Code With State/Strategy很类似,Replace-State/Strategy更彻底,将type单独列出来作为一个类,并将它作为宿主类的一个参数,好处就是上图中介绍的,“类型吗的值在对象生命周期中发生变化”和“其他原因使得宿主类不能被继承”。这两句话比较难懂,但考虑这样一个问题就好理解了。上面的Replace-Subclass例子中,如果一个对象被定义为了工程师,现在他得到了提升变为了经理,上面的代码就很难做到。后面“其他原因使得宿主类不能被继承”这里的其他原因确实还没想好(囧)。

  重构前的代码跟上面Replace Type Code With Subclass一样,也就是Replace Type Code With State/Strategy是Repace Type Code With Subclass的升级版。

  重构后的主体代码如下:

 package nelson;

 public class Employee {

     EmployeeType employeeType;

     public Employee()
{
} public void setType(int arg)
{
employeeType = EmployeeType.Create(arg);
} public void PaidAnnualBonus()
{
employeeType.PaidAnnualBonus();
} public void SpringFestivalVacationTime()
{
employeeType.SpringFestivalVacationTime();
}
} abstract class EmployeeType
{
public static final int ENGINEER = 0;
public static final int SALEMAN = 1;
public static final int MANAGER = 2; abstract int getType();
abstract void PaidAnnualBonus(); //发年终奖
abstract void SpringFestivalVacationTime(); //春节放假时间 public static EmployeeType Create(int type)
{
switch(type)
{
case ENGINEER: return new Engineer();
case SALEMAN: return new Salesman();
case MANAGER: return new Manager();
default:throw new IllegalArgumentException("Incorrect type code value");
}
}
} class Engineer extends EmployeeType
{ public Engineer() {
super();
} public int getType()
{
return EmployeeType.ENGINEER;
} public void PaidAnnualBonus()
{
System.out.println("我是工程师,我年终奖多发一个月工资");
} public void SpringFestivalVacationTime()
{
System.out.println("我是工程师,我春节放假7天");
}
} class Salesman extends EmployeeType
{ public Salesman() {
super();
} public int getType()
{
return EmployeeType.SALEMAN;
} public void PaidAnnualBonus()
{
System.out.println("我是销售员,我年终奖多发一个半月工资");
} public void SpringFestivalVacationTime()
{
System.out.println("我是销售员,我春节放假10天");
}
} class Manager extends EmployeeType
{
public Manager() {
super();
} public int getType()
{
return EmployeeType.MANAGER;
} public void PaidAnnualBonus()
{
System.out.println("我是经理,我年终奖多发两个月工资");
} public void SpringFestivalVacationTime()
{
System.out.println("我是经理,我春节放假5天");
}
}

  重构后的应用代码如下:

 package nelson;

 import java.util.ArrayList;
import java.util.List; public class HelloJava { public static void main(String[] args) { System.out.println("Hello,Java"); Employee xiaotang = new Employee();
xiaotang.setType(EmployeeType.ENGINEER); //小唐是工程师
Employee xiaoFang = new Employee();
xiaoFang.setType(EmployeeType.SALEMAN); //小方是销售
Employee laozhou = new Employee();
laozhou.setType(EmployeeType.MANAGER); //老周是经理 List<Employee> allStaffs = new ArrayList<Employee>(); //所有员工 allStaffs.add(xiaotang);
allStaffs.add(xiaoFang);
allStaffs.add(laozhou); //为所有员工发年终奖,喜闻乐见
System.out.println("\n为所有员工发年终奖"); for(Employee staff : allStaffs)
{
staff.PaidAnnualBonus();
} System.out.println("\n确定所有员工的春节放假时间");
for(Employee staff : allStaffs)
{
staff.SpringFestivalVacationTime();
}
}
}

  重构后的好处就像上面的两条动机那样,Employee类现在就可以动态改变员工类型属性了,还有就是Employee类也可以被方便的继承而不受约束。

Replace Type Code With Class和Replace Type Code With Subclass和Replace Type Code With State/Strategy的更多相关文章

  1. Multiple annotations found at this line: - The content of element type "mapper" must match "EMPTY". - Attribute "namespace" must be declared for element type "mapper".

    今天在mybatis的mapper映射配置文件中遇到了这样的问题,困扰了我3个小时: Multiple annotations found at this line: - The content of ...

  2. 解决MVC EF Code First错误:Model compatibility cannot be checked because the EdmMetadata type was not included in the model.

    Model compatibility cannot be checked because the EdmMetadata type was not included in the model. En ...

  3. No enclosing instance of type is accessible. Must qualify the allocation with an enclosing instance of type LeadRestControllerTest (e.g. x.new A() where x is an instance of ).

    java - No enclosing instance is accessible. Must qualify the allocation with an enclosing instance o ...

  4. 解决:function in namespace ‘std’ does not name a type + allocator_/nullptr/dellocator_ was not declared + base operand of ‘->’ has non-pointer type ‘std::vector<cv::Mat>’ 错误编译时报错(caffe)

    解决方法,用到了c++11,g++命令需要加上-std=c++11选项 附:g++默认的c++标准 gcc-6.4.0 gcc-7.2.0 默认是 -std=gnu++14gcc-4.3.6 gcc- ...

  5. "errcode":40163,"errmsg":"code been used...报错,做PC微信登录时出现code been used...报错问题

    这是一个坑,一个巨坑,一个恶心的坑 出现这个问题的大概意思就是微信回调了两次登录接口,code使用了两次,而在微信官方文档上写着code只能用一次,用来获取access_token,但我TM看着就糊涂 ...

  6. 【机器学习 Azure Machine Learning】使用VS Code登录到Linux VM上 (Remote-SSH), 及可直接通过VS Code编辑VM中的文件

    问题描述 在平常的工作习惯中,如果使用VS Code做脚本的开发,是一个非常好用的工具,现在也可以通过VS Code的不同方式来连接到Linux VM中(ssh), 第一种是VS Code的Termi ...

  7. The difference between a bad programmer and a good one is whether he considers his code or his data structures more important. Bad programmers worry about the code. Good programmers worry about data

    重构:改善饿了么交易系统的设计思路 原创: 盛赫 阿里巴巴中间件 昨天

  8. orcle自定义类型type/create or replace type

    一.type / create or repalce type 区别联系 相同: 可用关键字create type 或者直接用type定义自定义类型, 区别: create type 变量 as ta ...

  9. 6.Type and Member Basics

    1.The Different Kinds of Type Members 1.Constants:a symbol that identifies a never-changing data val ...

随机推荐

  1. BZOJ 4001 [TJOI2015]概率论 ——找规律

    题目太神了,证明还需要用到生成函数. 鉴于自己太菜,直接抄别人的结果好了. #include <map> #include <cmath> #include <queue ...

  2. BZOJ 4503 两个串 ——FFT

    [题目分析] 定义两个字符之间的距离为 (ai-bi)^2*ai*bi 如果能够匹配,从i到i+m的位置的和一定为0 但这和暴力没有什么区别. 发现把b字符串反过来就可以卷积用FFT了. 听说KMP+ ...

  3. ElasticSearch中Filter和Query的异同

    如下例子,查找性别是女,所在的州是PA,过滤条件是年龄是39岁,balance大于等于10000的文档: { "query": { "bool": { &quo ...

  4. COdevs 1074 食物链

    1074 食物链 2001年NOI全国竞赛  时间限制: 3 s 空间限制: 64000 KB 题目等级 : 钻石 Diamond 题目描述 Description 动物王国中有三类动物 A,B,C, ...

  5. 洛谷 P 1330 封锁阳光大学

    题目描述 曹是一只爱刷街的老曹,暑假期间,他每天都欢快地在阳光大学的校园里刷街.河蟹看到欢快的曹,感到不爽.河蟹决定封锁阳光大学,不让曹刷街. 阳光大学的校园是一张由N个点构成的无向图,N个点之间由M ...

  6. 【HDOJ6222】Heron and His Triangle(Java,二分,递推)

    题意:让你找这样的一个三角形,三条边为t,t-1,t+1,并且面积为整数,最后满足t大于等于n. n<=1e30 思路:直接推式子不会,打表找规律 f(n)=4*f(n-1)-f(n-2)(n& ...

  7. DP的序--Codeforces956E. Wardrobe

    $n \leq 10000$个盒子,有高度,高度总和$\leq 10000$,盒子有重要的和不重要的,问最多有多少重要盒子的底端在区间$[L,R]$. 这是个入门级的DP,但需要一点胆量MD这题能放D ...

  8. localStorage增删改查

    /** * 设置 本地缓存 */ export function setStorage(key, obj) { if (typeof obj === 'string') { localStorage. ...

  9. 标准C程序设计七---14

    Linux应用             编程深入            语言编程 标准C程序设计七---经典C11程序设计    以下内容为阅读:    <标准C程序设计>(第7版) 作者 ...

  10. css3 nth-child 与 nth-of-type 的区别

    by zhangxinxu from http://www.zhangxinxu.com本文地址:http://www.zhangxinxu.com/wordpress/?p=1709 一.深呼吸,直 ...