# HG changeset patch
# User japod@localhost
# Date 1222768022 -7200
# Node ID 2864c6d744c0b220358efb4bacf48df53e23dfde
# Parent a861bd02a1d27cb8e68782e0bdf01d09bce440a8
solution 02 updated to 1.5
diff -r a861bd02a1d2 -r 2864c6d744c0 task1/solution02/src/org/apidesign/apifest08/currency/Convertor.java
--- a/task1/solution02/src/org/apidesign/apifest08/currency/Convertor.java Mon Sep 29 13:40:19 2008 +0200
+++ b/task1/solution02/src/org/apidesign/apifest08/currency/Convertor.java Tue Sep 30 11:47:02 2008 +0200
@@ -4,35 +4,15 @@
/**
- * Converts one currency to other. The conversion is unidirectional.
- * For example you can have convertor that converts USD (sourceCurrency) to CZK (destination currency). You can call the {@link Convertor#convert(Money)} method
- * with amount in USD to get the equivalent in CZK. If you need convert CZK to USD you can call {@link Convertor#revert()} method to get CZK to USD
- * convertor. To create a convertor instance call {@link ConvertorFactory#createConvertor(Currency, Currency)}.
+ * Converts currencies. To create an instance call {@link ConvertorFactory#createConvertor(Money, Money)}.
*/
public interface Convertor {
/**
- * Converts amount in source currency to amount in destination currency. The result is rounded to two decimal places.
- * @param money
+ * Converts amount to its equivalent in the destination currency.
+ * @param amount
+ * @param destinationCurrency
* @return
- * @throws IllegalArgumentException if money.getCurrency is not equal to sourceCurrency.
+ * @throws IllegalArgumentException if currency of the amount is not supported or if it is not possible to convert it to the destination currency.
*/
- public Money convert(Money money);
-
- /**
- * Returns convertor that converts from destination currency to source currency with the same exchange rate.
- * @return
- */
- public Convertor revert();
-
- /**
- * Returns source currency.
- * @return
- */
- public Currency getSourceCurrency();
-
- /**
- * Returns destination currency.
- * @return
- */
- public Currency getDestinationCurrency();
+ public Money convert(Money amount, Currency destinationCurrency) throws IllegalArgumentException;
}
diff -r a861bd02a1d2 -r 2864c6d744c0 task1/solution02/src/org/apidesign/apifest08/currency/ConvertorFactory.java
--- a/task1/solution02/src/org/apidesign/apifest08/currency/ConvertorFactory.java Mon Sep 29 13:40:19 2008 +0200
+++ b/task1/solution02/src/org/apidesign/apifest08/currency/ConvertorFactory.java Tue Sep 30 11:47:02 2008 +0200
@@ -1,46 +1,27 @@
package org.apidesign.apifest08.currency;
-import java.util.Currency;
/**
- * Creates default {@link Convertor} implementations.
+ * Creates {@link Convertor} implementations.
* @author lukas
*
*/
public class ConvertorFactory {
- private static final DefaultConvertorFactory DEFAULT_FACTORY = new DefaultConvertorFactory();
-
private ConvertorFactory()
{
//nothing
}
/**
- * Creates {@link Convertor} that converts from sourceCurrency to destinationCurrency with stored exchange rate.
- * @param sourceCurrency
- * @param destinationCurrency
- * @return
- * @throws UnsupportedConversionException when exchange rate between currencies is not known.
- */
- /*
- * Only one of the createConveror methods is needed. The assignment is not explicit where the exchange rate should be set.
- */
- public static final Convertor createConvertor(Currency sourceCurrency, Currency destinationCurrency) throws UnsupportedConversionException
- {
- return DEFAULT_FACTORY.getConvertor(sourceCurrency, destinationCurrency);
- }
- /**
* Creates {@link Convertor} that converts from sourceEquivalent.currency to destinationEquivalent.currency.
* Exchange rate is set as equivalents. It means if you want to create USD to CZK convertor where USD1 = CZK17
- * call createConvertor(new MoneyImpl(1, USD), new MoneyImpl(17, CZK)).
+ * call createConvertor(new MoneyImpl(1, USD), new MoneyImpl(17, CZK)). Convertor created by this method
+ * rounds the result to two decimal places.
* @param sourceEquivalent
* @param destinationEquivalent
* @return
*/
- /*
- * Only one of the createConveror methods is needed. The assignment is not explicit where the exchange rate should be set.
- */
public static final Convertor createConvertor(Money sourceEquivalent, Money destinationEquivalent)
{
return new DefaultConvertor(sourceEquivalent.getAmount(), destinationEquivalent.getAmount(), sourceEquivalent.getCurrency(), destinationEquivalent.getCurrency());
diff -r a861bd02a1d2 -r 2864c6d744c0 task1/solution02/src/org/apidesign/apifest08/currency/DefaultConvertor.java
--- a/task1/solution02/src/org/apidesign/apifest08/currency/DefaultConvertor.java Mon Sep 29 13:40:19 2008 +0200
+++ b/task1/solution02/src/org/apidesign/apifest08/currency/DefaultConvertor.java Tue Sep 30 11:47:02 2008 +0200
@@ -31,26 +31,56 @@
public DefaultConvertor(BigDecimal sourceEquivalent, BigDecimal destinationEquivalent, Currency sourceCurrency, Currency destinationCurrency) {
super();
+ if (BigDecimal.ZERO.compareTo(sourceEquivalent)==0)
+ {
+ throw new IllegalArgumentException("Source equivalent amount can not be 0.");
+ }
+ if (BigDecimal.ZERO.compareTo(destinationEquivalent)==0)
+ {
+ throw new IllegalArgumentException("Destination equivalent amount can not be 0.");
+ }
this.sourceEquivalent = sourceEquivalent;
this.destinationEquivalent = destinationEquivalent;
this.sourceCurrency = sourceCurrency;
this.destinationCurrency = destinationCurrency;
}
- public Money convert(Money money) {
- if (money==null)
+ public Money convert(Money amount, Currency destinationCurrency) {
+ if (amount==null)
{
throw new NullPointerException("Money is null");
}
- if (!money.getCurrency().equals(getSourceCurrency()))
+ if (destinationCurrency==null)
{
- throw new IllegalArgumentException("Can not convert from "+money.getCurrency()+". Converts "+getSourceCurrency()+" to "+getDestinationCurrency());
+ throw new NullPointerException("destionationCurrency is null");
}
- BigDecimal sourceAmount = money.getAmount();
- BigDecimal destinationAmount = sourceAmount.multiply(destinationEquivalent).divide(sourceEquivalent, 2, RoundingMode.HALF_DOWN);
+ if (isConversionInOpositeDirection(amount, destinationCurrency))
+ {
+ return revert().convert(amount, destinationCurrency);
+ }
+ if (!amount.getCurrency().equals(getSourceCurrency()))
+ {
+ throw new IllegalArgumentException("Can not convert from "+amount.getCurrency()+". Converts between "+getSourceCurrency()+" and "+getDestinationCurrency());
+ }
+ if (!getDestinationCurrency().equals(destinationCurrency))
+ {
+ throw new IllegalArgumentException("Can not convert to "+destinationCurrency+". Converts between "+getSourceCurrency()+" and "+getDestinationCurrency());
+ }
+ BigDecimal sourceAmount = amount.getAmount();
+ BigDecimal destinationAmount = sourceAmount.multiply(destinationEquivalent).divide(sourceEquivalent, 2, RoundingMode.HALF_UP);
return new MoneyImpl(destinationAmount, getDestinationCurrency());
}
+ /**
+ * Returns true, if the conversion is in oposit direction.
+ * @param amount
+ * @param destinationCurrency
+ * @return
+ */
+ private boolean isConversionInOpositeDirection(Money amount, Currency destinationCurrency) {
+ return amount.getCurrency().equals(getDestinationCurrency()) && destinationCurrency.equals(getSourceCurrency());
+ }
+
public Convertor revert() {
return new DefaultConvertor(destinationEquivalent, sourceEquivalent, destinationCurrency, sourceCurrency);
diff -r a861bd02a1d2 -r 2864c6d744c0 task1/solution02/src/org/apidesign/apifest08/currency/Money.java
--- a/task1/solution02/src/org/apidesign/apifest08/currency/Money.java Mon Sep 29 13:40:19 2008 +0200
+++ b/task1/solution02/src/org/apidesign/apifest08/currency/Money.java Tue Sep 30 11:47:02 2008 +0200
@@ -10,7 +10,7 @@
*
*/
/*
- * Whether we need such interface depends on context. I can imagine than in a desktop application this interface
+ * Whether we need such interface depends on the context. I can imagine than in a desktop application this interface
* would be useless, Money could be a class. In J2EE environment it can be useful.
*/
public interface Money {
diff -r a861bd02a1d2 -r 2864c6d744c0 task1/solution02/src/org/apidesign/apifest08/currency/MoneyImpl.java
--- a/task1/solution02/src/org/apidesign/apifest08/currency/MoneyImpl.java Mon Sep 29 13:40:19 2008 +0200
+++ b/task1/solution02/src/org/apidesign/apifest08/currency/MoneyImpl.java Tue Sep 30 11:47:02 2008 +0200
@@ -19,7 +19,7 @@
public MoneyImpl(BigDecimal amount, Currency currency) {
if (amount==null) throw new NullPointerException("Amount is null");
if (currency==null) throw new NullPointerException("Currency is null"+currency);
- this.amount = amount.setScale(2);
+ this.amount = amount;
this.currency = currency;
}
@@ -31,15 +31,16 @@
this(BigDecimal.valueOf(amount), currency);
}
- /* (non-Javadoc)
- * @see org.apidesign.apifest08.currency.Money#getAmount()
+ /**
+ * Returns amount.
+ * @return
*/
public BigDecimal getAmount() {
return amount;
}
- /* (non-Javadoc)
- * @see org.apidesign.apifest08.currency.Money#getCurrency()
+ /**
+ * Returns currency.
*/
public Currency getCurrency() {
return currency;
@@ -67,7 +68,7 @@
if (amount == null) {
if (other.amount != null)
return false;
- } else if (!amount.equals(other.amount))
+ } else if (amount.compareTo(other.amount)!=0)
return false;
if (currency == null) {
if (other.currency != null)
diff -r a861bd02a1d2 -r 2864c6d744c0 task1/solution02/test/org/apidesign/apifest08/test/ConvertorFactoryTest.java
--- a/task1/solution02/test/org/apidesign/apifest08/test/ConvertorFactoryTest.java Mon Sep 29 13:40:19 2008 +0200
+++ b/task1/solution02/test/org/apidesign/apifest08/test/ConvertorFactoryTest.java Tue Sep 30 11:47:02 2008 +0200
@@ -1,14 +1,15 @@
package org.apidesign.apifest08.test;
-import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static org.apidesign.apifest08.test.Task1Test.CZK;
import static org.apidesign.apifest08.test.Task1Test.USD;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
-import java.util.Currency;
-
+import org.apidesign.apifest08.currency.Convertor;
import org.apidesign.apifest08.currency.ConvertorFactory;
-import org.apidesign.apifest08.currency.UnsupportedConversionException;
+import org.apidesign.apifest08.currency.MoneyImpl;
import org.junit.Test;
@@ -16,31 +17,36 @@
@Test(expected=NullPointerException.class)
public void testNullSource()
{
- ConvertorFactory.createConvertor(null, USD);
+ ConvertorFactory.createConvertor(null, new MoneyImpl(1,USD));
}
@Test(expected=NullPointerException.class)
public void testNullDestination()
{
- ConvertorFactory.createConvertor(CZK, null);
- }
- @Test(expected=IllegalArgumentException.class)
- public void testShortSource()
- {
- ConvertorFactory.createConvertor(Currency.getInstance("CZ"), USD);
- }
- @Test(expected=UnsupportedConversionException.class)
- public void testUnknownCombination()
- {
- ConvertorFactory.createConvertor(CZK, Currency.getInstance("ZAR"));
+ ConvertorFactory.createConvertor(new MoneyImpl(17,CZK), null);
}
@Test
public void testOk()
{
- assertNotNull(ConvertorFactory.createConvertor(CZK, USD));
+ assertNotNull(ConvertorFactory.createConvertor(new MoneyImpl(17,CZK), new MoneyImpl(1,USD)));
}
@Test
- public void testReverted()
+ public void testOkDecimalRate()
{
- assertEquals(ConvertorFactory.createConvertor(CZK, USD).revert(), ConvertorFactory.createConvertor(USD, CZK));
+ Convertor c = ConvertorFactory.createConvertor(new MoneyImpl(1,CZK), new MoneyImpl(1d/17d,USD));
+ assertNotNull(c);
+ assertEquals(new MoneyImpl(17,CZK),c.convert(new MoneyImpl(1,USD), CZK));
+ }
+ @Test
+ public void testZeroEquivalentRate()
+ {
+ try
+ {
+ ConvertorFactory.createConvertor(new MoneyImpl(1,CZK), new MoneyImpl(0,USD));
+ fail("Exception expected");
+ }
+ catch(IllegalArgumentException e)
+ {
+ assertTrue("OK",true);
+ }
}
}
diff -r a861bd02a1d2 -r 2864c6d744c0 task1/solution02/test/org/apidesign/apifest08/test/ConvertorTest.java
--- a/task1/solution02/test/org/apidesign/apifest08/test/ConvertorTest.java Mon Sep 29 13:40:19 2008 +0200
+++ b/task1/solution02/test/org/apidesign/apifest08/test/ConvertorTest.java Tue Sep 30 11:47:02 2008 +0200
@@ -6,6 +6,7 @@
import java.math.BigDecimal;
+import org.apidesign.apifest08.currency.Convertor;
import org.apidesign.apifest08.currency.ConvertorFactory;
import org.apidesign.apifest08.currency.Money;
import org.apidesign.apifest08.currency.MoneyImpl;
@@ -14,17 +15,18 @@
public class ConvertorTest {
+ private static final Convertor CZK_TO_USD_CONVERTOR = ConvertorFactory.createConvertor(new MoneyImpl(17,CZK), new MoneyImpl(1,USD));
@Test
public void testConvertSmall()
{
- Money converted = ConvertorFactory.createConvertor(CZK, USD).convert(new MoneyImpl(0.17,CZK));
+ Money converted = CZK_TO_USD_CONVERTOR.convert(new MoneyImpl(0.17,CZK),USD);
assertEquals(new MoneyImpl(new BigDecimal("0.01"),USD),converted);
assertEquals(USD,converted.getCurrency());
}
@Test
public void testConvertSmallReverse()
{
- Money converted = ConvertorFactory.createConvertor(USD, CZK).convert(new MoneyImpl(0.01,USD));
+ Money converted = CZK_TO_USD_CONVERTOR.convert(new MoneyImpl(0.01,USD),CZK);
assertEquals(new MoneyImpl(new BigDecimal("0.17"),CZK),converted);
}
}
diff -r a861bd02a1d2 -r 2864c6d744c0 task1/solution02/test/org/apidesign/apifest08/test/MoneyTest.java
--- a/task1/solution02/test/org/apidesign/apifest08/test/MoneyTest.java Mon Sep 29 13:40:19 2008 +0200
+++ b/task1/solution02/test/org/apidesign/apifest08/test/MoneyTest.java Tue Sep 30 11:47:02 2008 +0200
@@ -1,12 +1,12 @@
package org.apidesign.apifest08.test;
import static junit.framework.Assert.assertEquals;
+import static org.apidesign.apifest08.test.Task1Test.CZK;
import java.math.BigDecimal;
import org.apidesign.apifest08.currency.MoneyImpl;
import org.junit.Test;
-import static org.apidesign.apifest08.test.Task1Test.*;
public class MoneyTest {
@Test(expected=NullPointerException.class)
@@ -19,6 +19,6 @@
}
@Test
public void testOk(){
- assertEquals(new BigDecimal("123.00"),new MoneyImpl(123,CZK).getAmount());
+ assertEquals(0,new MoneyImpl(123,CZK).getAmount().compareTo(new BigDecimal("123")));
}
}
diff -r a861bd02a1d2 -r 2864c6d744c0 task1/solution02/test/org/apidesign/apifest08/test/Task1Test.java
--- a/task1/solution02/test/org/apidesign/apifest08/test/Task1Test.java Mon Sep 29 13:40:19 2008 +0200
+++ b/task1/solution02/test/org/apidesign/apifest08/test/Task1Test.java Tue Sep 30 11:47:02 2008 +0200
@@ -15,9 +15,11 @@
* shall run without any runtime permissions.
*/
public class Task1Test extends TestCase {
+
public static final Currency USD = Currency.getInstance("USD");
public static final Currency CZK = Currency.getInstance("CZK");
public static final Currency SKK = Currency.getInstance("SKK");
+
public Task1Test(String testName) {
super(testName);
}
@@ -30,8 +32,19 @@
protected void tearDown() throws Exception {
}
+ //
+ // Imagine that there are three parts of the whole system:
+ // 1. there is someone who knows the current exchange rate
+ // 2. there is someone who wants to do the conversion
+ // 3. there is the API between 1. and 2. which allows them to communicate
+ // Please design such API
+ //
+
/** Create convertor that understands two currencies, CZK and
- * USD. Make 1 USD == 17 CZK.
+ * USD. Make 1 USD == 17 CZK. This is a method provided for #1 group -
+ * e.g. those that know the exchange rate. They somehow need to create
+ * the objects from the API and tell them the exchange rate. The API itself
+ * knows nothing about any rates, before the createCZKtoUSD method is called.
*
* Creation of the convertor shall not require subclassing of any class
* or interface on the client side.
@@ -39,14 +52,15 @@
* @return prepared convertor ready for converting USD to CZK and CZK to USD
*/
public static Convertor createCZKtoUSD() {
- //You can use both variants. I wrote the first one but than I realized that maybe
- //you want me to write the second one. So I have written both.
- return ConvertorFactory.createConvertor(CZK, USD);
- //return ConvertorFactory.createConvertor(new MoneyImpl(17,CZK), new MoneyImpl(1,USD));
+ return ConvertorFactory.createConvertor(new MoneyImpl(17,CZK), new MoneyImpl(1,USD));
}
/** Create convertor that understands two currencies, CZK and
- * SKK. Make 100 SKK == 80 CZK.
+ * SKK. Make 100 SKK == 80 CZK. Again this is method for the #1 group -
+ * it knows the exchange rate, and needs to use the API to create objects
+ * with the exchange rate. Anyone shall be ready to call this method without
+ * any other method being called previously. The API itself shall know
+ * nothing about any rates, before this method is called.
*
* Creation of the convertor shall not require subclassing of any class
* or interface on the client side.
@@ -54,38 +68,93 @@
* @return prepared convertor ready for converting SKK to CZK and CZK to SKK
*/
public static Convertor createSKKtoCZK() {
- //You can use both variants. I wrote the first one but than I realized that maybe
- //you want me to write the second one. So I have written both.
- //return ConvertorFactory.createConvertor(SKK, CZK);
- return ConvertorFactory.createConvertor(new MoneyImpl(100,SKK), new MoneyImpl(80,CZK));
+ return ConvertorFactory.createConvertor(new MoneyImpl(100,SKK), new MoneyImpl(80,CZK));
}
+
+ //
+ // now the methods for group #2 follow:
+ // this group knows nothing about exchange rates, but knows how to use
+ // the API to do conversions. It somehow (by calling one of the factory
+ // methods) gets objects from the API and uses them to do the conversions.
+ //
/** Use the convertor from createCZKtoUSD
method and do few conversions
* with it.
*/
public void testCurrencyCZKUSD() throws Exception {
- Convertor czkToUsdConvertor = createCZKtoUSD();
+ Convertor c = createCZKtoUSD();
// convert $5 to CZK using c:
- Convertor usdToCzkConvertor = czkToUsdConvertor.revert();
- assertEquals("Result is 85 CZK",new MoneyImpl(85,CZK), usdToCzkConvertor.convert(new MoneyImpl(5,USD)));
+ assertEquals("Result is 85 CZK",new MoneyImpl(85,CZK), c.convert(new MoneyImpl(5,USD),CZK));
// convert $8 to CZK
- assertEquals("Result is 136 CZK",new MoneyImpl(136,CZK), usdToCzkConvertor.convert(new MoneyImpl(8,USD)));
+ assertEquals("Result is 136 CZK",new MoneyImpl(136,CZK), c.convert(new MoneyImpl(8,USD),CZK));
// convert 1003CZK to USD
- assertEquals("Result is 59 USD", new MoneyImpl(59,USD), czkToUsdConvertor.convert(new MoneyImpl(1003,CZK)));
+ assertEquals("Result is 59 USD", new MoneyImpl(59,USD), c.convert(new MoneyImpl(1003,CZK),USD));
+
}
/** Use the convertor from createSKKtoCZK
method and do few conversions
* with it.
*/
public void testCurrencySKKCZK() throws Exception {
- Convertor skkToCzkConvertor = createSKKtoCZK();
+ Convertor c = createSKKtoCZK();
// convert 16CZK using c:
- assertEquals("Result is 20 SKK", new MoneyImpl(20,SKK), skkToCzkConvertor.revert().convert(new MoneyImpl(16,CZK)));
+ assertEquals("Result is 20 SKK", new MoneyImpl(20,SKK), c.convert(new MoneyImpl(16,CZK),SKK));
// convert 500SKK to CZK
- assertEquals("Result is 400 CZK", new MoneyImpl(400,CZK), skkToCzkConvertor.convert(new MoneyImpl(500,SKK)));
+ assertEquals("Result is 400 CZK", new MoneyImpl(400,CZK), c.convert(new MoneyImpl(500,SKK),CZK));
+ }
+
+ /** Verify that the CZK to USD convertor knows nothing about SKK.
+ */
+ public void testCannotConvertToSKKwithCZKUSDConvertor() throws Exception {
+ Convertor c = createCZKtoUSD();
+ // convert $5 to SKK, the API shall say this is not possible
+ try
+ {
+ c.convert(new MoneyImpl(5, USD), SKK);
+ fail("Exception expected");
+ }
+ catch(IllegalArgumentException e)
+ {
+ assertTrue("Ok",true);
+ }
+ // convert 500 SKK to CZK, the API shall say this is not possible
+ try
+ {
+ c.convert(new MoneyImpl(500, SKK), CZK);
+ fail("Exception expected");
+ }
+ catch(IllegalArgumentException e)
+ {
+ assertTrue("Ok",true);
+ }
+ }
+
+ /** Verify that the CZK to SKK convertor knows nothing about USD.
+ */
+ public void testCannotConvertToUSDwithCZKSKKConvertor() throws Exception {
+ Convertor c = createSKKtoCZK();
+ // convert $5 to SKK, the API shall say this is not possible
+ try
+ {
+ c.convert(new MoneyImpl(5, USD), SKK);
+ fail("Exception expected");
+ }
+ catch(IllegalArgumentException e)
+ {
+ assertTrue("Ok",true);
+ }
+ // convert 500 CZK to USD, the API shall say this is not possible
+ try
+ {
+ c.convert(new MoneyImpl(500, CZK), USD);
+ fail("Exception expected");
+ }
+ catch(IllegalArgumentException e)
+ {
+ assertTrue("Ok",true);
+ }
}
}
-