japod@36: package org.apidesign.apifest08.test; japod@36: japod@36: import java.util.Currency; japod@36: import junit.framework.TestCase; japod@36: import org.apidesign.apifest08.currency.ConversionRate; japod@36: import org.apidesign.apifest08.currency.Convertor; japod@36: import org.apidesign.apifest08.currency.IllegalRequestSubtypeException; japod@36: import org.apidesign.apifest08.currency.MonetaryAmount; japod@36: import org.apidesign.apifest08.currency.TableConvertor; japod@36: japod@36: /** There are many currencies around the world and many banks manipulate japod@36: * with more than one or two at the same time. As banks are usually the japod@36: * best paying clients, which is true even in case of your Convertor API, japod@36: * it is reasonable to listen to their requests. japod@36: *

japod@36: * The quest for today is to enhance your existing convertor API to hold japod@36: * information about many currencies and allow conversions between any of them. japod@36: * Also, as conversion rates for diferent currencies usually arise from various japod@36: * bank departments, there is another important need. There is a need to japod@36: * compose two convertors into one by merging all the information about japod@36: * currencies they know about. japod@36: */ japod@36: public class Task2Test extends TestCase { japod@36: public Task2Test(String testName) { japod@36: super(testName); japod@36: } japod@36: japod@36: @Override japod@36: protected void setUp() throws Exception { japod@36: } japod@36: japod@36: @Override japod@36: protected void tearDown() throws Exception { japod@36: } japod@36: japod@36: protected static final Currency CZK = Currency.getInstance( "CZK" ); japod@36: protected static final Currency SKK = Currency.getInstance( "SKK" ); japod@36: protected static final Currency USD = Currency.getInstance( "USD" ); japod@36: japod@36: // As in Task1Test, keep in mind, that there are three parts japod@36: // of the whole system: japod@36: // 1. there is someone who knows the current exchange rate japod@36: // 2. there is someone who wants to do the conversion japod@36: // 3. there is the API between 1. and 2. which allows them to communicate japod@36: // japod@36: // Please backward compatibly enhance your existing API to support following japod@36: // usecases: japod@36: // japod@36: japod@36: /** Create convertor that understands two currencies, CZK and japod@36: * SKK. Make 100 SKK == 75 CZK. This is method for the group of users that japod@36: * knows the exchange rate, and needs to use the API to create objects japod@36: * with the exchange rate. Anyone shall be ready to call this method without japod@36: * any other method being called previously. The API itself shall know japod@36: * nothing about any rates, before this method is called. japod@36: */ japod@36: public static Convertor createTripleConvertor() { japod@36: // Rates: 1USD = 15CZK japod@36: // Rates: 1USD = 20SKK japod@36: // Rates: 75CZK = 100SKK japod@36: final TableConvertor convertor = new TableConvertor(); japod@36: final MonetaryAmount amountInUSD = new MonetaryAmount( 1, USD ); japod@36: final MonetaryAmount amountInCZK = new MonetaryAmount( 15, CZK ); japod@36: final MonetaryAmount amountInSKK = new MonetaryAmount( 20, SKK ); japod@36: convertor.putIntoTable( new ConversionRate( amountInCZK, amountInUSD ) ); japod@36: convertor.putIntoTable( new ConversionRate( amountInUSD, amountInCZK ) ); japod@36: convertor.putIntoTable( new ConversionRate( amountInSKK, amountInUSD ) ); japod@36: convertor.putIntoTable( new ConversionRate( amountInUSD, amountInSKK ) ); japod@36: convertor.putIntoTable( new ConversionRate( amountInSKK, amountInCZK ) ); japod@36: convertor.putIntoTable( new ConversionRate( amountInCZK, amountInSKK ) ); japod@36: return new ContractImposingDelegatingConvertor( convertor ).test(); japod@36: } japod@36: japod@36: /** Define convertor that understands three currencies. Use it. japod@36: */ japod@36: public void testConvertorForUSDandCZKandSKK() throws Exception { japod@36: final Convertor c = createTripleConvertor(); japod@36: japod@36: // convert $5 to CZK using c: japod@36: final Convertor.ConversionResult r1 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 5, USD ), CZK ) ); japod@36: final MonetaryAmount a1 = r1.getNetAmount(); japod@36: // assertEquals("Result is 75 CZK"); japod@36: assertNotNull( a1 ); japod@36: assertEquals( 75.0, a1.getAmount().doubleValue() ); japod@36: assertEquals( CZK, a1.getCurrency() ); japod@36: japod@36: // convert $5 to SKK using c: japod@36: final Convertor.ConversionResult r2 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 5, USD ), SKK ) ); japod@36: final MonetaryAmount a2 = r2.getNetAmount(); japod@36: // assertEquals("Result is 100 SKK"); japod@36: assertNotNull( a2 ); japod@36: assertEquals( 100.0, a2.getAmount().doubleValue() ); japod@36: assertEquals( SKK, a2.getCurrency() ); japod@36: japod@36: // convert 200SKK to CZK using c: japod@36: final Convertor.ConversionResult r3 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 200, SKK ), CZK ) ); japod@36: final MonetaryAmount a3 = r3.getNetAmount(); japod@36: // assertEquals("Result is 150 CZK"); japod@36: assertNotNull( a3 ); japod@36: assertEquals( 150.0, a3.getAmount().doubleValue() ); japod@36: assertEquals( CZK, a3.getCurrency() ); japod@36: japod@36: // convert 200SKK to USK using c: japod@36: final Convertor.ConversionResult r4 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 200, SKK ), USD ) ); japod@36: final MonetaryAmount a4 = r4.getNetAmount(); japod@36: // assertEquals("Result is 10 USD"); japod@36: assertNotNull( a4 ); japod@36: assertEquals( 10.0, a4.getAmount().doubleValue() ); japod@36: assertEquals( USD, a4.getCurrency() ); japod@36: } japod@36: japod@36: /** Merge all currency rates of convertor 1 with convertor 2. japod@36: * Implement this using your API, preferably this method just delegates japod@36: * into some API method which does the actual work, without requiring japod@36: * API clients to code anything complex. japod@36: */ japod@36: public static Convertor merge( final Convertor one, final Convertor two ) { japod@36: return new Convertor() { japod@36: japod@36: public ConversionResult convert( ConversionRequest req ) throws IllegalRequestSubtypeException { japod@36: final ConversionResult res1 = one.convert( req ); japod@36: final ConversionResult res2 = two.convert( req ); japod@36: if ( res1.getNetAmount() != null ) { japod@36: if ( res2.getNetAmount() != null ) { japod@36: // TODO check if they arrive at the same thing japod@36: return res1; japod@36: } else { japod@36: return res1; japod@36: } japod@36: } else { japod@36: if ( res2.getNetAmount() != null ) { japod@36: return res2; japod@36: } else { japod@36: // neither converts japod@36: return new ConversionResult( null ); japod@36: } japod@36: } japod@36: } japod@36: japod@36: }; japod@36: } japod@36: japod@36: /** Join the convertors from previous task, Task1Test and show that it japod@36: * can be used to do reasonable conversions. japod@36: */ japod@36: public void testConvertorComposition() throws Exception { japod@36: final Convertor c = merge( japod@36: Task1Test.createCZKtoUSD(), japod@36: Task1Test.createSKKtoCZK() japod@36: ); japod@36: japod@36: // convert $5 to CZK using c: japod@36: final Convertor.ConversionResult r1 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 5, USD ), CZK ) ); japod@36: final MonetaryAmount a1 = r1.getNetAmount(); japod@36: // assertEquals("Result is 85 CZK"); japod@36: assertNotNull( a1 ); japod@36: assertEquals( 85.0, a1.getAmount().doubleValue() ); japod@36: assertEquals( CZK, a1.getCurrency() ); japod@36: japod@36: // convert $8 to CZK japod@36: final Convertor.ConversionResult r2 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 8, USD ), CZK ) ); japod@36: final MonetaryAmount a2 = r2.getNetAmount(); japod@36: // assertEquals("Result is 136 CZK"); japod@36: assertNotNull( a2 ); japod@36: assertEquals( 136.0, a2.getAmount().doubleValue() ); japod@36: assertEquals( CZK, a2.getCurrency() ); japod@36: japod@36: // convert 1003CZK to USD japod@36: final Convertor.ConversionResult r3 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 1003, CZK ), USD ) ); japod@36: final MonetaryAmount a3 = r3.getNetAmount(); japod@36: // assertEquals("Result is 59 USD"); japod@36: assertNotNull( a3 ); japod@36: assertEquals( 59.0, a3.getAmount().doubleValue() ); japod@36: assertEquals( USD, a3.getCurrency() ); japod@36: japod@36: // convert 16CZK using c: japod@36: final Convertor.ConversionResult r4 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 16, CZK ), SKK ) ); japod@36: final MonetaryAmount a4 = r4.getNetAmount(); japod@36: // assertEquals("Result is 20 SKK"); japod@36: assertNotNull( a4 ); japod@36: assertEquals( 20.0, a4.getAmount().doubleValue() ); japod@36: assertEquals( SKK, a4.getCurrency() ); japod@36: japod@36: // convert 500SKK to CZK using c: japod@36: final Convertor.ConversionResult r5 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 500, SKK ), CZK ) ); japod@36: final MonetaryAmount a5 = r5.getNetAmount(); japod@36: // assertEquals("Result is 400 CZK"); japod@36: assertNotNull( a5 ); japod@36: assertEquals( 400.0, a5.getAmount().doubleValue() ); japod@36: assertEquals( CZK, a5.getCurrency() ); japod@36: } japod@36: }