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");
        }
    }
    复制代码
  • 相关阅读:
    C++获取时间函数
    平滑算法:三次样条插值(Cubic Spline Interpolation)
    为什么想要交谈?
    c++日常小问题
    看板娘
    世界碰撞算法原理和总结(sat gjk)
    转载c++默认初始化文章--google翻译
    从4行代码看右值引用(转载 《程序员》2015年1月刊)
    c++模板特例化 函数模板(非法使用显式模板参数 )
    InverseTransformPoint 函数问题
  • 原文地址:https://www.cnblogs.com/JavaBlackHole/p/7728294.html
Copyright © 2011-2022 走看看