1 / 43

Программирование с помощью тестирования на примере

Тема 3. Программирование с помощью тестирования на примере. XP. eXtreme Programming. Задача – разработка класса для работы с различными валютами. Пусть есть некоторая информационная система для банков.

nash
Download Presentation

Программирование с помощью тестирования на примере

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Тема 3 Программирование с помощью тестирования на примере XP eXtreme Programming

  2. Задача – разработка класса для работы с различными валютами Пусть есть некоторая информационная система для банков. В качестве основной валюты для расчетов используется доллар, для работы с которым был реализован класс Dollar. В условиях кризиса требовалось перевести систему на мультивалютные расчеты. XP

  3. Список задач для тестированияРешение задачи 2 $5+10 CHF =$10, если курс обмена 2:1 $5*2=$10 public void testMultiplication() { Dollar five=new Dollar(5); five.times(2); assertEquals(10.,five.amount); } public class Dollar { double amount; Dollar (double amount) {} void times(intmult) {} } XP

  4. Варианты исправления • При объявлении переменной amount: double amount=5*2; • В методе times: void times(int mult){ amount=5*2;} • Изменения должны быть и в конструкторе, и в методе times: Dollar (double amount){this.amount=amount;} void times(int mult) {amount*=mult;} XP

  5. Список задач $5+10 CHF =$10, если курс обмена 2:1 $5*2=$10 amount-закрытая переменная Побочные эффекты в классе Dollar – изменение объекта Округление денежных величин Тест, который не выполняется: public void testMultiplication() { Dollar five=new Dollar(5); five.times(2); assertEquals(10.,five.amount); five.times(3); assertEquals(15.,five.amount); } XP

  6. Решение проблемы Изменение метода times: Dollar times(intmult) { return new Dollar(amount*mult); } Изменение теста: public void testMultiplication() { Dollar five=new Dollar(5); Dollar product=five.times(2);assertEquals(10.,product.amount); product=five.times(3); assertEquals(15.,product.amount); } XP

  7. Равенство $5+10 CHF =$10, если курс обмена 2:1 $5*2=$10 amount-закрытая переменная Побочные эффекты в классе Dollar – изменение объекта Округление денежных величин equals() Тест на равенство: public void testEquality() { assertTrue(new Dollar(5).equals(new Dollar(5))); } Тест не работает – нужна заглушка метода equals. public boolean equals(Object obj){return true;} XP

  8. Равенство Не работает другой тест: public void testEquality() { assertTrue(new Dollar(5).equals(new Dollar(5))); assertFalse(new Dollar(5).equals(new Dollar(6))); } Требуется изменить метод equals: public boolean equals(Object obj) { Dollar dollar=(Dollar)obj; return amount==dollar.amount; } XP

  9. Список задач $5+10 CHF =$10, если курс обмена 2:1 $5*2=$10 amount-закрытая переменная Побочные эффекты в классе Dollar – изменение объекта Округление денежных величин equals() Равенство значению null Равенство объектов XP

  10. Закрытые переменные Меняем тест – сравниваем на равенство объекты: public void testMultiplication() { Dollar five=new Dollar(5); assertEquals(new Dollar(10),five.times(2)); assertEquals(new Dollar(15),five.times(3)); } XP

  11. Список задач $5+10 CHF =$10, если курс обмена 2:1 $5*2=$10 amount-закрытая переменная Побочные эффекты в классе Dollar – изменение объекта Округление денежных величин equals() Равенство значению null Равенство объектов 5 CHF*2=10 CHF XP

  12. Добавляем класс для швейцарского франка • Добавляем новый класс Franc (код из класса Dollar); • Пишем тест: public void testFrancMultiplication() { Franc five=new Franc(5); assertEquals(new Franc(10),five.times(2)); assertEquals(new Franc(15),five.times(3)); } XP

  13. Список задач $5+10 CHF =$10, если курс обмена 2:1 $5*2=$10 amount-закрытая переменная Побочные эффекты в классе Dollar – изменение объекта Округление денежных величин equals() Равенство значению null Равенство объектов 5 CHF*2=10 CHF Дублирование Dollar/Franc Общие операции equals() Общие операции times() XP

  14. Решение проблемы – создание иерархии Создадим класс Money, от которого наследуют Dollar и Franc Перенесем в него переменную amount. Для выполнения всех тестов требуется сделать ее protected. XP

  15. Снова проверка на равенство Меняем в классе Dollar метод equals – перенос на тип данных Money public boolean equals(Object obj) { Money money=(Money)obj; return amount==money.amount; } Переносим метод в класс Money и удаляем из классов Dollar и Franc. Тест: public void testEquality() { assertTrue(new Dollar(5).equals(new Dollar(5))); assertFalse(new Dollar(5).equals(new Dollar(6))); assertTrue(new Franc(5).equals(new Franc(5))); assertFalse(new Franc(5).equals(new Franc(6))); } XP

  16. Список задач $5+10 CHF =$10, если курс обмена 2:1 $5*2=$10 amount-закрытая переменная Побочные эффекты в классе Dollar – изменение объекта Округление денежных величин equals() Равенство значению null Равенство объектов 5 CHF*2=10 CHF Дублирование Dollar/Franc Общие операции equals() Общие операции times() Сравнение долларов и франков XP

  17. Сравнение долларов и франков Тест: public void testEquality() { assertTrue(new Dollar(5).equals(new Dollar(5))); assertFalse(new Dollar(5).equals(new Dollar(6))); assertTrue(new Franc(5).equals(new Franc(5))); assertFalse(new Franc(5).equals(new Franc(6))); assertFalse(new Franc(5).equals(new Dollar(5))); } Изменение метода: public boolean equals(Object obj) { Money money=(Money)obj; return amount==money.amount && getClass().equals(money.getClass()); } XP

  18. Список задач $5+10 CHF =$10, если курс обмена 2:1 $5*2=$10 amount-закрытая переменная Побочные эффекты в классе Dollar – изменение объекта Округление денежных величин equals() Равенство значению null Равенство объектов 5 CHF*2=10 CHF Дублирование Dollar/Franc Общие операции equals() Общие операции times() Сравнение долларов и франков Валюта? XP

  19. Устранение дублирования times требует другого способа создания объектов Методы times могут возвращать Money Money times(intmult) { return new Dollar(amount*mult); } В результате классы почти одинаковые -> хочется сделать только один класс XP

  20. Фабричные методы создания объектов В класс Money добавим фабричный метод создания объекта Dollar: static Dollar dollar(double amount) { return new Dollar(amount);} Меняем принцип создания объекта в тесте: public void testMultiplication() { Money five=Money.dollar(5); assertEquals(Money.dollar(10),five.times(2)); assertEquals(Money.dollar(15),five.times(3)); } Осталась ошибка – метод times не определен в классе Money XP

  21. Фабричные методы создания объектов Делаем класс Money абстрактным, добавляя в него абстрактный метод times. abstract class Money { protected double amount; … abstract Money times(int mult); } Меняем фабричный метод с точки зрения возвращаемого значения static Money dollar(double amount) { return new Dollar(amount); } XP

  22. Фабричные методы создания объектов Делаем те же действия с классом Franc Меняем тесты public void testEquality() { assertTrue(Money.dollar(5).equals(Money.dollar(5))); assertFalse(Money.dollar(5).equals(Money.dollar(6))); assertTrue(Money.franc(5).equals(Money.franc(5))); assertFalse(Money.franc(5).equals(Money.franc(6))); assertFalse(Money.franc(5).equals(Money.dollar(5))); } XP

  23. Список задач $5+10 CHF =$10, если курс обмена 2:1 $5*2=$10 amount-закрытая переменная Побочные эффекты в классе Dollar – изменение объекта Округление денежных величин equals() Равенство значению null Равенство объектов 5 CHF*2=10 CHF Дублирование Dollar/Franc Общие операции equals() Общие операции times() Сравнение долларов и франков Валюта? Нужен ли тест на умножения франков на число? XP

  24. Пытаемся избавиться от производных классов Добавляем метод для получения названия валюты в классы Dollar и Franc и абстрактный метод в класс Money: abstract String currency(); String currency(){ return "USD";} String currency(){ return "CHF";} Тест public void testCurrency() { assertEquals("USD",Money.dollar(1).currency()); assertEquals("CHF",Money.franc(1).currency()); } XP

  25. Пытаемся избавиться от производных классов Вводим символьную переменную для обозначения валюты в класс Money и переносим функцию currency полностью в класс Money abstract class Money { protected double amount; protected String currency; … String currency() { return currency; } } Конструкторы должны инициализировать эту строку, например, Franc (double amount) { this.amount=amount; this.currency="CHF"; } XP

  26. Пытаемся избавиться от производных классов Конструкторы производных классов похожи -> можно инициализировать переменную currency внутри конструктора базового класса: public Money(double amount, String currency) { this.amount=amount; this.currency=currency; } Конструкторы производных классов выглядят, например, так: Franc (double amount) { super(amount,"CHF"); } Можно добавить второй параметр с обозначением валюты и передавать его конструктору базового класса XP

  27. Список задач $5+10 CHF =$10, если курс обмена 2:1 $5*2=$10 amount-закрытая переменная Побочные эффекты в классе Dollar – изменение объекта Округление денежных величин equals() Равенство значению null Равенство объектов 5 CHF*2=10 CHF Дублирование Dollar/Franc Общие операции equals() Общие операции times() Сравнение долларов и франков Валюта? Нужен ли тест на умножения франков на число? XP

  28. Общая операция times Функция times класса Dollar выглядит так: Money times(intmult) { return Money.dollar(amount*mult);} Вернемся к вызову конструктора: Money times(intmult) { return new Dollar(amount*mult,“USD");} Строку с названием валюты можно получить из поля currency-> переносим в класс Money (класс перестает быть абстрактным) Money times(intmult){ return new Money(amount*mult,currency);} Тест не срабатывает – сравнение имен классов (один объект класса Dollar, другой – класса Money). XP

  29. Общая операция times Вернемся к методам times внутри производных классов, например, в Dollar Money times(int mult) { return new Dollar(amount*mult,currency);} Тесты сработали. Добавляем тест для сравнения объектов, созданных с помощью Money и Dollar public void testDifferentClasses() { assertTrue(new Money(10,"CHF").equals(new Franc(10,"CHF"))); } Тест не сработал. XP

  30. Общая операция times Меняем equals – сравнение названий валют, а не имен классов. public boolean equals(Object obj) { Money money=(Money)obj; return amount==money.amount && currency.equals(money.currency); } Тесты сработали. Снова отказываемся от методов times производных классов. Тесты сработали. XP

  31. Список задач $5+10 CHF =$10, если курс обмена 2:1 $5*2=$10 amount-закрытая переменная Побочные эффекты в классе Dollar – изменение объекта Округление денежных величин equals() Равенство значению null Равенство объектов 5 CHF*2=10 CHF Дублирование Dollar/Franc Общие операции equals() Общие операции times() Сравнение долларов и франков Валюта? Нужен ли тест на умножения франков на число? XP

  32. Устраняем дублирование Производные классы имеют только конструкторы. Отказываемся от них. Методы-фабрики должны вызывать конструктор класса Money. static Money franc(double amount) { return new Money(amount,"CHF"); } Тест на равенство объектов разных классов и умножение франков можно удалить. XP

  33. Список задач $5+10 CHF =$10, если курс обмена 2:1 $5*2=$10 amount-закрытая переменная Побочные эффекты в классе Dollar – изменение объекта Округление денежных величин equals() Равенство значению null Равенство объектов 5 CHF*2=10 CHF Дублирование Dollar/Franc Общие операции equals() Общие операции times() Сравнение долларов и франков Валюта? Нужен ли тест на умножения франков на число? $5+$5=$10 XP

  34. Реализуем простое сложение Реализуем тест: public void testSimpleAddition() { Money sum=Money.dollar(5).plus(Money.dollar(5)); assertEquals(sum,Money.dollar(10)); } В класс Money добавляем функцию plus: Money plus(Money added) { return new Money(amount+added.amount,currency); } XP

  35. Используем метафору – банк Обменный курсы должен знать банк -> создаем класс Bank (в нем метод reduce для перевода денег в конкретную валюту). XP

  36. Используем метафору – банк и бумажник Тест: public void testSimpleAddition() { Money five=Money.dollar(5); Money sum=five.plus(five); Bank bank=new Bank(); Money reduced=bank.reduce(sum, "USD"); assertEquals(sum,Money.dollar(10)); } В классе Bank должна быть заглушка: public class Bank { Money reduce(Money source,String to) { return Money.dollar(10); } } Метод plus возвращает Money. XP

  37. Пытаемся определить курсы валют Тесты: public void testReduceMoney() { Bank bank=new Bank(); bank.addRate("CHF","USD",2); Money result=bank.reduce(Money.franc(2), "USD"); assertEquals(Money.dollar(1),result); } public void testHashRates() { Bank bank=new Bank(); bank.addRate("CHF", "USD", 2); assertTrue(bank.rate("CHF", "USD")==2.); } XP

  38. Реализуем хранение курсов валют В классе Bank создаем хэш-таблицу: private class Pair { private String from; private String to; public Pair(String from, String to) { this.from=from; this.to=to; } public boolean equals(Object obj) { Pair p=(Pair)obj; return from.equals(p.from) && to.equals(p.to); } public inthashCode(){ return 0; } } private Hashtable rates=new Hashtable(); XP

  39. Добавление и получение курса void addRate(String from, String to, double rate) { rates.put(new Pair(from,to), new Double(rate)); } double rate(String from, String to) { Double rate=(Double)rates.get(new Pair(from,to)); return rate.doubleValue(); } XP

  40. Проверка определения курса валюты Делаем метод-заглушку класса Bank: Money reduce(Money source,String to) { double rate=1; if (source.currency.equals("CHF") && to.equals("USD")) rate=2.; if (source.currency.equals("USD") && to.equals("CHF")) rate=1./2; return new Money(source.amount/rate,to); } XP

  41. Проверка определения курса валюты Изменяем заглушку: Money reduce(Money source,String to) { double rate=1; Pair p=new Pair(source.currency,to); if (rates.containsKey(p)) rate=rate(source.currency,to); return new Money(source.amount/rate,to); } XP

  42. Что, если курса валют нет? Изменяем тест public void testReduceMoney() { Bank bank=new Bank(); bank.addRate("CHF","USD",2); Money result=bank.reduce(Money.franc(2), "USD"); assertEquals(Money.dollar(1),result); result=bank.reduce(Money.dollar(2), "USD"); assertEquals(result,Money.dollar(2)); result=bank.reduce(Money.dollar(2), "CHF"); assertEquals(result,Money.franc(4)); } Тест не работает, так как если курса нет, то в хэш-таблице ничего не будет найдено. XP

  43. Что, если курса валют нет? Добавлять надо два курса void addRate(String from, String to, double rate) { rates.put(new Pair(from,to), new Double(rate)); rates.put(new Pair(to,from), new Double(1/rate)); } XP

More Related