(一):重新组织自己的函数
问题:过长函数(Long Method) 往往需要我们通过手法提炼函数(Extract Method),把一段代码从原函数里面提炼出一段代码放入一单独函数中。遇到的问题主要有
临时变量问题(Temp variable matter) 和参数问题(Parameter matter),
(1)、在处理临时变量问题时我们提供(replace temp with query)去掉一些临时变量。如果很多地方用到这个临时变量,
可以是用Split temporary variable,将它变得容易替换,如果临时变量太乱,可以引入Replace Method with Method Object代价是引入新的class。
(2)、处理参数使用remove Assignments to parameters。
最后函数分解以后使用Substitute Algorithm引入更清晰的算法
做法:在Extract Method 关键是要在函数名称和函数体之间的语义距离,强化代码的清晰度。一下是Exctract Method的三种实例
1) 没有局部变量,直接提炼。2) 有局部变量,把这个局部变量作为参数传入被提炼函数 3)如果需要对局部变量赋值,得需要使用remove Assignments to parameters
如果被赋值的局部变量只在被提炼的函数中被赋值,则把这个局部变量声明到被提炼函数中。如果被赋值的局部变量需要在被提炼的函数其它的地方使用,则需要被提炼
函数返回一个被修改后的值。如果遇到返回的不止一个值,则最好的方法是再重新提炼一个新的方法,每次只返回一个值最佳。
(1)Inline Temp
如果有一个临时变量,只被一个简单的表达式赋值过一次。重构手法,将所有对该变量的引用动作,替换为对它赋值的那个表达式
double price = anOrder*basePrice();
return price>1000
替换=》 return anOrder*basePrice()>1000;
(2)Replace Temp with Query
一个临时变量保存了一个表达式的结果,需要把这个表达式提炼到一个独立函数里,在所以应用到这个临时变量的地方替换为一个新函数的调用。
double basePrice = _quantity*_item;
if (basePrice>1000){
return basePrice*0.95;
} else {
return basePrice*0.98;
}
替换=》
public basePrice(){
return _quantity*_item;
}
if (basePrice()>1000) {
return basePrice*0.95
} else {
return basePrice*0.98;
}
(3) Introduce Explaining Variable
将复杂表达式的结果放入一个临时变量里面,以此变量名来解释表达式用途
if(platform.toUpperCase().indexOf("MSC")>-1 && browers.toUpperCase.indexOf("IE")>-1 && size()>0){...}
替换=》
final boolean isMSC = platform.toUpperCase().indexOf("MSC");
final boolean isBrowsers = browers.toUpperCase.indexOf("IE");
final boolean isResize = size()>0;
if(isMSC && isBrowsers && isResize){...}。
这个也可以使用Replace Temp with Query来重构
public boolean isMSC(){retrun platform.toUpperCase().indexOf("MSC");}
public boolean isBrowsers(){browers.toUpperCase.indexOf("IE");}
public boolean isResize(){size()>0}
if(isMSC() && isBrowsers() && isResize()){...}
(4) Split Temporary Variable 解剖临时变量
在程序中临时变量超过一次,它既不是循环变量,也不是集中临时变量。则每次给临时变量赋值,创造一个独立的,对应的临时变量
double temp = 2*(width+height);
System.out.println(temp);
temp = width*height;
System.out.println(temp);
替换=》
final double perimeter = 2*(width+height);
System.out.println(perimeter);
final double area = widht * height;
System.out.println(area);
final 关键字的作用是让这两个变量只被赋值一次
(5) Remove Assignments Parameters
对一个参数进行赋值操作,以一个临时变量来取代该参数的位置
int disCount(int nInput, int quantity){
if (nInput>50){
nInput -= 2;
}
替换=》
int disCount(int nInput, int quantity){
int result = nInput;
if (nInput>50){
result -=2;
}
}
(6) Replace Mehtod with Method Object
将所有的局部变量都变成函数对象(Mehtod Class)的值域(Field)。然后你就可以对这个新的对象使用Extract Method 来创造新的函数
(7) Substitute Algorithm(替换算法)
String findPersion(String[] persion){
for (int i = 0; i<persion.length; i++){
if(persion[i].equals("Don")){
return "Don";
}
if(persion[i].equals("John")){
return "John";
}
if(persion[i].equals("Kent")){
return "Kent";
}
}
}
替换=》
String findPersion(String[] persion){
List<String> perList = Arrays.asList(new String[]{"Don","John","Kent"});
for (int i = 0; i<persion.length; i++){
if(perList.containts(persion[i])){
return persion[i];
}
}
return "";
}
(二):在对象之间搬移特性
通过Move Method 和Move Field来移动对象的行为,通常是先使用Move Field 后使用Move Method。如果class 责任太大,变得臃肿不堪,则使用
Extract Class来分离一部分责任,如果一个类太不负责任,则使用Inline class融入到另一个类。如果一个类使用了另外一个类运用Hide Delegate
隐藏这种关系,是很有帮助的。有时隐藏会导致Delegate Class接口拥挤,此时需要使用Remove Middle Man
(1) Move Method
如果这个函数使用另外一个对象的次数比使用自己所驻次数还多,则需要考虑Move Method.
class Account{
private AccountType accountType;
private int daysOverdraft;
double overdraftCharg(){
if(accountType.isPremium()){//这个方法每次调用都需要访问accountType对象
double result = 0l;
if(daysOverdraft>7){
result += (daysOverdraft -7)*0.85;
return result;
}
}
else {
return daysOverdraft*1.75;
}
}
double bankCharg(){
double result =4.5;
if(daysOverdraft>0) {
result +=overdraftCharg();
return result;
}
}
}
假设有数种新帐户,每一种都有自己的透支计费规则,所有希望透支计费搬移到AccountType里去
public class AccountType{
double overdraftCharg(int daysOverdraft){
if(isPremium()){
double result = 0l;
if(daysOverdraft>7){
result += (daysOverdraft -7)*0.85;
return result;
}
}
else {
return daysOverdraft*1.75;
}
}
}
但我们在AccountType 类里面需要使用Account特性,将Account对象作为参数传递给AccountType,如果特性是个变量,只需要把这个变量当作参数传递过去
然后我们可以删除Account里的方法,也可以保留,如果删除需要替换它在这个类其它地方的引用
class Account{
private AccountType accountType;
private int daysOverdraft;
double overdraftCharg(){
return accountType.overdraftCharg(daysOverdraft);
}
double bankCharg(){
double result =4.5;
if(daysOverdraft>0) {
result +=overdraftCharg();//或者如果已经删除了overdraftCharg() 则 result +=accountType.overdraftCharg(daysOverdraft);
return result;
}
}
}
(2) Move Field
在程序中,某个(Field)被其所驻clss之外一个class更多的使用
class class Account{
private AccountType accoutType;
private double rate;
double cacRate(int amount, intdays){
return rate*amount*days/365;
}
}
如果rate经常被AccountType类访问,则需要移动这个Field 到这个类
public class AccountType{
private double rate;
public void setRate(double rate){
this.rate = rate;
}
public double getRate(){
return rate;
}
}
=》替换
double cacRate(int amount, intdays){
return accountType.getRate()*amount*days/365;
}
如果有多个函数已经使用了这个属性还需要自封装这个属性(Self Encapsulate Field)
private void setTypeRate(rate){
accountType.setRate(rage);
}
private double getTypeRate(){
return accountType.getRate();
}
double cacRate(int amount, intdays){
return getTypeRate()*amount*days/365;
}
(3)Hide Delegate 隐藏委托关系
在service端建立Client所有的所需函数。但有时客户端需要调用service端某个值域基础之上的函数。那么客户端必须要知道这个值域对象(Delegat Object)。
为了达到这一目的需要隐藏委托关系。把委托关系隐藏起来,从而消除这种依存性,即使委托关系发生变化也只是在service。而不会波及Client
public class Persion{
Department department;
public void setDepartment(Department department){
this.department = department;
}
public Department getDepartment(){
return department;
}
}
public class Department{
Persion Manger;
public void setManger(Manger manger){
this.manger = manger;
}
public Manger getManger(){
return manger;
}
}
Client: 客户想知道jack 的经理是谁?必须先取得Department
Persion persion = new Persion("jack");
Persion manager = persion.getDepartment().getManager();
如果客户需要隐藏Department ,减少耦合,需要在Persion里面建立一个委托函数
Persion 类:
public Persion getManager(){
return department.getManager();
}
客户端就可以这样调用了
Persion manger = persion.getManager();
(3)Remove Middle Man (移除中间人)
但客户端需要使用受托类更多的新特性时,都需要在service 加入委托函数。随着新特性的增加。service就变成了中间人。解决办法是删除这些委托函数
重新让客户端通过调用委托对象来得到新特性。
Persion manager = persion.getDepartment().getManager();
(4) Introduce Foreign Method (引入外加函数)
如果一个service class为你提供了所有服务。但是又需要一项新的服务,而这个class 无法提供(无法修改源代码)。则需要在Client 中提供一个额外的方法,参数就是
这个service class 实例。并且写上注释为foreign Method 本应在service class 中实现。这样一来如果要搬移这个Forign Method时候就很
容易找到了。
Client
Date nextDate = new Date(previousEnd.getYear(),previousEnd.getMonth(),previousEnd.getDate()+1);
=》替换
public static Date nextDate(Date previousEnd){
return new Date(previousEnd.getYear(),previousEnd.getMonth(),previousEnd.getDate()+1);
}
(5) Introduce Local Extension(引入本地扩展)
如果引入外加函数太多了,就需要Introduce Local Extension,建立一个新的class 包含这些Foreign Mehtod,让这些扩展品成为source class
的subclass(子类)或者wrapper(覆盖类)
public class DateSub extends Date{
//override supClass construct Method
public DateSub(String str){
super(str);
}
public DateSub(Date date){
super(date.getTime());
}
//把外加函数都搬移过来
//下一天
public static Date nextDate(){
return new Date(getYear(),getMonth(),getDate()+1);
}
//上一天
public static Date PreDate(Date previousEnd){
return new Date(getYear(),getMonth(),getDate()-1);
}
}
(三):重新组织数据
(1) Replace Date Value with Object 以对象取代数据值
当简单的数据项要表现复杂的行为就需要把这个数据项变成对象了。
public class Order{
private String customer;
public Order(String customer){
this.customer = customer;
}
public void SetCustomer(String customer){
this.customer = customer;
}
public String getCustomer(){
return this.customer;
}
public int calculateOrder(){
int result = 0;
for(Interator iterator = orders.iterator(); iterator.hasNext()){
Order order = (Order)iterator.next();
if (order.getCustomer().equals(customer)){
result ++;
}
}
return result;
}
}
=》替换
public Class Customer{
private final String name;
public Customer(String name){
this.name = name;
}
public void SetName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
}
public class Order{
private Customer customer;
public Order(String customerName){
this.customer = new Customer(customerName);
}
public void SetCustomer(String customerName){
this.customer = new Customer(customerName);
}
public String getCustomerName(){
return customer.getName();
}
public int calculateOrder(){
int result = 0;
for(Interator iterator = orders.iterator(); iterator.hasNext()){
Order order = (Order)iterator.next();
if (order.getCustomerName().equals(customer)){
result ++;
}
}
return result;
}
}
这样,每个customer 作为实值对象(Value Object) 来处理,每个Order 都有一个自己的customer
(2) change value to Reference (将实例对象该改为引用对象)
这样可以使所有的order 对象共享一个customer对象。而不是每个order对象都保存一份customer对象。如果我在customer对象中加入邮政编码和其它信息
那么对邮政编码的修改,应该会影响到所有引用此对象的地方。所以需要Reference Object。
找出由什么样的对象来访问新对象的途径:
a. 可能是一个静态字典(Static Dictionary)或者是注册对象(Registry Object)。
b. 多个对象作为新对象的访问点(access point)
以上面那个例子为例,我们希望一个客户拥有多份不同的订单。所有的order 对象都可以共享一个customer对象。
首先要介绍一种重构方法 Replace Constructor with Method Factory
public class Customer{
private String name;
private Customer(String name){
this.name = name;
}
private static Map map =new HashMap();
public void store(){
map.put(name,this);
}
//这些对象预先创建
private static loadCustomer(){
new Customer("zhangsan").store();
new Customer("wangwu").store();
new Customer("zhaoliu").store();
}
//访问点
public static Customer getInstance(String name){
return (Customer)map.get(name);
}
}
public class Order{
private Customer customer;
pulbic Order(String name){
Customer.getInstance(name);
}
}
Order order1 = new Order("zhangsan");
Order order2 = new Order("zhangsan");
无论何时得到的都是一个customer引用。如果order1,改变了customer对象的某个特征,不如电话号码,则从order2中得到的也是改变后的值
//因为我们customer的构造函数是private ,所以不能在其它的地方Customer c = new Customer("name") 来创建对象,只能通过Mehtod Factory
静态方法来得到这个实例这个就类似Sington Pattern。
(3)Change Reference to Value 将引用对象改为实值对象
value Object 应该是不可变的(immutable),无论何时,调用此对象的查询函数得到的都是一个结果,
比如第一个例子中,customer作为value Object。每个order 都有自己的一份customer。
order1.getCustomer("张三").setTelepho("123");
order2.getCustomer("张三").getTelepho();
如果张三开始的号码是135。order2得到的customer 的值还是135,虽然order1已经改变了张三的电话号码。
reference Object 应该是可变的(mutable),确保某一对象修改,自动会更新其它代表某一相同事物的其它对象的修改
要把reference Object 变成value Object 只需要重写equals()和hashCode()两个方法,并且去掉Method Factory 对构造函数的调用,
(4)Replace Array with Object (以对象取代数组)
数组一般只用于以某种顺序容纳一组相似的对象,如果发现数组容纳了不同的对象,这样我们很难知道数组的第一个元素是人名还是年龄。
此时,我们应该用对象封装起来,运用值域名称和和函数名称来传达这样的信息。
String[] row = new String[3];
row[0] = "张三";
row[1] = 2;
=》替换
Performance p =new Performance();
p.setName("张三");
p.setAge(2);
(5) Replace Magic Number with Symblic Constant 以符号常量或者字面常量取代魔法数。
double potan(double mass,double height){
return mass*9.18*height;
}
=》替换
public static final GRANT_CONSTANT = 9.18;
double potan(double mass,double height){
return mass * GRANT_CONSTANT *height;
}
(6) Encapsulate Field(封装值域)
public String name;
=》替换
private String name;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
(7)Encapsulate Collection(封装集合)
一般不能使用getter或者setting来得到群集或者设置群集,如果用户通过getting得到了群集,然后修改群集内容,而群集对象一无所知,所以需要
群集对象自己提供add(),remove()方法来控制群集对象的添加和删除
比如在学生和课程类设计中,学生和课程的关系是一对多
在学生类里添加addCourse(),removeCourse()方法
public class Persion{
Map courses = new HashMap();
public void addCourse(Courses course){
courses.add(course);
}
public void addCourses(List<Courses> courses){
courses.addAll(courses);
}
public void removeCourses(Courses course){
courses.removeCourses(course);
}
}
(8) Replace Subclass with Fields (以值域取代子类)
当各个子类唯一的区别是返回常量数据的函数上,常量函数只是返回一些硬编码,可以在不同的SubClass中调用这个函数得到不同的值
abstract class Persion{
abstract public boolean isMale();
abstract public char getCode();
}
class Male extends Persion{
public boolean isMale(){
return true;
}
public char getCode(){
return 'M';
}
}
class Pemale extends Persion {
public boolean isMale(){
return false;
}
public char getCode(){
return 'F';
}
}
=》替换
public class Persion{
//replace construct with method factor
private boolean arg0;
private char arg1;
public static Persion createMale(){
return new Persion(true,'M');
}
public static Persion createPemale(){
return new Persion(false,'F');
}
public Persion(boolean arg0,char arg1){
this.arg0 = arg0;
this.arg1 = arg1;
}
}
//Client
Persion persion = Persion.createMale();//male Object
Persion persion1 = Persion.createPemale();//Pemale Object
(四) 简化条件表达式
(1)Decompose Conditional(分解条件)
把大块头代码分解成函数
if(date.before(DATE_START) || data.after(DATE_END)){
charge = quantity * _winterCharge * windcharge;
} else {
charge = quantity * sumRage;
}
=》替换
private boolean noteSummber(){
return date.before(DATE_START) || data.after(DATE_END);
}
private double winterCharge(){
return quantity * _winterCharge * windcharge;
}
private double sumCharge(){
return quantity * sumRage;
}
if(noteSummber()){
charge = winterCharge();
} else {
charge = sumCharge();
}
(2)Consolidate Conditional Expression(合并条件式)
有的时候条件检查各不相同,而结果却一致,这时可以考虑用logic-AND 或者logic-OR将条件合并为一个表达式
double disabilityAmount(){
if(_seriv<0) return 0;
if(_mountDisabled<12) return 0;
if(_isPartTime) return 0;
//amount
...
}
=》替换 logic-OR
boolean isNotForDisabled (){
return ((_seriv<0) || (_mountDisabled<12) || _isPartTime);
}
double disabilityAmount(){
if(isNotForDisabled ()) return 0;
//amount
...
}
logic-AND 实例
if(overs()){
if(lengthOfService()>10){
return 0;
}
}
return 0.5;
等价
if(overs() && lengthOfService()>10) {
return 0;
} else {
return 0.5;
}
return (overs() && lengthOfService()>10)?0:0.5;
(3) Compsolidate Duplicate Conditional Fragments(合并重复的条件片段)
有时候,如果一组条件式的所有分支都执行了所有相同的某个代码,就应该把这段代码移动到条件以外
if(isSepcialDeal()){
total = price * 0.95;
send();
} else {
total = price * 0.98;
send();
}
=》替换
if(isSepcialDeal()){
total = price * 0.95;
} else {
total = price * 0.98;
}
send();
(4)Remove ControlFlag (移除控制标记)
以break continue return语句代替控制标记
void checkSecurity(String[] people){
for (int i = 0 ;i <people.length; i++){
boolean isFound = false;
if(!isFound){
if(people[i].equals("Don")){
sendAlert();
isFound = true;
}
if(people[i].equals("JOM")){
sendAlert();
isFound = true;
}
}
}
}
=》替换
public void checkSecurity(String[] people){
for (int i = 0 ;i <people.length; i++){
if(people[i].equals("Don")){
sendAlert();
break;
}
if(people[i].equals("JOM")){
sendAlert();
break;
}
}
}
return 语句返回控制标记
public void checkSecurity(String[] people){
String found = findScran(people);
somerAlert(found);
}
public String findScran(String[] people){
String found = "";
if("".equals(found)){
for (int i = 0 ;i <people.length; i++){
if(people[i].equals("Don")){
sendAlert();
found = "Don"
}
if(people[i].equals("JOM")){
sendAlert();
found = "JOM";
}
}
}
}
(5) Replace Nested Conditional with Guard clauses 以卫语句取代嵌套语句
double getPayAmount(){
double result;
if(_isCount) result = deadAmount();
else{
if(_separated) result = separatAmount();
else{
if(_isRetired) result = retiredAmount();
else{
result = nomalAmount();
}
}
}
return result;
}
=》替换
if(_isCount) reutrn deadAmount();
if(_separated) reutrn separatAmount();
if(_isRetired) reutrn retiredAmount()
reutrn nomalAmount();
条件式一般有两种,一种是所有分支都属于正常行为,另外一种是提供的答案只有一种是正常的,另外的都不正常。
通过替换我们可以看出嵌套的if else 讲究的是一个入口和一个出口,而替换后的函数代码更清晰,只要符合条件
了就直接返回退出,而不需要再看后面的代码。
特例:将条件反转
pubic double getCapital(){
double result = 0.0;
if(_capital>0.0){
if(_intRate > 0.0 && _duration >0.0){
result = (intRate/duration)*rate;
}
}
return result;
}
=》替换
public double getCapital(){
double result = 0.0;
if(_capital <=0) return result;
if(_intRate > 0.0 && _duration >0.0){
result = (intRate/duration)*rate;
}
return result;
}
(五) 简化函数调用
(1)Separate Query form Modifier 将查询函数和修改函数分离
如果某个函数只向你提供一个值,没有其它任何副作用(连带关系)。那么你就可以任意调用这个函数值。如果某个函数既返回对象状态值,又修改对象状态。则
需要分离这两个函数。
(2)Parameterize Method 令函数携带参数
若干函数做了类似的工作,但在函数体中却包含不同的值,需要建立单一的函数,以参数表达那些不同的值
Class Employee{
public void tenPercentRaise(){
salary*0.1;
}
public void fivePercentRaise(){
salary*1.05;
}
}
=》替换
Class Employee{
public void raise(double factor){
salary*factor;
}
}
(3) Replace Parameter with Explicit Methods 以明确的函数取代参数
这个和Parameterize Method 正好相反
如果某个参数有离散取值,而函数又以条件式检查这些参数,并且根据不同的参数作出不同的反应。
public void setValue(String name,int value){
if("weight".equals(name)){
_weight = value;
}
if("height".equals(name)){
_height = value;
}
}
=》替换
public void setWeight(int value){
_weight = value;
}
public void setHeight(int value){
_height = value;
}
(4)Preserve whole Object 保持对象完整
有时候将来自同一个对象的若干数据项作为参数,传递给某个函数。这样做的问题,万一新的调用者需要新的数据就的修改原来的函数
int low =dayRang.getLow();
int high =dayRang.getHigh();
widthPlan = plan.widthPlan(low,high);
=》替换
widthPlan = plan.widthPlan(dayRang);
(5) Replace Parameter with Methods 以函数取代参数
对象调用某个函数,并将结果作为传递给某个函数。而接受该参数的函数也有能力调用这个前一个函数
int basePrice = _quantity*_itemPrice;
disCountLevl = getDisCountLevl();
double findPrice = disCountPrice(basePrice, disCountLevl);
=》替换
int basePrice = _quantity*_itemPrice;
double findPrice = disCountPrice(basePrice);
对getDisCountLevl()的调用在disCountPrice函数里进行。
(6) Introduce Parameter Object (引入参数对象)
某些参数总是同时出现 比如startDate endDate 以一对值表示一个范围。我们称之为数据泥团Data Clump。把这些参数组织在一起
double getFlowBetween(Date startDate,Date endDate){
.....
}
=》替换
public class DateRang{
private final Date startDate;//设置为final是只能是构造函数才能初始化
private final Date endDate;
public DateRang(Date startDate,Date endDate){
this.startDate = startDate;
this.endDate = endDate;
}
public Date getStartDate(){
return startDate;
}
public Date getEndDate(){
return endDate;
}
}
double getFlowBetween(DateRang dateRang){
.....
}
(7)Replace Error Code with Exception 以异常取代错误代码
某个函数返回一个错误的编码 -1 以表示返某种错误的情况。当一个错误发生时,并不一定要知道如何处理,只需要让调用者知道这个错误。而调用者可能将这个
错误沿着调用链(call chain)传递下去。
int withdraw(int amount){
if(amount > _blance){
return -1;
} else {
_blance -= amount;
return 0;
}
}
=》替换
void withdraw(int amount) throws BalanceException{
if(amount > _blance){
throw new BalanceException();
} else {
_blance -= amount;
return 0;
}
}
(8) Replace Exception with Test 以测试取代异常
面对一个调用者可以预先加一个测试条件,如果你抛出一个异常。而这个异常对你没有任何意义。
double getValueForPeriod(int periodIndex){
try{
return _varArray[periodIndex];
}catch(ArrayIndexOutOfBoundException ex){
return 0;
}
}
=》替换
double getValueForPeriod(int periodIndex){
if(periodIndex >=_varArray.length){ return 0; }
return _varArray[periodIndex];
}
总结:异常只用来处理异常、罕见的行为,也就是那些产生意料子外的行为,而不能做为条件检查的替代品。
(8) Replace Constructor with Factory Method 以工厂函数取代构造函数
332页
(六):处理概括关系
处理概括关系主要是将函数上下移动于继承体系之中。
(1) pull up method 函数上移
有写函数,在各个subclass中产生完全相同的结果或者函数体完全相同
比如在两个subclass中都有createBill()函数
public void createBill(Date date){
double chargeAmount = chargeFor(Date startDate, Date endDate);
addBill();
}
因为chargeFor()函数在每个子类中不同的结果,所以需要在superclass中
public abstract double chargeFor(Date startDate, Date endDate);
把createBill() pull up到supclass中
(2) pull up Constructor body 构造函数上移
你在subclass中拥有一些构造函数,它们的本体代码几乎完全相同
此时在superclass中新建一个构造函数,并在subclass构造函数中调用它
class Manager extends Employee{
pubic Manager(String name, String id, String grade){
this.name = name;
this.id = id;
this.grade = grade;
}
}
替换=》
public class Employee{
protected String name;
protected String id;
public Employee(String name,String id){
this.name = name;
this.id = id;
}
}
class Manager extends Employee{
private String grade;
public Employee(String name, String id, String grade){
super(name,id)
this.grade = grade;
}
}
(3) Replace Inheritance with Delegation 以委托取代继承(适配器模式的雏形)
某个subclass 只使用了subperclass接口中的一部分,或者根本就不需要继承而来的数据。
做法:在subclass中增加一个值域用以保存superclass 调整subclass函数,令它该为委托superclass,然后去掉两者之间的继承关系
比如Vector 的一个子类Statck Statck的两个方法size(),isEmpty()是从Vector继承过来的,其它两个方法push(),pop(),是Statck需要自己声明的
class MyStatck extends Vector{
public void push(Object obj){
insertElementAt(obj,0);
}
public Object pop(){
Object result = firstElement();
removeElementAt(0);
return result;
}
}
替换=》
class MyStatck extends Vector{
Vector vector = new Vector();
public void pop(Object obj){
vector.insertElementAt(obj,0);
}
public Object pop(){
Object result = vector.firstElement();
vector.removeElementAt(0);
return result;
}
//这两个函数是
public int size(){
return vector.size();
}
public boolean isEmpty(){
return vector.isEmpty();
}
}
(4) Replace Delegation with Inheritance以继承取代委托
这个正好和上面Replace Inheritance with Delegation相反,如果发现自己需要使用所托类的所有函数或大部分函数,并且花费很大力气去编写极简的请托函数。