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.集合的封装

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");
    }
}

 

posted @ 2015-11-06 16:18  draem0507  阅读(5034)  评论(0编辑  收藏  举报
View Code