31天重构学习笔记(java版本)
准备下周分享会的内容,无意间看到.net版本的重构31天,花了两个小时看了下,可以看成是Martin Fowler《重构》的精简版
原文地址:http://www.lostechies.com/blogs/sean_chambers/archive/2009/07/31/31-days-of-refactoring.aspx
原文代码地址:https://github.com/schambers/days-of-refactoring
圣殿骑士写的.net版本的读书笔记地址:http://www.cnblogs.com/KnightsWarrior/p/31DaysOfRefactoring.html
有百度文库的可以直接下载:http://wenku.baidu.com/view/27423a5c3b3567ec102d8a0c.html?pn=51
抽个时间(就这周六吧,不过得先把分享会的PPT写好 囧),把java版本的写下,先mark下
周六总算是写完了,不过还想是多跨了一周 囧。有时间,在加下描点,还有pdf下载吧。累,回家咯 ^^ --20151114
1.集合的封装
/** * @title 封装集合对象,不要暴露太多方法给外部访问内部数据 * @desc * @atuh lwx * @createtime on 2015/11/12 23:50 */ public class Day_1 { public static void main(String[] args) { Day1Test day1Test = new Day1Test(); //获取到了内部对象 List<String> list = day1Test.getList(); //肆无忌惮的操作 list.add("a"); day1Test.iterator(); //正确的做法 Day1Test2 day1Test2 = new Day1Test2(); //获取到了内部对象 List<String> list2 = day1Test2.getList(); //肆无忌惮的操作 list2.add("a"); day1Test2.iterator(); } static class Day1Test { private List<String> list = new ArrayList<String>(); public List getList() { return list; } //模拟不暴露给外部 protected void add(String value) { list.add(value); } protected void remove(String value) { list.remove(value); } public void iterator() { for (String str : list) { System.out.println(str); } } } static class Day1Test2 { private List<String> list = new ArrayList<String>(); public List getList() { return new ArrayList(list); } //模拟不暴露给外部 protected void add(String value) { list.add(value); } protected void remove(String value) { list.remove(value); } public void iterator() { for (String str : list) { System.out.println(str); } } } }
2.移动方法
Move method does exactly what it sounds like, move a method to a better location(移动方法到更合适的位置)
public class Day_2 { public static void main(String[] args) { } } class BankAccount1 { public BankAccount1(int accountAge, int creditScore, AccountInterest1 accountInterest) { AccountAge = accountAge; CreditScore = creditScore; AccountInterest1 = accountInterest; } public int AccountAge ; public int CreditScore; public AccountInterest1 AccountInterest1 ; } class AccountInterest1 { public BankAccount1 Account ; public AccountInterest1(BankAccount1 account) { Account = account; } public double InterestRate() { return CalculateInterestRate(); } public boolean IntroductoryRate() { return CalculateInterestRate() < 0.05; } public double CalculateInterestRate() { if (Account.CreditScore > 800) return 0.02; if (Account.AccountAge > 10) return 0.03; return 0.05; } } class BankAccount { public BankAccount(int accountAge, int creditScore, AccountInterest accountInterest) { AccountAge = accountAge; CreditScore = creditScore; AccountInterest = accountInterest; } public int AccountAge; public int CreditScore; public AccountInterest AccountInterest; //这个方法跟BankAccount没有直接关系 public double CalculateInterestRate() { if (CreditScore > 800) return 0.02; if (AccountAge > 10) return 0.03; return 0.05; } } class AccountInterest { public BankAccount Account; public AccountInterest(BankAccount account) { Account = account; } public double InterestRate() { return Account.CalculateInterestRate(); } public boolean IntroductoryRate() { { return Account.CalculateInterestRate() < 0.05; } } }
3.提升方法
简单点说,如果子类都有相同的方法,那就应该将方法提上到父类层
abstract class Vehicle { // other methods } class Car extends Vehicle { public void Turn(String str) { // code here } } public class Motorcycle extends Vehicle { public void Turn(String str) { // code here } }
提升后的结构
abstract class Vehicle1 { public void Turn(String str) { // code here } } class Car1 extends Vehicle1 { } public class Motorcycle1 extends Vehicle1 { }
4.下移方法
与第三个上升方法相比,有时候,父类的方法,随着业务的变化,只适合部分子类的时候,则需要将父类的方法下移到具体需要的子类中,这样才符合接口最小原则^^
abstract class Animal { //狗吠 public void Bark() { // code to bark } } class Dog extends Animal { } class Cat extends Animal { }
正常小猫是不会狗吠的,当然,有些接口可能当初定义的时候,有些子类还未出现,因此不会有这样的问题。随着业务的增加,这样的问题出现了,那么,我们就要及时的将接口下移
abstract class Animal1 { } class Dog1 extends Animal1 { //狗吠 public void Bark() { // code to bark } } class Cat1 extends Animal1 { }
5.提升字段
同提升方法,思路一样的,就不多说了
abstract class Account { } public class CheckingAccount extends Account { private int _minimumCheckingBalance = 5; } public class SavingsAccount extends Account { private int _minimumSavingsBalance = 5; }
上升后的结构
abstract class Account1 { protected int _minimumCheckingBalance = 5; } public class CheckingAccount1 extends Account1 { } public class SavingsAccount1 extends Account1 { }
6.下移字段
abstract class Task { protected String _resolution; } public class BugTask extends Task { } public class FeatureTask extends Task { }
改造后的情况
abstract class Task1 { } class BugTask1 extends Task1 { protected String _resolution; } class FeatureTask1 extends Task1 { }
7.重命名(类、方法、参数)
demo就不上,只提一点,命名规则不要担心太长,而选择简写,这样反而为后期的维护带来麻烦。
8.使用委托代替继承
设计模式中,很多模式就使用了委托的方式,来解耦继承带来的强依赖,比如装饰者,适配器模式等等。
class Sanitation { public String WashHands() { return "Cleaned!"; } } public class Child extends Sanitation { }
正确的做法
class Sanitation1 { public String WashHands() { return "Cleaned!"; } } class Child1 { private Sanitation1 Sanitation; public Child1() { Sanitation = new Sanitation1(); } public String WashHands() { return Sanitation.WashHands(); } }
上述其实就是代理者模式的框架思路了,如果把Sanitation1暴露出来,就是装饰者了。
9.提取接口
官方已经找不到这个页面的链接了,参考了其他地方,做法其实也很简单,就是遵循了接口最小原则来设计的
interface Bird { public void eat(); public void fly(); //我们假设有的鸟是不会唱歌的 public void song(); }
重新设计后
interface Bird1 { public void eat(); public void fly(); } interface SongBird extends Bird1 { //我们假设有的鸟是不会唱歌的 public void song(); }
10.提取方法
提取方法是重构中很常见到的一种手法。他可以通过方法名,增加代码的可读性,减少不必要的注释说明。
class Receipt { private List<Float> discounts; private List<Float> itemTotals; public float CalculateGrandTotal() { float subTotal = 0f; for (Float itemTotal : itemTotals) subTotal += itemTotal; if (discounts.size() > 0) { for (Float discount : discounts) subTotal -= discount; } float tax = subTotal * 0.065f; subTotal += tax; return subTotal; } }
使用分离方法后的结构
class Receipt1 { private List<Float> discounts; private List<Float> itemTotals; public float CalculateGrandTotal() { float subTotal = 0f; subTotal=addItemTotals(itemTotals); subTotal=minuteDiscounts(itemTotals); subTotal=calcTax(subTotal); return subTotal; } float addItemTotals(List<Float> itemTotals){ float subTotal = 0f; for (Float itemTotal : itemTotals) { subTotal += itemTotal; } return subTotal; } float minuteDiscounts(List<Float> discounts){ float subTotal = 0f; if (discounts.size() > 0) { for (Float discount : discounts) subTotal -= discount; } return subTotal; } float calcTax( float subTotal){ float tax = subTotal * 0.065f; subTotal += tax; return subTotal; } }
11.切换到策略模式
很多时候,要完成目标的方式不是只有一种,当我们需要使用不同的条件,来获取不同的结果的时候,我们可以使用策略模式,这样,不会因为新增加一个条件,而去修改判断逻辑
public class ClientCode { public int CalculateShipping() { ShippingInfo shippingInfo = new ShippingInfo(); return shippingInfo.CalculateShippingAmount(State.Alaska); } } public enum State { Alaska, NewYork, Florida; } public class ShippingInfo { public int CalculateShippingAmount(State shipToState) { if (shipToState == State.Alaska) { return GetAlaskaShippingAmount(); } else if (shipToState == State.NewYork) { return GetNewYorkShippingAmount(); } else if (shipToState == State.Florida) { return GetFloridaShippingAmount(); } else return 0; } } private int GetAlaskaShippingAmount() { return 15; } private int GetNewYorkShippingAmount() { return 10; } private int GetFloridaShippingAmount() { return 3; }
如果判断条件足够简单,上述做法,其实是可以容忍的,但是,如果Getxx方法变的足够复杂的时候,考虑到单一责任原则,一个类的变化,有且只有一个原因引起,这样,每个判断条件方法发生变化,类都必须做出修改,
这样就不合适了。而且使用类封装,可以更好的实现复用。
static class ShippingInfo1{ //模拟一个工厂 private static Map<State,CalculateShippingAmountStrategy>strategyFactory=new HashMap<State, CalculateShippingAmountStrategy>(); static { strategyFactory.put(State.Alaska,new GetAlaskaShippingAmount()); strategyFactory.put(State.NewYork,new GetNewYorkShippingAmount()); strategyFactory.put(State.Florida,new GetFloridaShippingAmount()); } public int CalculateShippingAmount(State shipToState) { return strategyFactory.get(shipToState).calc(); } } interface CalculateShippingAmountStrategy{ public int calc(); } static class GetAlaskaShippingAmount implements CalculateShippingAmountStrategy{ public int calc(){ return 15; } } static class GetNewYorkShippingAmount implements CalculateShippingAmountStrategy{ public int calc(){ return 10; } } static class GetFloridaShippingAmount implements CalculateShippingAmountStrategy{ public int calc(){ return 3; } }
12.解耦依赖
六大设计原则中的最少知识原则(迪米特)说的就是,对依赖的了解,降低到最少。作者强调,当我们进行单元测试的时候,我们就需要一定的隔离,否则无法进行mock.这个自己也是深有体会。
良好的隔离,确实可以让单元测试的Mock变得非常的简单和容易。先看下面的例子,由于AnimalFeedingService直接依赖了静态类Feeder,因此当我们需要只测试FoodBowlEmpty的逻辑判断走向的时候,必然会触发
Feeder的方法,这其实并不是我们想要的。但是又无法直接对静态类进行mock.
public class AnimalFeedingService { private boolean FoodBowlEmpty; public void Feed() { if (FoodBowlEmpty) Feeder.ReplenishFood(); // more code to feed the animal } } public static class Feeder { public static void ReplenishFood() { // fill up bowl } }
解决的办法,就是让Service跟静态的对象解耦
public class AnimalFeedingService1 { public IFeederService FeederService ; public AnimalFeedingService1(IFeederService feederService) { FeederService = feederService; } private boolean FoodBowlEmpty ; public void Feed() { if (FoodBowlEmpty) FeederService.ReplenishFood(); // more code to feed the animal } } public interface IFeederService { void ReplenishFood(); } public class FeederService implements IFeederService { public void ReplenishFood() { Feeder.ReplenishFood(); } }
13.提取方法对象
这并不是一种很常见的重构手段,即当我们对象中定义了很多变量,及其需要利用这些变量进行一些业务操作的时候,可以考虑将方法提取到一个新的类中,这样就解耦了变量与逻辑操作的直接关联。
也比较符合单一责任原则。
public class OrderLineItem { public int Price ; } public class Order { private List<OrderLineItem> OrderLineItems ; private List<Integer> Discounts; private int Tax ; public int Calculate() { int subTotal = 0; // Total up line items for (OrderLineItem lineItem : OrderLineItems) { subTotal += lineItem.Price; } // Subtract Discounts for (int discount : Discounts) subTotal -= discount; // Calculate Tax int tax = subTotal * Tax; // Calculate GrandTotal int grandTotal = subTotal + tax; return grandTotal; } }
咋看,代码并没有什么大的问题,order中定义了很多关于自身的属性,还有对属性的一些业务操作,但是,计算价格,其实并不是order对象本身应该关系的。因此,需要引入一个计算order price能力的类
public class Order1 { private List<OrderLineItem> OrderLineItems ; private List<Integer> Discounts; private int Tax ; public int Calculate(){ return new OrderCalculator(this).Calculate(); } } public class OrderCalculator{ private Order1 order; private List<OrderLineItem> OrderLineItems ; private List<Integer> Discounts; private int Tax ; public OrderCalculator(Order1 order){ this.order=order; } public int Calculate() { int subTotal = 0; // Total up line items for (OrderLineItem lineItem : OrderLineItems) { subTotal += lineItem.Price; } // Subtract Discounts for (int discount : Discounts) subTotal -= discount; // Calculate Tax int tax = subTotal * Tax; // Calculate GrandTotal int grandTotal = subTotal + tax; return grandTotal; } }
14.单一责任
上面的问题,其实一直提到设计原则,自然也提到了单一责任原则SRP,要学重构,SRP是必然要知道,且学会的思想,并且灵活应用到重构代码中。
下面作者举了一个Video的例子,Video类中有两个方法,分别负责统计客户购买的Video数量,并且计算每个客户的购买金额
public class Video { public void PayFee(int fee) { } public void RentVideo(Video video, Customer customer) { customer.Videos.add(video); } public int CalculateBalance(Customer customer) { return customer.LateFees.size(); } } public class Customer { public List<Integer> LateFees; public List<Video> Videos ; }
很明显,顾客购买Video的金额,并不是Video本身应该关系的,而是每个Customer应该关系的,因此,需要将计算购买金额的方法下移到Customer类中来完成
public class Video1 { public void RentVideo(Video1 video, Customer1 customer) { customer.Videos.add(video); } } public class Customer1 { public List<Integer> LateFees; public List<Video1> Videos ; public void PayFee(int fee) { } public int CalculateBalance(Customer1 customer) { return customer.LateFees.size(); } }
15.移除拷贝
当我们有两段一样的代码的时候,很明显,我们需要对他进行简单的封装(具体如何处理,这里先不说,技巧很多种),让重复的代码彻底消息掉。这个可能也是重构最简单,也是最好用的一种方式了
public class MedicalRecord { public Date DateArchived ; public boolean Archived; public void ArchiveRecord() { Archived = true; DateArchived = new Date(); } public void CloseRecord() { Archived = true; DateArchived = new Date(); } }
我们模拟了一段在两个方法中都存在相同逻辑的代码,这时候,我们就要对他进行重构了
public class MedicalRecord1 { public Date DateArchived ; public boolean Archived; public void ArchiveRecord() { init(); } public void CloseRecord() { init(); } public void init() { Archived = true; DateArchived = new Date(); } }
16.封装条件
简单来说,就是对复杂的条件逻辑判断,进行单独处理,这样,当条件参数发生变化的时候,不会影响到真实的业务逻辑流程
public class RemoteControl { private String[] Functions; private String Name; private int CreatedYear; public String PerformCoolFunction(String buttonPressed) { // Determine if we are controlling some extra function // that requires special conditions if (Functions.length > 1 && Name == "RCA" && CreatedYear > new Date().getYear() - 2) { return "doSomething"; } return ""; } }
如何处理呢
public class RemoteControl2 { private String[] Functions; private String Name; private int CreatedYear; public String PerformCoolFunction(String buttonPressed) { // Determine if we are controlling some extra function // that requires special conditions if (HasExtraFunctions()) { return "doSomething"; } return ""; } private boolean HasExtraFunctions() { return Functions.length > 1 && Name == "RCA" && CreatedYear > new Date().getYear() - 2 ; } }
17.提取父类
如何理解呢?简单来说,就是当我们发现定义的方法,可以被抽象成更高层次对象的时候,就需要考虑抽象一个更上层的父类,并将接口迁移到父类中去定义
public class Dog { public void EatFood() { // eat some food } public void Groom() { // perform grooming } }
重构后的效果
public class Animal { public void EatFood() { // eat some food } public void Groom() { // perform grooming } } public class Dog1 extends Animal { }
但是需要注意,过多的继承容易引起耦合,所以有时候,我们需要考虑接口或则聚合来解决继承带来的强依赖。
18.条件判断代替异常
这个其实在很多语言规则中,都有提到,就是不能使用异常来代替控制逻辑,比如《effective java》一书中就有提到。
public class Microwave { public boolean Start() { boolean foodCooked = false; try { //do something perhaps throw new exception foodCooked = true; } catch (Exception e) { foodCooked = false; } return foodCooked; } } }
重构后的效果
public class Microwave1 { public boolean Start() { boolean foodCooked = false; //mock 模拟先判断是否满足某种条件,避免异常发生 if(true){ //do something foodCooked = true; }else { foodCooked = false; } return foodCooked; } }
19.拓展工厂类
将创建对象的过程给封装起来,这就是工厂模式的设计初衷。将一些列有关系的产品簇组合成一个最终的产品,便是抽象工厂了。好像讲偏了,回归正题,使用工厂模式,从重构角度来看,就是为了实现单一职责,使得
代码更加稳定。
public class PoliceCarController { public PoliceCar New(int mileage, boolean serviceRequired) { PoliceCar policeCar = new PoliceCar(); policeCar.ServiceRequired = serviceRequired; policeCar.Mileage = mileage; return policeCar; } } class PoliceCar{ public boolean ServiceRequired; public int Mileage; }
重构后的效果
public interface IPoliceCarFactory { PoliceCar Create(int mileage, boolean serviceRequired); } public class PoliceCarFactory implements IPoliceCarFactory { public PoliceCar Create(int mileage, boolean serviceRequired) { PoliceCar policeCar = new PoliceCar(); policeCar.ServiceRequired = serviceRequired; policeCar.Mileage = mileage; return policeCar; } } public class PoliceCarController1 { public IPoliceCarFactory PoliceCarFactory ; public PoliceCarController1(IPoliceCarFactory policeCarFactory) { PoliceCarFactory = policeCarFactory; } public PoliceCar New(int mileage, boolean serviceRequired) { return PoliceCarFactory.Create(mileage, serviceRequired); } }
20.提取子类
这个方式,之前好像已经提到的下移方法类似,也是为了遵循接口隔离原则。
public interface Ball { public void play(); public void size(); //打气 public void pumpUp(); }
球,可以用来玩,也都有他们的大小,但是不是每种球,都需要打球的pumpUp
因此需要将pumpUp方法下移到具体子类中
public interface BasketBall extends Ball2{ //打气 public void pumpUp(); } public interface Ball2 { public void play(); public void size(); }
21合并集成
//将子类的方法迁移到父类中 不多说了,我想静静
public abstract class Website { public abstract String Title(); } public abstract class StudentWebsite extends Website { public abstract boolean IsActive() ; }
改造后的结构
public abstract class Website2 { public abstract String Title(); public abstract boolean IsActive() ; } public abstract class StudentWebsite2 extends Website { }
虽然感觉跟上移方法很像,但是确实在职责区分中,一定需要判断好,方法到底归属于父类还是子类。
22.分解方法
是不是想到了"提取方法"了,omg。果然够2,我只贴代码,不说话 orz
public class CashRegister { public CashRegister() { Tax = 0.06f; } private float Tax ; public void AcceptPayment(Customer customer, List<Product> products, int payment) { float subTotal = 0f; for (Product product : products) { subTotal += product.Price; } for (Product product : products) { subTotal -= product.AvailableDiscounts; } float grandTotal = subTotal * Tax; customer.DeductFromAccountBalance(grandTotal); } } public class Customer { public void DeductFromAccountBalance(float amount) { // deduct from balance } } public class Product { public int Price ; public int AvailableDiscounts ; }
方法封装后的结构
public class CashRegister2 { public CashRegister2() { Tax = 0.06f; } private float Tax ; private List<Product> Products; public void AcceptPayment(Customer customer, List<Product> products, int payment) { int subTotal = CalculateSubtotal(); subTotal = SubtractDiscounts(subTotal); float grandTotal = AddTax(subTotal); SubtractFromCustomerBalance(customer, grandTotal); } private void SubtractFromCustomerBalance(Customer customer, float grandTotal) { customer.DeductFromAccountBalance(grandTotal); } private float AddTax(int subTotal) { return subTotal * Tax; } private int SubtractDiscounts(int subTotal) { for (Product product : Products) { subTotal -= product.AvailableDiscounts; } return subTotal; } private int CalculateSubtotal() { int subTotal = 0; for (Product product : Products) { subTotal += product.Price; } return subTotal; } }
23.引入参数对象
此重构模式非常的好用,也非常容易上手,重点推荐,下面代码中,可以比较下
public void test(boolean check, String str, int order) { //todo } public void test(Argument argument) { //todo } class Argument { boolean check; String str; int order; }
24.分解复杂判断
原意是移除箭头模式,简言之,即对于复杂的逻辑判断if else{if else ..}类似这样嵌套判断,可以有一些重构的技巧
public class Security { public List list; public Security(List list) { this.list = list; } public boolean HasAccess(Date date, String []arrs, List<String> exemptions) { boolean hasPermission = false; if (date != null) { if (arrs != null) { if (arrs.length == 0) { if (null!=exemptions&&exemptions.get(0).equals("abc")) { hasPermission = true; } } } } return hasPermission; } }
如何重构呢,比较通用的一个做法是判断一次,return一次
public boolean HasAccess2(Date date, String[] arrs, List<String> exemptions) { boolean hasPermission = false; if (date == null||arrs==null) { return false; } if(arrs.length!=0){ return false; } if (null != exemptions && exemptions.get(0).equals("abc")) { return true; } return false; }
最后是stackoverflow上,关于arrowhead pattern的一些建议:http://stackoverflow.com/questions/17804005/how-to-prevent-the-arrowhead-anti-pattern/17813388
25.引入契约检查
Design by contract,即要求我们对输入和输出都进行验证,已保证系统不会因为意想不到的情况出现,而导致程序出现不可以控的情况
先看下面的例子
public class CashRegister { public int TotalOrder(List<String> products, Calendar calendar) { int orderTotal =products.size(); orderTotal+=calendar.get(Calendar.SUNDAY); return orderTotal; } }
采用DBC后的重构效果
public int TotalOrder2(List<String> products, Calendar calendar) { if (products == null) { throw new NullPointerException("products must not be empty"); } if (products.size() == 0) { throw new ArithmeticException("products's size must more than one"); } //calendar校验省略 int orderTotal = products.size(); orderTotal += calendar.get(Calendar.SUNDAY); //输出校验 if (orderTotal == 0) { throw new SecurityException("orderTotal's value must bigger than 0"); } return orderTotal; }
更多关于DBC:https://en.wikipedia.org/wiki/Design_by_contract
26.避免双重否定
没什么好说的,直接上代码吧。
/** * @title 避免双重否定 * @desc * @atuh lwx * @createtime on 2015/11/14 16:27 */ public class Day_26 { static boolean isEmpty(String str){ if(null==str||str.length()==0){ return true; } return false; } static boolean isNotEmpty(String str){ return !isEmpty(str); } public static void main(String[] args) { if(!isEmpty("")){ //todo } // if(isNotEmpty("")){ } } }
27.移除上帝类
如何理解所谓的上帝类呢,说白了,就是一些“功能强大的工具/管理类”,他可能庞大到整个业务系统只会有一个的工具类,这样就违反了单一责任原则。
public class CustomerService { public int CalculateOrderDiscount(String str) { // do work return 0; } public boolean CustomerIsValid(String str) { // do work return true; } public List<String> GatherOrderErrors() { // do work return null; } public void Register(Object customer) { // do work } public void ForgotPassword(Object customer) { // do work } }
职责明确后的结构
public class CustomerService2 { public int CalculateOrderDiscount(String str) { // do work return 0; } public boolean CustomerIsValid(String str) { // do work return true; } public List<String> GatherOrderErrors() { // do work return null; } } public class CustomerRegistrationService{ public void Register(Object customer) { // do work } public void ForgotPassword(Object customer) { // do work } }
28.重命名布尔类型方法
如果有Boolean类型参数,则为了简化外部调用带来的困难,一般会使用重命名方法来简化调用带来的困难,当然,也可以通过重载来弱化boolean变量在使用中带来的不变
public class BankAccount { public void CreateAccount( Object customer,boolean withChecking, boolean withSavings) { // do work } }
改造后的结果
public class BankAccount2 { public void CreateAccountWithChecking(Object customer) { CreateAccount(customer, true, false); } public void CreateAccountWithCheckingAndSavings(Object customer) { CreateAccount(customer, true, true); } private void CreateAccount(Object customer, boolean withChecking, boolean withSavings) { // do work } }
29.去除中间人
如何理解去除中间人呢?简单理解,就是当A需要通过B去访问C的时候,并且B除了调用C的方法,不在有任何作用的时候,则B就成了所谓的中间人,就应该被delete掉
public class Consumer { public AccountManager AccountManager; public Consumer(AccountManager accountManager) { AccountManager = accountManager; } public void Get(int id) { Account account = AccountManager.GetAccount(id); } } public class AccountManager { public AccountDataProvider DataProvider; public AccountManager(AccountDataProvider dataProvider) { DataProvider = dataProvider; } public Account GetAccount(int id) { return DataProvider.GetAccount(id); } } public class AccountDataProvider { public Account GetAccount(int id) { // get account return null; } } class Account { }
重构后的效果
public class Consumer2 { public AccountDataProvider AccountDataProvider ; public Consumer2(AccountDataProvider dataProvider) { AccountDataProvider = dataProvider; } public void Get(int id) { Account account = AccountDataProvider.GetAccount(id); } }
这里需要作两点补充:第一,AccountManager当初设计是为了隔离Consumer与AccountProvider,后面可能随着业务形态发生变化,两者可以直接调用的时候,AccountManager对象就失去了意义。
举个简单的例子,我们买电视,都是去超市去买,因为你不可能直接去厂家拿货,如果哪天你的角色变成代理商或则厂家工人了,也许,你就可以内部直接拿货了
第二,有时候,对于两个需要隔离的对象,需要制造一个中间人,来隔离他们。好比,你原先是公司的员工,享受福利,离职后,就不会再有这种福利了。内部的一些东西,你也就接触不到了。
30.尽快返回
return as soon as possible。即对之前的复杂逻辑判断的一个侧面说明了。
public class Order { public Object Customer; public int CalculateOrder(Object customer, List<Object> products, int discounts) { Customer = customer; int orderTotal = 0; if (products.size() > 0) { orderTotal = products.size(); if (discounts > 0) { orderTotal -= discounts; } } return orderTotal; } }
改造后
public class Order2 { public Object Customer; public int CalculateOrder(Object customer, List<Object> products, int discounts) { Customer = customer; int orderTotal = 0; if (products.size() == 0) { return 0; } orderTotal = products.size(); if (discounts > 0) { orderTotal -= discounts; } return orderTotal; } }
31.使用多态代替条件
上面其实也提到了策略模式替换多条件,其实是类似的。如果对java的单双派机制,有更多了解的,可以移步我之前写的一篇文章,java单双派机制理解
/** * @title 使用多态代替条件判断 * @desc * @atuh lwx * @createtime on 2015/11/14 17:41 */ public class Day_31 { public static void main(String[] args) { Day_31 day_31 = new Day_31(); Parent parent = new Parent(); Son son = new Son(); Daughter daughter = new Daughter(); day_31.invokeSay(parent); day_31.invokeSay(son); day_31.invokeSay(daughter); System.out.println("华丽的分割线"); //使用动态方式 day_31.invokeSay2(parent); day_31.invokeSay2(son); day_31.invokeSay2(daughter); //考虑重载解决 -->又涉及到单分派-->通过使用访问者模式来解决 } public void invokeSay(Object parent) { if (parent instanceof Son) { ((Son) parent).say(); } else if (parent instanceof Daughter) { ((Daughter) parent).say(); } else { ((Parent)parent).say(); } } public void invokeSay2(Parent parent) { parent.say(); } } class Parent { public void say() { System.out.println("parent say"); } } class Son extends Parent { public void say() { System.out.println("Son say"); } } class Daughter extends Parent { public void say() { System.out.println("Daughter say"); } }