Глава 1

Рифэкторинг, первый пример

Как мне начать писать о рифэкторинге? Традиционный путь начинать говорить о чем-либо - это обрисовать историю, обобщить принципы и тому подобное. Я становлюсь сонным когда кто-нибудь делает подобное на конференции, то я в пол уха слушаю докладчика, пока тот не начнет давать примеры. Примеры заставляют меня проснуться, потому-что на примерах я могу видеть о чем идет речь. На сколько просто обобщить какой-то принцип, настолько потом трудно представить как это применить. Примеры помогают прояснить ситуацию.

Я собираюсь начать эту книгу с примера рифэкторинга. Я покажу как проводится рифэкторинг и дам вам почувствовать процесс. После этого я смогу сделать традиционное введениие в принципы.

Однако, с вводным примером возникает определенная проблема. Если выбрать большую программу, то читателю будет сложно понять, как был проведен рифэкторинг (я попробовал, и даже не очень сложный пример занял более чем сотню страниц). В то же время, если программа будет достаточно маленькой для понимания, то рифэкторинг не будет выглядеть заслуживающим внимания.

Таким образом я в классическом тупике, как и любой другой, кто хочет описать технику, применимую для реальных приложений. Откровенно говоря, нет большого смысла делать рифэкторинг той небольшой программы, который я собираюсь вам показать. Но если этот код является частью большой системы, то рифэкторинг сразу становится важным. Поэтому я прошу посмотреть и представить это в контексте большой системы.

Исходная программа

Исходный пример очень простой. Это программа для расчета и печати отчета о задолженности клиента в видеопрокате. Она показывает какие фильмы брал клиент и на какое время. После этого она расчитывает задолженность в зависимости от типа фильма и как долго фильм был на руках. Различаются три типа фильмов: обычные, детские фильмы и новинки. В дополнение к задолженности расчитывается бонус, который зависит от того, новый фильм или нет.

Модель системы представлена несколькими классами (см. Рис 1.1)


Рис 1.1 Диаграмма классов исходной программы в UML нотации
Ниже вы можете увидеть код каждого из этих классов.

Movie

Класс Movie представляет данные о фильме.
  public class Movie {
    public static final int CHILDRENS = 2;
    public static final int REGULAR = 0;
    public static final int NEW_RELEASE = 1;

    private String title;
    private int priceCode;

    public Movie( String title, int priceCode) {
      this.title = title;
      this.priceCode = priceCode;
    }

    public int getPriceCode()  {
      return priceCode;
    }

    public void setPriceCode( int priceCode)  {
      this.priceCode = priceCode;
    }

    public String getTitle() {
      return title;
    }
  }

Rental

Класс Rental представляет данные о прокате фильма.

  public class Rental {
    private Movie movie;
    private int daysRented;

    public Rental( Movie movie, int daysRented) {
      this.movie = movie;
      this.daysRented = daysRented;
    }

    public int getDaysRented() {
      return daysRented;
    }

    public void setDaysRented( int daysRented) {
      this.daysRented = daysRented;
    }

    public Movie getMovie() {
      return movie;
    }
  }

Customer

Класс Customer представляет клиента видеопроката. Как и предыдущие классы он содержит данные и методы для доступа к ним:
  public class Customer {
    private String name;
    private Vector rentals = new Vector();

    public Customer( String name) {
      this.name = name;
    }

    public void addRental( Rental rental) {
      rentals.addElement( rental);
    }

    public String getName() {
      return name;
    }
Помимо этого, Customer имеет метод, который создает отчет:
    public String statement() {
      double totalAmount = 0;
      int frequentRenterPoints = 0;
      Enumeration rentals = this.rentals.elements();
      String result = "Rental Record for "+getName()+"\n";
      while( rentals.hasMoreElements()) {
        double thisAmount = 0;
        Rental each = ( Rental) rentals.nextElement();

        // determine amounts for each line
        switch() {
          case Movie.REGULAR:
            thisAmount += 2;
            if( each.getDaysRented()>2)
              thisAmount += ( each.getDaysRented()-2)*1.5;
            break;

          case Movie.NEW_RELEASE:
            thisAmount += each.getDaysRented()*3;
            break;

          case Movie.CHILDRENS:
            thisAmount += 1.5;
            if( each.getDaysRented()>3)
              thisAmount += ( each.getDaysRented()-3)*1.5;
            break;
        }

        // add frequent renter points
        frequentRenterPoints++;

        // add bonus for a two day new release rental
        if( each.getMovie().getPriceCode()==Movie.NEW_RELEASE &&
            each.getDaysRented()>1) frequentRenterPoints++;

        // show figures for this rental
        result += "\t"+each.getMovie().getTitle()+"\t"+
                  String.valueOf( thisAmount)+"\n";
        totalAmount += thisAmount;
      }

      // add footer lines
      result += "Amount owned is "+String.valueOf( totalAmount)+"\n";
      result += "You earned "+String.valueOf( frequentRenterPoints)+
                " frequent renter points";

      return result;
    }
  }
Sequence диаграмма для этого метода показана на Рис. 1.2

Рис 1.2 Sequence диаграмма для метода statement

В начало | следующая