• Introduce Null Object


      今天继续总结《重构》这本书中的一个重构手法,Introduce Null Object。写这个手法是因为它确实很巧妙,在实际编程中经常会遇到这种情况,前人总结出来了这么一个经典的手法,当然还有由此手法扩展更普遍更经典的手法--Special Case。

      刚入行的时候,听“老人”给我讲,书是要越读越薄的。当时没什么感受,觉得“老人”在故弄玄虚。工作4年多来,发现看过不少书,烂熟于心的却是最初入门的那一本--郭天祥C51。买的是一本影印盗版书(在大学里嘛,那年大二),不停的翻,书都翻烂了。现在那本书也不知道去哪了,但C51的所有内容都清清楚楚了。学习是为了什么,无非是为了精进,为以后做准备嘛。

      扯远了,接下来半年的时间里,争取有时间就写写读书笔记吧。近几年看了好几本技术书籍,有些还看了好几遍。写下来的内容都是为了把学过的知识捋一遍。

      开始今天的内容吧,这个重构手法在一定的应用场景下很有用。先来看看其应用场景。

      重构前的主体代码:

      1 package com.nelson.io;
      2 
      3 public class Site {
      4     
      5     private Customer _customer;
      6     
      7     public Customer getCustomer()
      8     {
      9         return _customer;
     10     }
     11     
     12     public void setCustomer(Customer cus)
     13     {
     14         _customer = cus;
     15     }
     16     
     17 }
     18 
     19 class Customer
     20 {
     21     String _name;
     22     BillingPlan _billingplan;
     23     PaymentHistory _paymentHistory;
     24     
     25     public String getName()    {return _name;}
     26     
     27     public BillingPlan getPlan() {
     28         return _billingplan;
     29     }
     30     
     31     public PaymentHistory getHistroy(){
     32         return _paymentHistory;
     33     }
     34     
     35     public Customer(){
     36     }
     37     
     38     public Customer(String name,BillingPlan bill,PaymentHistory pay){
     39         _name = name;
     40         _billingplan = bill;
     41         _paymentHistory = pay;
     42     }
     43 }
     44 
     45 class BillingPlan
     46 {
     47     private int basicpay;
     48     private int extrapay;
     49     
     50     public BillingPlan(){
     51         setBasicpay(0);
     52         setExtrapay(0);
     53     }
     54     
     55     public BillingPlan(int pay)
     56     {
     57         setBasicpay(pay);
     58         setExtrapay(0);
     59     }
     60     
     61     public BillingPlan(int basic,int extra)
     62     {
     63         basicpay = basic;
     64         extrapay = extra;
     65     }
     66     
     67     static BillingPlan basic()
     68     {
     69         return new BillingPlan(100);   //最低消费100元
     70     }
     71     
     72     public int getTotalExpand()
     73     {
     74         return basicpay+extrapay;
     75     }
     76 
     77     int getExtrapay() {
     78         return extrapay;
     79     }
     80 
     81     void setExtrapay(int extrapay) {
     82         this.extrapay = extrapay;
     83     }
     84 
     85     int getBasicpay() {
     86         return basicpay;
     87     }
     88 
     89     void setBasicpay(int basicpay) {
     90         this.basicpay = basicpay;
     91     }
     92 }
     93 
     94 class PaymentHistory
     95 {
     96     public int getWeeksDelinquentInLastYear;
     97     
     98     public PaymentHistory()
     99     {
    100         getWeeksDelinquentInLastYear = 0;
    101     }
    102 }
    View Code

      重构前的应用代码:

     1 package com.nelson.io;
     2 
     3 public class HelloWorld {
     4 
     5     public static void main(String[] args) {
     6         
     7         System.out.println("Hello Java!");
     8 
     9         //不正常的客户
    10         Site site = new Site();             //这样默认Site中的_customer = null
    11         Customer cus1 = site.getCustomer();
    12         
    13         String strName;
    14         if(cus1 == null) strName = "occupant";   //顾客名字暂时叫做occupant
    15         else strName = cus1.getName();   //获取用户的名字
    16         System.out.println("Current Customer1: "+strName);
    17         
    18         BillingPlan plan1;
    19         if(cus1 == null) plan1 = BillingPlan.basic();
    20         else plan1 = cus1.getPlan();
    21         System.out.println("Total Expand:"+plan1.getTotalExpand());
    22         
    23         //////////////////////////////////////////////////////////
    24         //正常的客户
    25         BillingPlan plan2 = new BillingPlan(100,19);
    26         PaymentHistory history2 = new PaymentHistory();
    27         Customer cus= new Customer("xiaoming",plan2,history2);
    28         site.setCustomer(cus);
    29         
    30         Customer cus2 = site.getCustomer();
    31         if(cus2 == null) strName = "occupant";      //顾客名字暂时叫做occupant
    32         else strName = cus2.getName();              //获取用户的名字
    33         System.out.println("Current Customer2: "+strName);
    34         
    35         if(cus2 == null) plan1 = BillingPlan.basic();
    36         else plan1 = cus2.getPlan();
    37         System.out.println("Total Expand:"+plan1.getTotalExpand());
    38         
    39     }
    40 
    41 }
    View Code

           在这里的重构要解决的问题是,应用代码中会不断的查询Site中的customer对象是否为空,这个应用的关键也在于允许customer=null的情况(这种情况存在不算错,而且必须应对这种情况)。Introduce Null Object手法就是去掉这里的重复查询是否为空的代码。

       重构后的主体代码:

      1 package com.nelson.io;
      2 
      3 public class Site {
      4     
      5     private Customer _customer;
      6     
      7     public Customer getCustomer()
      8     {
      9         return (_customer==null)?Customer.newNull():_customer;
     10     }
     11     
     12     public void setCustomer(Customer cus)
     13     {
     14         _customer = cus;
     15     }
     16     
     17 }
     18 
     19 class Customer implements Nullable
     20 {
     21     String _name;
     22     BillingPlan _billingplan;
     23     PaymentHistory _paymentHistory;
     24     
     25     public String getName()    {return _name;}
     26     
     27     public BillingPlan getPlan() {
     28         return _billingplan;
     29     }
     30     
     31     public PaymentHistory getHistroy(){
     32         return _paymentHistory;
     33     }
     34     
     35     protected Customer(){
     36     }
     37     
     38     public Customer(String name,BillingPlan bill,PaymentHistory pay){
     39         _name = name;
     40         _billingplan = bill;
     41         _paymentHistory = pay;
     42     }
     43     
     44     public static Customer newNull()
     45     {
     46         return new NullCustomer();
     47     }
     48 
     49     public boolean isNull() {
     50         return false;
     51     }
     52 }
     53 
     54 class NullCustomer extends Customer
     55 {
     56     public String getName()    {return "occupant";}
     57     
     58     public BillingPlan getPlan() {
     59         return BillingPlan.basic();
     60     }
     61     
     62     public boolean isNull() {
     63         return true;
     64     }
     65 }
     66 
     67 class BillingPlan
     68 {
     69     private int basicpay;
     70     private int extrapay;
     71     
     72     public BillingPlan(){
     73         setBasicpay(0);
     74         setExtrapay(0);
     75     }
     76     
     77     public BillingPlan(int pay)
     78     {
     79         setBasicpay(pay);
     80         setExtrapay(0);
     81     }
     82     
     83     public BillingPlan(int basic,int extra)
     84     {
     85         basicpay = basic;
     86         extrapay = extra;
     87     }
     88     
     89     static BillingPlan basic()
     90     {
     91         return new BillingPlan(100);   //最低消费100元
     92     }
     93     
     94     public int getTotalExpand()
     95     {
     96         return basicpay+extrapay;
     97     }
     98 
     99     int getExtrapay() {
    100         return extrapay;
    101     }
    102 
    103     void setExtrapay(int extrapay) {
    104         this.extrapay = extrapay;
    105     }
    106 
    107     int getBasicpay() {
    108         return basicpay;
    109     }
    110 
    111     void setBasicpay(int basicpay) {
    112         this.basicpay = basicpay;
    113     }
    114 }
    115 
    116 class PaymentHistory
    117 {
    118     public int getWeeksDelinquentInLastYear;
    119     
    120     public PaymentHistory()
    121     {
    122         getWeeksDelinquentInLastYear = 0;
    123     }
    124 }
    125 
    126 interface Nullable
    127 {
    128     boolean isNull();
    129 }
    View Code

      重构后的测试代码:

     1 package com.nelson.io;
     2 
     3 public class HelloWorld {
     4 
     5     public static void main(String[] args) {
     6         
     7         System.out.println("Hello Java!");
     8 
     9         //////////////////////////////////////////////////////////////////
    10         //不正常的客户
    11         Site site = new Site();            //这样默认Site中的_customer = null
    12         Customer cus1 = site.getCustomer();
    13         String strName = cus1.getName();   //获取用户的名字
    14         System.out.println("Current Customer1: "+strName);
    15         BillingPlan plan1 = cus1.getPlan();
    16         System.out.println("Total Expand:"+plan1.getTotalExpand());
    17         
    18         if(!cus1.isNull()) 
    19             System.out.println("Customer1 is not null customer");
    20         else
    21             System.out.println("Customer1 is null customer");
    22         
    23         System.out.println();
    24         
    25         //////////////////////////////////////////////////////////
    26         //正常的客户
    27         BillingPlan plan2 = new BillingPlan(100,19);
    28         PaymentHistory history2 = new PaymentHistory();
    29         Customer cus= new Customer("xiaoming",plan2,history2);
    30         site.setCustomer(cus);
    31         
    32         Customer cus2 = site.getCustomer();
    33         strName = cus2.getName();              //获取用户的名字
    34         System.out.println("Current Customer2: "+strName);
    35         cus2.getPlan();
    36         System.out.println("Total Expand:"+plan1.getTotalExpand());
    37         
    38         if(!cus2.isNull()) 
    39             System.out.println("Customer2 is not null customer");
    40         else
    41             System.out.println("Customer2 is null customer");
    42     }
    43 
    44 }
    View Code

      重构后的测试代码中,减少了对customer是否为null的判断。当测试代码中出现很多这样的判断时,此项重构价值得以体现。而且这样的好处还有,如果程序中碰到一个对象为null,不加判断去调用对象的函数,将造成程序崩溃。而将null的情况封装为Null Object后,将永不会出现这种异常。程序运行更安全。测试代码中的调用方只管使用方法就好了,保证不出错。注意Customer和NullCustomer中都实现了Nullable接口,意在告诉调用方有NullCustomer类存在。如果调用方实在想知道谁是NullCustomer谁是Customer,通过接口函数就可以知道了。

      引申的Special Case模式讲的就是特例模式,当SIte中的Customer = null时也算一种特例,当Customer中的name = “”时也是一种特例,也可以定义UnknownCustomer。或者种种其他的特殊情况--程序运行过程中大部分情况都是正常的Customer,但偶尔出现NullCustomer或者UnknownCustomer,Null Object和Special Case 保证程序能处理“极端”情况。

  • 相关阅读:
    2 浮点数
    1 有符号与无符号
    4.变量和不同的赋值方式
    文档格式标准
    常用mysql命令
    一个简单的jsp+servlet实例,实现简单的登录
    容量调度器与公平调度器区别
    CListCtrl用法总结(二)
    CListCtrl 实现排序功能(数字和字母)
    CString 转换成 char *
  • 原文地址:https://www.cnblogs.com/kanite/p/7679261.html
Copyright © 2020-2023  润新知