31天重構之我的粗淺見解

其實不少模式,咱們平時就已經用到,只是不知道而已。html

 

1.集合的封裝java

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.提高方法ide

簡單點說,若是子類都有相同的方法,那就應該將方法提上到父類層函數

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.提高字段

同提高方法,思路同樣的,就很少說了ui

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.切換到策略模式

其實就是用多態減小 if-else-if,是一個值得記住的方法,但要注意抽出總體的邏輯。

不少時候,要完成目標的方式不是隻有一種,當咱們須要使用不一樣的條件,來獲取不一樣的結果的時候,咱們可使用策略模式,這樣,不會由於新增長一個條件,而去修改判斷邏輯

 

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 ..}相似這樣嵌套判斷,能夠有一些重構的技巧

去除 if-else 重複嵌套,通用的一個作法是判斷一次,return一次,很是有用。

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");
    }
}
相關文章
相關標籤/搜索