6.分解依赖
分解依赖:即A类对B类的直接调用变成A类调用C接口,C接口底层调用了B类对象。
总结:使用中间的装饰接口来分解两个类之间的依赖,对类进行装饰,然后使它满足我们所需要的功能。
eg:
//A类: 直接调用B类对象的方法
class AnimalService
{
private boolean isHungry;
public void feeding()
{
if(isHungry)
Feeder.feed();
}
}
//B类:
class Feeder
{
public static void feed()
{
System.out.println("动物喂食中。。。");
}
}
重构:
//A类:调用C接口,C接口底层是靠C接口的实现类调用B类的方法
class AnimalService
{
private boolean isHungry;
private ImpFeedServie feedService;
public AnimalService(ImpFeedServie feedServie)
{
this.feedService=feedServie;
}
public void feeding()
{
if(isHungry)
feedService.feed();
}
}
//C接口:
interface ImpFeedServie
{
void feed();
}
//C接口实现类:调用B类的方法
class FeedService implements ImpFeedServie
{
@Override
public void feed() {
// TODO Auto-generated method stub
Feeder.feed();
}
}
//B类:
class Feeder
{
public static void feed()
{
System.out.println("动物喂食中。。。");
}
}
7.提取方法对象
提取方法对象:指当你发现一个方法中存在过多的局部变量时,你可以通过使用“提取方法对象”重构其实就是将自身传递给另一个类(自身数据传递给另一个类的过程),由该类帮助自己实现功能。
eg:如下代码所示,Order 类中的Calculate方法要完成很多功能,在之前我们用“提取方法”来进行重构,现在我们采取“提取方法对象”来完成重构。
public class OrderLineItem
{
public decimal Price { get; private set; }
}
public class Order
{
private IList<OrderLineItem> OrderLineItems { get; set; }
private IList<decimal> Discounts { get; set; }
private decimal Tax { get; set; }
public decimal Calculate()
{
decimal subTotal = 0m;
// Total up line items
foreach (OrderLineItem lineItem in OrderLineItems)
{
subTotal += lineItem.Price;
}
// Subtract Discounts
foreach (decimal discount in Discounts)
subTotal -= discount;
// Calculate Tax
decimal tax = subTotal * Tax;
// Calculate GrandTotal
decimal grandTotal = subTotal + tax;
return grandTotal;
}
}
重构:我们引入了OrderCalculator类,该类实现了所有的计算方法,Order类将自身传递给 OrderCalculator类并调用Calculate方法完成计算过程。
public class OrderLineItem
{
public decimal Price { get; private set; }
}
public class Order
{
public IEnumerable<OrderLineItem> OrderLineItems { get; private set; }
public IEnumerable<decimal> Discounts { get; private set; }
public decimal Tax { get; private set; }
public decimal Calculate()
{
return new OrderCalculator(this).Calculate();
}
}
public class OrderCalculator
{
private decimal SubTotal { get; set; }
private IEnumerable<OrderLineItem> OrderLineItems { get; set; }
private IEnumerable<decimal> Discounts { get; set; }
private decimal Tax { get; set; }
public OrderCalculator(Order order)
{
OrderLineItems = order.OrderLineItems;
Discounts = order.Discounts;
Tax = order.Tax;
}
public decimal Calculate()
{
CalculateSubTotal();
SubtractDiscounts();
CalculateTax();
return SubTotal;
}
private void CalculateSubTotal()
{
// Total up line items
foreach (OrderLineItem lineItem in OrderLineItems)
SubTotal += lineItem.Price;
}
private void SubtractDiscounts()
{
// Subtract Discounts
foreach (decimal discount in Discounts)
SubTotal -= discount;
}
private void CalculateTax()
{
// Calculate Tax
SubTotal += SubTotal * Tax;
}
}
总结:本文的重构方法在有的时候还是比较有用,但这样会造成字段的增加,同时也会带来一些维护的不便,它和“提取方法”最大的区别就是一个通过方法返回需要的数据,另一个则是通过字段来存储方法的结果值,所以在很大程度上我们都会选择“提取方法”
8.分离职责(搬移方法)
分离职责:当一个类中承载了过多的职责时,需要将部分的职责分离到独立类中,这符合了面向对象的单一职责的原则,每个类尽量负责一个职责。
总结:其实就是将一个过多方法的类中的某个方法搬移到适当的类中去实现。
eg:
class Video
{
private double price;
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
//添加video 到相应的 customer去
public void addVieo(Video video,Customer cust)
{
cust.addVideo(video);
}
//该方法应当搬移到Customer类中
public double payAlls(Customer cust)
{
return cust.list.getSum();
}
}
class Customer
{
IList<Video> list=new MyList<Video>();
public void addVideo (Video video)
{
list.add(video);
}
}
interface IList<T>
{
void add(T t);
double getSum();
}
class MyList<T> implements IList<T>
{
private List<T> list=new ArrayList<T>();
@Override
public void add(T t) {
// TODO Auto-generated method stub
list.add(t);
}
@Override
public double getSum() {
double sum=0;
for (T t:list)
{
if (t instanceof Video)
{
Video v=(Video)t;
sum+=v.getPrice();
}
}
return sum;
}
}
重构:
class Video
{
private double price;
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
//添加video 到相应的 customer去
public void addVieo(Video video,Customer cust)
{
cust.addVideo(video);
}
//该方法应当搬移到Customer类中
/* public double payAlls(Customer cust)
{
return cust.list.getSum();
}*/
}
class Customer
{
IList<Video> list=new MyList<Video>();
public void addVideo (Video video)
{
list.add(video);
}
//将职责搬移到适合的类中
public double payAlls()
{
return this.list.getSum();
}
}
interface IList<T>
{
void add(T t);
double getSum();
}
class MyList<T> implements IList<T>
{
private List<T> list=new ArrayList<T>();
@Override
public void add(T t) {
// TODO Auto-generated method stub
list.add(t);
}
@Override
public double getSum() {
double sum=0;
for (T t:list)
{
if (t instanceof Video)
{
Video v=(Video)t;
sum+=v.getPrice();
}
}
return sum;
}
}
9.提炼重复内容
提炼重复内容:将重复使用的语句提炼出来,封装到一个“模块”上去,让其他地方调用该模块即可。
eg:ArchiveRecord和CloseRecord都会用到Archived = true; 和DateArchived = DateTime.Now; 这两条语句,所以我们就可以对它进行重构。
public class MedicalRecord
{
public DateTime DateArchived { get; private set; }
public bool Archived { get; private set; }
public void ArchiveRecord()
{
Archived = true;
DateArchived = DateTime.Now;
}
public void CloseRecord()
{
Archived = true;
DateArchived = DateTime.Now;
}
}
重构:我们提炼了SwitchToArchived方法来封装公用的操作,然后给ArchiveRecord和CloseRecord统一调用。
public class MedicalRecord
{
public DateTime DateArchived { get; private set; }
public bool Archived { get; private set; }
public void ArchiveRecord()
{
SwitchToArchived();
}
public void CloseRecord()
{
SwitchToArchived();
}
private void SwitchToArchived()
{
Archived = true;
DateArchived = DateTime.Now;
}
}
总结:这个重构很简单,绝大多数程序员都会使用这种重构方法,但有时由于习惯、时间、赶进度等原因而忽略它,所以会使得整个系统杂乱无章,到处都是Ctrl+C和Ctrl+V的痕迹。
10.封装条件
封装条件:当条件关系特别复杂时,此时需要封装条件,如果条件关系不需要参数则可以提取成属性,如果条件关系需要参数则可以提取成方法。
eg:PerformCoolFunction里面的if条件判断比较复杂,看起来有点杂乱,所以就把它提出来。
class RemoteControl
{
private String name;
private int createdYear;
public String performCoolFunction(String buttonPressed)
{
//条件关系涉及了多个参数,可以提取成方法
if(buttonPressed.equals("press") && name.equals("REC") && createdYear<10)
return "doSomething";
return "error";
}
}
重构:条件表达式封装成HasExtraFunctions属性,这样先前的条件判断就成了if (HasExtraFunctions) ,所以这样就在很大程度上提高了可读性。
class RemoteControl
{
private String name;
private int createdYear;
public String performCoolFunction(String buttonPressed)
{
if(hasExtraFuntion(buttonPressed))
return "doSomething";
return "error";
}
//提取成方法
public boolean hasExtraFuntion(String buttonPressed)
{
return (buttonPressed.equals("press") && name.equals("REC") && createdYear<10);
}
}
总结:这个重构在很大程度上能改善代码的可读性,尤其是在一个逻辑很复杂的应用中,把这些条件判断封装成一个有意义的名字,这样很复杂的逻辑也会立刻变得简单起来。