Программист-прагматик. Путь от подмастерья к мастеру

ОглавлениеДобавить в закладки К обложке

Мы бы предложили абстрагировать форму окна из самого класса Window.

public abstract class Shape {

//...

  public abstract boolean overlaps (Shape s);

  public abstract int getArea();

}

public class Window {

  private Shape shape;

  public Window(Shape shape)  {

     this.shape = shape;

     ...

}

public void setShape(Shape shape) {

   this.shape = shape;

   ...

}

public boolean overlaps(Window w) {

   return shape.overlaps(w.shape);

}

public int getArea() {

   return shape.getArea();

}

}

Заметим, что в этом подходе мы предпочли делегирование созданию подклассов: окно не является «разновидностью» формы – окно «имеет» форму. Оно использует форму для выполнения своей работы. Вы убедитесь, что во многих случаях делегирование оказывается полезным для реорганизации.

Мы могли бы расширить этот пример, внедрив интерфейс Java, указывающий на методы, которые должны поддерживаться неким классом для поддержания функций формы. Эта удачная идея означает, что, когда вы расширяете принцип формы, компилятор предупредит вас о классах, которые вы затронули. Мы рекомендуем использовать интерфейсы подобным способом при делегировании всех функций какого-либо другого класса.

Упражнение 41 из раздела "Программа, которую легко тестировать"

Ответ: Вначале добавим подпрограмму main, которая будет действовать как ведущий элемент модульного тестирования. В качестве аргумента она примет простой мини-язык: <Е> будет означать опорожнение блендера, <F> – его наполнение, цифры 0–9 будут задавать скорость вращения ротора, и т. д.

public static void main(String args[]) {

// Create the blender to test

  dbc_ex blender = new dbc_ex();

// And test it according to the string on standard input

  try {

    int a;

    char c;

    while ((a = System.in.read())!= -1) {

       с = (char)a;

       if (Character.isWhitespace(c)) {

          continue;

       }

       if (Character.is Digit(с)) {

          blender.setSpeed(Character.digit(c, 10));

       }

      else {

         switch (c) {

          case 'F': blender.fi!l();

                       break;

          case 'E': blender.empty();

                       break;

          case 's': System.out.printlnfSPEED: " + blender.getSpeed());

                       break;

          case 'f': System.out.println(<FULL> + blender.isFull());

                       break;

          default: throw new RuntimeException(

                       "Unknown Test directive");

   }

 }

}

catch (java.io.lOException e) {

  System.err.println("Tesf jig failed: " + e.getMessage());

}

System.err.println("Completed blending\n");

System.exit(0);

}

Затем появится сценарий оболочки для управления тестированием.

#!/bin/sh

CMD="java dbc.dbc_sx"

failcount=0

expect okay() {

  if echo "$*" | $CMD #>/dev/null 2>&1

  then

  ...

  else

     echo "FAILED! $*"

     failcount='expr $failcount + 1'

  fi

}

expect_fail() {

if echo "$*" | SCMD>/dev/null 2> &1

then

  echo "FAILED! (Should have failed): $*"

   failcount='expr $failcount + 1'

fi

}

report() {

  if [$failcount -gt 0]

  then

     echo – e "\n\n*** FAILED $failcount TESTS\n"

     exit 1 # In case we are part of something larger

  else

    exit 0 # In case we are part of something larger

  fi

}

#

# Start the tests

#

expect_okay F12345678987654321OE # Should run thru

expect_fail F5 # Fails, speed too high

expect_fail 1 # Fails, empty

expect_fail F10E1 # Fails, empty

expect_fail F1238 # Fails, skips

expect_okay FE # Never turn on

expect_fail F1E # Emptying while running

expect_okay F10E # Should be ok

report # Report results

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


Логин
Пароль
Запомнить меня