zoukankan      html  css  js  c++  java
  • 31天重构学习笔记(java版本)

     

    http://www.cnblogs.com/draem0507/p/4942939.html#1.集合的封装#undefined    出处

     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");
        }
    }
    复制代码

    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");
        }
    }
    复制代码
  • 相关阅读:
    17. Letter Combinations of a Phone Number
    16. 3Sum Closest
    15. 3Sum
    14. Longest Common Prefix
    13. Roman to Integer
    12. Integer to Roman
    11. Container With Most Water
    10. Regular Expression Matching
    9. Palindrome Number
    8. String to Integer (atoi)
  • 原文地址:https://www.cnblogs.com/JavaBlackHole/p/7728294.html
Copyright © 2011-2022 走看看