1、自封装字段(Self En capsulate Field)
(1)症状:直接访问一个字段,但与字段之间耦合关系逐渐变得笨拙
(2)解决:为这个字段建立取值/设值函数,并且只以这些函数来访问字段
(3)间接访问变量的好处:子类可以通过覆写一个函数而改变取数据的途径
(4)直接访问变量的好处:代码比较容易阅读
为待封装字段建立取值/设值函数找出该字段的所有引用点,将它们全部改为调用取值/设值函数将该字段声明为private复查,确保找出所有引用点2、以对象取代数据值(Replace Data Value with Object)
(1)症状:有一个数据项需要与其他数据和行为一起使用才有意义
(2)解决:将数据项变为对象
为待替换数值新建一个类,在其中声明一个final字段,其类型和源类中的待替换数值类型一样,然后在新类中加入这个字段的取值函数,再加上一个接受此字段为参数的构造函数编译将源类中的待替换数值字段的类型改为前面新建的类修改源类中该字段的取值函数,令它们调用新类的取值函数如果源类构造函数中用到了这个待替换字段(多半是赋值动作),我们就修改构造函数,令它们改用新类的构造函数来对字段进行赋值修改源类中待替换字段的设值函数,令它为新类创建一个实例编译,测试 class Order... private String _customer; public Order(String customer){ _customer = customer; } public String getCustomer(){ return _customer; } public void setCustomer(String arg){ _customer = arg; } private static int numberOfOrdersFor(Collection orders, String customer){ int result = 0; Iterator iter = orders.iterator(); while(iter.hashNext()){ Order each = (Order) iter.next(); if(each.getCustomer().equals(customer)) result++; } return result; } -------------------------------------------------------------------------- 重构(以对象取代数据值)之后的代码 class Customer{ private final String _name; public Customer(String name){ _name = name; } public String getName(){ return _name; } } class Order... private Customer _customer; public Order(String customerName){ _customer = new Customer(customerName); } public string getCustomerName(){ return _customer.getName(); } public void setCustomer(String customerName){ _customer = new Customer(customerName); }3、将值对象改为引用对象(Change Value to Reference)
(1)症状:从一个类衍生出许多彼此相等的实例,希望将它们替换成同一个对象
(2)解决:将这个值对象变成引用对象
(3)值对象常常用于保存少量不可修改的数据,如果希望加入一些可修改的数据,并确保对任何一个对象的修改都能影响到所有引用此一对象的地方,就需要将这个对象变成一个引用对象
class Customer{ private final String _name; public Customer(String name){ _name = name; } public String getName(){ return _name; } } //Customer类被下面Order类使用 class Order... private Customer _customer; public Order(String customerName){ _customer = new Customer(customerName); } public string getCustomerName(){ return _customer.getName(); } public void setCustomer(String customerName){ _customer = new Customer(customerName); } //上述Customer对象还是值对象,每个Order对象还是拥有各自的Customer对象 //希望得到不同的Order对象可以共享同一个Customer对象,意味着:每个客户只对应一个Customer对象 //1、采用工厂方法代替构造函数,在Customer类中定义工厂函数 //2、把原本的构造函数的声明改为private //3、在Order类中原本调用Customer构造函数的地方改为调用工厂函数 class Customer{ private final String _name; private Customer(String name){ _name = name; } public String getName(){ return _name; } public static Customer create (String name){ return new Customer(name); } } class Order... private Customer _customer; public Order(String customerName){ _customer = Customer.create(customerName); } public string getCustomerName(){ return _customer.getName(); } public void setCustomer(String customerName){ _customer = Customer.create(customerName); }4、将引用对象改为值对象(Change Reference to Value)
(1)症状:有一个引用对象,很小且不可变,而且不易管理
(2)解决:将它变为一个值对象
(3)在分布式系统和并发系统中,不可变的值对象无需考虑同步问题
(4)不可变概念:以Money类为例,Money类表示钱,其中有“币种”和“金额”两条信息,Money对象是一个不可变的值对象并非意味着薪资不能改变,而是意味着:如果你要改变薪资,就需要使用另一个Money对象来取代现有的Money对象,而不是在现有的Money对象上修改
你和Money对象之间的关系可以改变,但Money对象自身不能改变
检查重构目标是否为不可变对象,或是否可修改为不可变对象建立equals()和hashCode()编译与测试考虑是否可以删除工厂函数,并将构造函数声明为public
5、以对象取代数组(Replace Array with Object)
(1)症状:你有一个数组,其中元素各自代表不同的东西
(2)解决:以对象替换数组,对于数组中的每个元素,以一个字段来表示
(3)对象可以运用字段名称和函数名称来传达不同元素的含义
(4)使用对象可以将信息封装起来,并为它加上相关行为
(5)所有对数组的直接访问都转而调用对象中的函数,将对象中保存该数组的字段声明为private
String[] rows = new String[3]; row[0] = "Liverpool"; row[1] = "15"; String name = row[0]; int wins = Integer.parseInt(row[1]); -------------------------------------------------------- 重构(以对象取代数组)后的代码 class Performance... private String _name; public String getName(){ return _name; } public void setName(String arg){ _name = arg; } String _wins public int getWins(){ return Integer.parseInt(_wins); } public void setwins(String arg){ _wins = arg; }6、复制“被监视数据”(Duplicate Observed Data)
(1)症状:有些领域数据置身于GUI控件中,而领域函数需要访问这些数据
(2)解决:将该数据复制到一个领域对象中,建立一个Observer模式,用以同步领域对象和GUI对象内的重复数据
(3)一个良好的系统应该将处理用户界面和处理业务逻辑的代码分开,原因:
你可能需要使用若干个不同的用户界面来表现相同的业务逻辑。如果同时承担两种责任,用户界面会变得过分复杂。与GUI隔离之后,业务类的维护和演化都会更容易;你甚至可以让不同的开发者负责不同部分的开发。(4)如果你遇到的代码是以双层(Two-tiered)方式开发,业务逻辑(Business Logic)被内嵌于用户界面(UI)之中,你就有必要将行为分离出来
(5)在函数的分解和搬移时,不能仅仅只是移动数据,你必须将它复制到新建模块中,并提供相应的同步机制
public class MyFrame { private MyText beginField; private MyText endField; private MyText lengthField; public MyFrame(MyText beginField, MyText endField, MyText lengthField) { this.beginField = beginField; this.endField = endField; this.lengthField = lengthField; } private void beginChange() { calculateLength(); } private void endChange() { calculateLength(); } private void lengthChange() { calculateEnd(); } public void onchange(int type) { if (type == 1) { beginChange(); } else if (type == 2) { endChange(); } else { lengthChange(); } } private void calculateEnd() { int start = Integer.parseInt(this.beginField.getText()); int length = Integer.parseInt(this.lengthField.getText()); int end = length + start; this.endField.setText(String.valueOf(end)); } private void calculateLength() { int start = Integer.parseInt(this.beginField.getText()); int end = Integer.parseInt(this.endField.getText()); int length = start - begin; lengthField.setText(String.valueOf(length)); } } 注:上述计算结束的值函数(calculateEnd)和计算长度的值函数 (calculateLength) 不该置于界面类中,应该放在业务逻辑层 ------------------------------------------------------------------------ 重构(复制“被监视数据”)后的代码 (1)引入观察者模式 (2)利用Move Method将calculateEnd及calculateLength移到新类中 (3)建立委托关系 public class MyFrame implements Observer { @Override public void update(Observable o, Object arg) { this.beginField.setText(subject.getBegin()); this.endField.setText(subject.getEnd()); this.lengthField.setText(subject.getLength()); } private MyText beginField; private MyText endField; private MyText lengthField; Interval subject; public MyFrame(MyText beginField, MyText endField, MyText lengthField) { super(); this.beginField = beginField; this.endField = endField; this.lengthField = lengthField; subject = new Interval(this.beginField.getText(), this.endField.getText(), this.lengthField.getText()); subject.addObserver(this); } private void baginLost() { setBegin(beginField.getText()); } private void endLost() { setEnd(endField.getText()); } private void lengthLost() { setLength(lengthField.getText()); } public void onchange(int type) { if (type == 1) { baginLost(); } else if (type == 2) { endLost(); } else { lengthLost(); } } public String getBegin() { return this.subject.getBegin(); } public void setBegin(String begin) { this.subject.setBegin(begin); } public String getEnd() { return this.subject.getEnd(); } public void setEnd(String end) { this.subject.setEnd(end); } public String getLength() { return this.subject.getLength(); } public void setLength(String length) { this.subject.setLength(length); } } public class Interval extends Observable { private String begin; private String end; private String length; public Interval(String begin, String end, String length) { super(); this.begin = begin; this.end = end; this.length = length; } @Override public synchronized void addObserver(Observer o) { super.addObserver(o); } public String getBegin() { return begin; } public void setBegin(String begin) { this.begin = begin; change(1); } public String getEnd() { return end; } public void setEnd(String end) { this.end = end; change(1); } public String getLength() { return length; } public void setLength(String length) { this.length = length; change(2); } private void change(int type) { if(type==1){ calculateLength(); }else { calculateEnd(); } super.setChanged(); super.notifyObservers(); } private void calculateEnd() { int begin=Integer.parseInt(this.begin); int length=Integer.parseInt(this.length); int end=length+begin; this.end=String.valueOf(end); } private void calculateLength() { int begin=Integer.parseInt(this.begin); int end=Integer.parseInt(this.end); int length=end-begin; this.length=String.valueOf(length); } } 注:上述案例引用自颠坤 原文:https://blog.csdn.net/wobendiankun/article/details/384466097、将单向关联改为双向关联(Change Unidirectional Association to Bidirectional)
(1)症状:两个类都需要使用对方特性,但其间只有一条单向连接
(2)解决:添加一个反向指针,并使修改函数能够同时更新两条连接
(3)本重构手法需要添加测试
在被引用类中增加一个字段,用以保存反向指针。决定由哪个类—引用端还是被引用端—控制关联关系。在被控端建立一个辅助函数,其命名应该清楚指出它的有限用途。如果既有的修改函数在控制端,让它负责更新反向指针。如果既有的控制函数在被控端,就在控制端建立一个控制函数,并让既有的修改函数调用这个新建的控制函数。 定单(Order)类和客户(Customer)类, Order引用了Customer,Customer则并没有引用Order class Order... Customer _customer; Customer getCustomer() { return _customer; } void setCustomer (Customer arg) { _customer = arg; } ------------------------------------------------------------------ 重构 1、 为Customer添加一个值域 由于一个客户可以拥有多份定单,所以这个新增值域应该是个群集(collection), 同时不希望同一份订单在同一个群集中出现一次以上,所以选用set集合 class Customer { private Set _orders = new HashSet(); 2、决定由哪一个class负责控制关联性(association) (1)如果两者都是reference objects,而其间的关联是「一对多」关系,那么就由「拥有单一 reference 」的那一方承担「控制者」角色。 (2)如果某个对象是另一对象的组成(component),那么由后者负责控制关联性。 (3)如果两者都是reference objects,而其间的关联是「多对多」关系,那么随便其中哪个对象来控制关联性,都无所谓。 以本例而言,如果一个客户可拥有多份定单,那么就由Order class (定单)来控制关联性。 3、由Order负责控制关联性,必须为Customer添加一个辅助函数,让Order可以直接访问 _orders(订单〕群集 class Customer... void addOrder(Order arg){ arg.setCustomer(this); } private Set _orders = new HashSet(); Set friendOrders() { return _orders; } //改变修改函数(modifier),令它同时更新反向指针 class Order... private Customer _customer; Customer getCustomer(){ return _customer; } void setCustomer(){ if(_customer != null) _customer.friendOrders().remove(this); _customer = arg; if(_customer != null) _customer.friendOrders().add(this); }8、将双向关联改为单向关联(Change Bidirectional Association to Unidirectional)
(1)症状:两个类之间有双向关联,但其中一个类如今不再需要另一个类的特性
(2)解决:去除不必要的关联
(3)大量的双向链接容易造成“僵尸对象”:某个对象本来已经该死亡,却仍然保留在系统中,因为对它的引用还没有完全清除
(4)双向关联迫使两个类之间有了依赖:对其中任一个类的任何修改,都可能引发另一个类的变化
找出保存“你想去除的指针”的字段,检查它的每一个用户,判断是否可以去除该指针如果客户使用了取值函数,先运用Self Encapsulate Field将待删除字段自我封装起来,然后使用Substitute Algorithm对付取值函数,令它不再使用该字段,然后编译,测试如果客户未使用取值函数,那就直接修改待删除字段的所有引用点:改以其它途径获得该字段所保存的对象,每次修改后,编译并测试如果已经没有任何函数使用待删除字段,移除所有对该字段的更新逻辑,然后移除该字段 class Order... private Customer _customer; Customer getCustomer(){ return _customer; } void setCustomer(){ if(_customer != null) _customer.friendOrders().remove(this); _customer = arg; if(_customer != null) _customer.friendOrders().add(this); } class Customer... void addOrder(Order arg){ arg.setCustomer(this); } private Set _orders = new HashSet(); Set friendOrders(){ return _orders; } -------------------------------------------------------------- 重构(将双向关联改为单向关联)后的代码 class Order... double getDiscountPrice(Customer customer){ return getGrossPrice() * (1 - customer.getDiscount()); } class customer... double getPriceFor(Order order){ Assert.isTrue(_orders.contains(order)); return order.getdiscountedPrice(this); Customer getCustomer(){ Iterator iter = Customer.getInstances().iterator(); while(iter.hasNext()){ if(each.containsOrder(this)) return each; } return null; }9、以字面常量取代魔法数(Replace Magic Number with Symbolic Constant)
(1)症状:你有一个字面数值,带有特别含义
(2)创造一个常量,根据其意义为它命名,并将上述的字面数值替换为这个常量
(3)魔法数:指拥有特殊意义,却又不能明确表现出这种意义的数字
(4)魔法数带来的问题:一旦魔法数发生改变,就必须在程序中找到所有魔法数,并将它们全部修改一遍
double potentialEnergy(double mass, double height){ return mass * 9.81 * height; } ------------------------------------------------------------------------------ 重构(以字面常量取代魔法数)之后的代码: static final double GRAVITATIONAL_CONSTANT = 9.81; double potentialEnergy(double mass, double height){ return mass * GRAVITATIONAL_CONSTANT * height; }10、封装字段(Encapsulate Field)
(1)症状:你的类中存在一个public字段
(2)解决:将它声明为private,并提供相应的访问函数
(3)面向对象的首要原则之一就是封装,不应该将数据声明为public,否则其他对象就有可能访问甚至修改数据
public String name; ------------------------------------------------------------------------------- 重构(封装字段)之后的代码: private String name; public String getName() { return name; } public void setName(String name) { this.name = name; }11、封装集合(Encapsulate Collection)
(1)症状:有个函数返回一个集合
(2)解决:让这个函数返回该集合的一个只读副本,并在这个类中提供添加/移除集合元素的函数
(3)不应该为整个集合提供一个设值函数,但应该提供用以为集合添加/移除元素的函数
class Person ... private Set courses = new HashSet(); public getCourses() { return courses; } public setCourses(List courses) { this.courses = courses; } ------------------------------------------------------------------------------- 重构(封装集合)之后的代码 class Person ... private Set courses = new HashSet(); public getCourses() { return courses; } public void addCourse(Course course) { this.courses.add(course); } public void removeCourse(Course course) { this.courses.remove(course); }12、以数据类取代记录(Replace Record with Data Class)
(1)症状:你需要面对传统编程环境中的记录结构
(2)解决:为该记录创建一个“哑”数据对象
新建一个类,表示这个结构对于记录中的每一项数据,在新建的类中建立一个对应的private字段,并提供相应的取值/设置函数
13、以类取代类型码(Replace Type Code with Class)
(1)症状:类中有一个数值类型码,但它并不影响类的行为
(2)解决:以一个新的类替换该数值类型码
(3)任何接受类型码作为参数的函数,所期望的实际上是一个数值,无法强制使用符号名,会大大降低代码的可读性
(4)把数值换成一个类,编译器就可以对这个类进行类型检验
(5)只有当类型码是纯粹数据时,才能以类来取代它
为type code 建立一个Class修改原来的class,让它使用上述新建的class编译,测试对于source class中每一个使用type code的函数,相应建立一个函数,让新函数使用新建的class逐一修改source class用户,让它们使用新接口每修改一个用户,编译并测试删除使用type code的旧接口,并删除保存旧type code的静态变量编译、测试 用type code来表示血型的Person类 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; } } -------------------------------------------------------------------------------- 重构(以类取代类型码)之后的代码 (1)新建BloodGroup Class表示血型,并在这个Class中保存type code 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); private static final BloodGroup[] _values = {O, A, B, AB}; private final int _code; private BloodGroup (int code ) { _code = code; } public int getCode() { return _code; } public static BloodGroup code(int arg) { return _values[arg]; } } (2)把Person中的type code改成新的Class中的code class Person { public static final int O = BloodGroup.O.getCode(); public static final int A = BloodGroup.A.getCode(); public static final int B = BloodGroup.B.getCode(); public static final int AB = BloodGroup.AB.getCode(); private BloodGroup _bloodGroup; public Person (int bloodGroup) { _bloodGroup = BloodGroup.code(bloodGroup); } public int getBloodGroup() { return _bloodGroup.getCode(); } public void setBloodGroup(int arg) { _bloodGroup = BloodGroup.code (arg); } } (3)在BloodGroup Class中已经有了运行时的检查,还需要把Person中的数字改成BloodGroup。 首先,使用Rename Method修改type code访问函数的名称,说明当前情况: class Person... public int getBloodGroupCode() { return _bloodGroup.getCode(); } 然后,增加一个新的get方法,返回BloodGroup public BloodGroup getBloodGroup() { return _bloodGroup; } 另外,我还要建立新的使用BloodGroup的构造函数和设置函数 public Person (BloodGroup bloodGroup ) { _bloodGroup = bloodGroup; } public void setBloodGroup(BloodGroup arg) { _bloodGroup = arg; } 继续处理Person用户。每次只处理一个用户,这样才可以保持小步前进。对Person内的所有static变量的所有引用点也需要修改,因此 Person thePerson = new Person(Person.A) 变成 Person thePerson = new Person(BloodGroup.A); 调用取值函数必须改为调用新取值函数。因此 thePerson.getBloodGroupCode() 变成 thePerson.getBloodGroup().getCode() 设置函数也一样。因此 thePerson.setBloodGroup(Person.AB) 变成 thePerson.setBloodGroup(BloodGroup.AB) 修改完毕所有的客户端后,就可以删除那些原本使用整数的旧的取值函数、静态变量、设置函数和构造函数。 还可以将BloodGroup中的使用整数的函数声明为private class BloodGroup... private int getCode() { return _code; } private static BloodGroup code(int arg) { return _values[arg]; }14、以子类取代类型码(Replace Type Code with Subclasses)
(1)症状:有一个不可变的类型码,它会影响类的行为
(2)解决:以子类取代这个类型码
(3)如果类中有一个标志,看起来像是switch这样,或者if-else if-else if这种结构,并且以后可能还要增加其他类型的标志,就可以使用这种重构方法
先使用Self Encapsulate Field(自封装字段)将类型码自我封装起来,如果类型码有在构造函数里赋值,就需要将构造函数改为工厂函数来创建对象为类中的每一个标志建立一个子类,在每个子类中覆写类型码的get函数,返回相应的类型码从超类中删掉保存标志的字段,将超类的get函数声明为抽象函数 public class Employee { private static final int ENGINEER = 0; private static final int SALESMAN = 1; private int type; public Employee(int type) { this.type = type; } public int getType() { return this.type; } } -------------------------------------------------------------------------------- 重构(以子类取代类型码)之后代码 public abstract class Employee { protected static final int ENGINEER = 0; protected static final int SALESMAN = 1; protected Employee() {} public static Employee createEmployee(int type) { switch (type) { case ENGINEER: { return new Engineer(); } case SALESMAN: { return new Salesman(); } default: { throw new RuntimeException("createEmployee error"); } } } protected abstract int getType(); } public class Engineer extends Employee { protected Engineer() { super(); // TODO Auto-generated constructor stub } @Override protected int getType() { // TODO Auto-generated method stub return Employee.ENGINEER; } } public class Salesman extends Employee { protected Salesman() { super(); // TODO Auto-generated constructor stub } @Override protected int getType() { // TODO Auto-generated method stub return Employee.SALESMAN; } } 注:以后想要增加一种方法就只要创建一个子类,然后修改一下父类的工厂函数就可以了15、以State/Strategy 取代类型码(Replace Type Code with State/Strategy)
(1)症状:有一个类型码,它会影响类的行为,但你无法通过继承手段消除它
(2)解决:以状态对象取代类型码
(3)本项重构和Replace Type Code with Subclass (以子类取代类型码)类似,但如果“类型码在对象生命期中发生变化”或“其他原因使得宿主类不能被继承”,你也可以使用本重构。
(4)Replace Type Code With State/Strategy是Repace Type Code With Subclass的升级版。
(5)State模式和Strategy模式非常相似,选择其中哪一个:
如果你打算在完成本重构后再以Replace Conditional with Polymorphism (以多态取代条件表达式)简化一个算法,那么选择Strategy模式较合适;如果你打算搬移状态相关的数据,而且你把新建对象视为一种变迁状态,就应该选择State模式。 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; } } -------------------------------------------------------------------------------- 重构(以State/Strategy 取代类型码)之后代码 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天"); } } 注:上述案例引用自kanite 原文:https://www.cnblogs.com/kanite/p/7670305.html16、以字段取代子类(Replace Subclass with Fields)
(1)症状:你的各个子类的唯一差别只在“返回常量数据”的函数身上
(2)解决:修改这些函数,使它们返回超类中的某个(新增)字段,然后销毁子类
(3)建立子类的目的,是为了增加新特性或变化其行为
(4)常量函数:让不同子类中的同一访问函数返回不同的值,可以在超类中将访问函数声明为抽象函数,并在不同的子类中让它返回不同的值
对所有子类使用Replace Constructor with Factory Method如果有任何代码直接引用子类,令它改而引用超类针对每个常量函数,在超类中声明一个final字段为超类声明一个protected构造函数,用以初始化这些新增字段新建或修改子类构造函数,使它调用超类的新增构造函数编译,测试在超类中实现所有常量函数,令它们返回相应的字段,然后将函数从子类中删掉每删除一个常量函数,编译并测试子类中所有的常量函数都被删除后,使用Inline Method将子类构造函数内联到超类的工厂函数中编译,测试将子类删掉编译,测试重复“内联构造函数,删除子类”过程,直到所有子类都被删除简化条件表达式 abstract class Person{ abstract boolean isMale(); abstract char getCode(); .... } class Male extends Person{ boolean isMale(){ return true; } char getCode(){ return 'M'; } } class Female extends Person{ boolean isMale(){ return false; } char getCode(){ return 'F'; } } ---------------------------------------------------------------------------------- 重构(以字段取代子类)之后的代码 class Person... private final boolean _isMale; private final char _code; protected Person(boolean isMale, char code){ _isMale = isMale; _code = code; } boolean isMale(){ return _isMale; } static Person createMale(){ return new Person(true,'M'); } static Person createFemale(){ return new Person(false, 'F'); }
尾注
上述的总结与思考是基于对《重构—改善既有代码的设计》这本书的精读与演绎更多及时干货,请关注微信公众号:JAVA万维猿圈