株式会社エス・スリー・フォー

Refactoring C-code

ここに挙げるリファクタリングの例は、

「リファクタリング」 マーチン・ファウラー トッパン/ISBN4-89471-228-8

に列挙されたJavaによるサンプルの中から、Cにも摘要できるものを選んだものです。(カッコ内の数字は該当ページを示します)
すべてを必ず摘要しなければならないというものではありません。
より簡潔に/単純になると考えられるとき、正しく運用してください。

関数の抽出 (110)

ひとまとめにできるコードの断片がある。
コードの断片を関数にして、それに目的を表すような名前をつける
before
        void printOwing(double amount) {
          printBanner();
          /* 明細の表示 */
          printf("name:%s\n", _name);
          printf("amount:%f\n", amount);
        }
      
after
        void printOwing(double amount) {
          printBanner();
          printDetails(amount);
        }
      

関数のインライン化 (117)

関数の本体が、名前をつけて呼ぶまでもなく明らかである。
関数本体をコール元にインライン化して、関数を除去する。
before
        int getRating() {
          return (moreThanFiveLateDeliveries()) ? 2 : 1;
        }
        
        int moreThanFiveLateDeliveries() {
          return _numberOfLateDeliveries > 5;
        }
      
after
        int getRating() {
          return (_numberOfLateDeliveries > 5) ? 2 : 1;
        }
      

一時変数のインライン化 (119)

簡単な式によって一度だけ代入される一時変数があり、それが他のリファクタリングの障害となっている。
その一時変数への参照をすべて式で置き換える
before
        double basePrice;
        basePrice = order_basePrice(anOrder);
        return (basePrice > 1000);
      
after
        return (order_basePrice(anOrder) > 1000);
      

問い合わせによる一時変数の置き換え (120)

一時変数を使って式の結果を保持している
式を関数に抽出する。一時変数へのすべての参照を式に置き換える。
before
        double basePrice;
        basePrice = _quantity * _itemPrice;
        if ( basePrice > 1000) {
          return basePrice * 0.95;
        } else {
          return basePrice * 0.98;
        }
      
after
        if ( basePrice() > 1000 ) {
          return basePrice() * 0.95;
        } else {
          return basePrice() * 0.98;
        }
        
        ...
        double basePrice() {
          return _quantity * _itemPrice;
        }
      

説明用変数の導入 (124)

複雑な式がある
その式の結果または部分的な結果をその目的を説明する名前をつけた一時変数に代入する。
before
        if ( (indexOf(toUpperCase(platform), "MAC") > -1) &&
             (indexOf(toUpperCase(brwoser)), "IE") > -1) &&
             wasInitialized() || resize > 0 ) {
          ....
        }
      
after
        int isMacOs;
        int isIEBrowser;
        int wasResized;
        
        isMacOs = indexOf(toUpperCase(platform), "MAC") > 1;
        isIEBRowser = indexOf(toUpperCase(brwoser)), "IE") > 1;
        wasResised = resize > 0;
        
        if ( isMacOs && isIEBrowser && wasInitialized() || wasResized ) {
          ...
        }
      

一時変数の分離 (128)

複数回代入される一時変数があるが、それは、ループ変数でも一時変数を集める変数でもない。
代入毎に別の変数に分ける。
before
        double temp;
        
        temp = 2 * (_height + _width);
        printf("%f\n", temp);
        
        temp = _height * _width;
        printf("%f\n", temp);
      
after
        {
          double perimeter;
          perimeter = 2 * (_height + _width);
          printf("%f\n", perimeter);
        }
        
        {
          double area;
          area = _height * _width;
          printf("%f\n", area);
        }	  
      

メンバの移動 (146)

ある構造体に定義されているメンバが、現在または将来に渡って、定義している構造体よりも、他の構造体から使われることの方が多い。
移動先の構造体に新たなメンバを作って、その利用側をすべて変更する。
before
        struct A {
          ...
          int aMember;
        };
        
        struct B {
          ...
        };;
      
after
        struct A {
          ...
        };
        
        struct B {
          ...
          int aMember;
        };	  
      

構造体の抽出 (149)

2つの構造体でなされるべき作業を1つの構造体で行なっている。
構造体を新たに作って、適当なメンバを元の構造体からそこに移動する。
before
        struct Person {
          char name[10];
          char officeAreaCode[10];
          char officeNumber[10];
        };
      
after
        struct TelephoneNumber {
          char areaCode[10];
          char number[10];
        };
        
        struct Person {
          char name[10];
          struct TelephoneNumber officeTelephone;
        };	  
      

双方向関連の単方向への変更 (200)

双方向関連があるが、一方の構造体はもはや他方の特性を利用していない。
不要になった関連の一方を削除する
before
        struct Order {
          ...
          struct Customer* customer;
        };
        
        struct Customer {
          ...
          struct Order* order;
        };
      
after
        struct Order {
          ...
          struct Customer* customer;
        };
        
        struct Customer {
          ...
        };	  
      

条件記述の分解 (238)

複雑な条件記述(if-then-else)がある。
その条件記述とthen部およびelse部から、関数を抽出する。
before
        if ( date_beofore(date, SUMMER_START) || date_after(date, SUMMER_END) ) {
          charge = quantity * _winterRate + _winterServiceCharge;
        } else {
          charge = quantity * _summerRate;
        }
      
after
        if ( notSummer(date) ) {
          charge = winterCharge(quantity);
        } else {
          charge = summerCharge(quantity);
        }	  
      

条件記述の統合 (240)

同じ結果を持つ一連の条件判定がある。
それらを1つの条件記述にまとめてから抽出する。
before
        double disabilityAmount() {
          if ( _seniority > 2) return 0;
          if ( _monthsDisabled > 12 ) return 0;
          if ( _isPartTime ) return 0;
          ...
        }
      
after
        double disabilityAmount() {
          if ( isNotEligibleForDisability() ) return 0;
          ...
        }	  
      

重複した条件記述の断片の統合 (243)

条件式のすべての分岐に同じコードの断片がある。
それを式の外側に移動する。
before
        if ( isSpecialDeal() ) {
          total = price * 0.95;
          send();
        } else {
          total = price * 0.98;
          send();
        }
      
after
        if ( isSpecialDeal() ) {
          total = price * 0.95;
        } else {
          total = price * 0.98;
        }
        send();	  
      

ガード節による入れ子条件記述の置き換え (250)

関数に正常ルートが不明確な条件つき振る舞いがある。
特殊ケースすべてに対してガード節を使う。
before
        double getPayAmount() {
          double result;
          if ( _isDead ) {
            result = deadAmount();
          } else {
            if ( _isSeparated ) {
              result = separatedAmount();
            } else {
              if ( _isRetired ) {
                result = retiredAmount();
              } else {
                result = normalPayAmount();
              }
            }
          }
          return result;
        }
      
after
        double getPayAmount() {
          if ( _isDead ) { return deadAmount(); }
          if ( _isSeparated ) { return separatedAmount(); }
          if ( _isRetired ) { return retiredAmount(); }
          return normalPayAmount();
        }	  
      

関数名の変更 (273)

関数の名前がその目的を正しく表現できていない。
関数の名前を変更する。
before
        void getinvcdtlmt() {
          ...
        }
      
after
        void getInvoiceableCreditLimit() {
          ...
        }	  
      

問い合わせと更新の分離 (279)

1つの関数が値を返すと同時に状態を更新している。
問い合わせ用と更新用の2つの関数をそれぞれ用意する。
before
        int getTotalOutstandingAndSetReadyForSummaries() {
          ...
        }
      
after
        int getTotalOutstanding() {
          ...
        }
        
        void setReadyForSummaries() {
          ...
        }	  
      

関数のパラメータ化 (283)

複数の関数が、異なる値に対してよく似た振る舞いをしている。
その異なる値を1つの引数として受け取る関数を作成する。
before
        void fivePersentRise() { 
          ...
        }
        
        void tenPercentRise() {
          ...
        }
      
after
        void raise(int percentage) {
          ...
        }	  
      

明示的な関数群による引数の置き換え (285)

引数の特定の値によって異なるコードが実行される関数がある。
引数の値に対応する別々の関数を作成する。
before
        void setValue(int command, int value) {
          if ( command == HEIGHT ) {
            _height = value;
          } else if ( command = WIDTH ) {
            _width = value;
          }
        }
      
after
        void setHeight(int value) {
          _height = value;
        }
        
        void setWidth(int value) {
          _width = value;
        }	  
      

構造体そのものの受け渡し (288)

ある構造体から複数の値を取得し、それらの値を関数呼び出しの引数として渡している。
代わりに構造体そのものを渡す。
before
        int low;
        int high;
        
        low = range.lo;
        high = range.hi;
        withinRange(low, high);
      
after
        withinRange(range);