From 11dbf563c2345f44bcf45b3a92297e6555c55adc Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 5 Sep 2017 15:11:17 -0400 Subject: [PATCH] Kotlin - Bank Account Complete --- kotlin/bank-account/README.md | 33 +++++++++ .../src/main/kotlin/BankAccount.kt | 40 +++++++++++ .../src/test/kotlin/BankAccountTest.kt | 69 +++++++++++++++++++ 3 files changed, 142 insertions(+) create mode 100644 kotlin/bank-account/README.md create mode 100644 kotlin/bank-account/src/main/kotlin/BankAccount.kt create mode 100644 kotlin/bank-account/src/test/kotlin/BankAccountTest.kt diff --git a/kotlin/bank-account/README.md b/kotlin/bank-account/README.md new file mode 100644 index 0000000..ab233b3 --- /dev/null +++ b/kotlin/bank-account/README.md @@ -0,0 +1,33 @@ +# Bank Account + +Simulate a bank account supporting opening/closing, withdrawals, and deposits +of money. Watch out for concurrent transactions! + +A bank account can be accessed in multiple ways. Clients can make +deposits and withdrawals using the internet, mobile phones, etc. Shops +can charge against the account. + +Create an account that can be accessed from multiple threads/processes +(terminology depends on your programming language). + +It should be possible to close an account; operations against a closed +account must fail. + +## Instructions + +Run the test file, and fix each of the errors in turn. When you get the +first test to pass, go to the first pending or skipped test, and make +that pass as well. When all of the tests are passing, feel free to +submit. + +Remember that passing code is just the first step. The goal is to work +towards a solution that is as readable and expressive as you can make +it. + +Have fun! + + + + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. diff --git a/kotlin/bank-account/src/main/kotlin/BankAccount.kt b/kotlin/bank-account/src/main/kotlin/BankAccount.kt new file mode 100644 index 0000000..84c5465 --- /dev/null +++ b/kotlin/bank-account/src/main/kotlin/BankAccount.kt @@ -0,0 +1,40 @@ +import kotlin.reflect.KProperty + +class BankAccount() { + var balance: Int by Delegate() + private var isClosed = false + internal val lock = Any() + + fun adjustBalance(x: Int): Unit { + synchronized(lock) { + if (!isClosed) { + balance += x + } else throw IllegalStateException() + } + } + + fun close(): Unit { + isClosed = true + } + + class Delegate() { + private var balance = 0 + operator fun getValue(thisRef: BankAccount, property: KProperty<*>): Int { + synchronized(thisRef.lock) { + if (thisRef.isClosed) { + throw IllegalStateException() + } + return balance + } + } + + operator fun setValue(thisRef: BankAccount, property: KProperty<*>, value: Int) { + synchronized(thisRef.lock) { + if (thisRef.isClosed) { + throw IllegalStateException() + } else balance = value + } + } + } + +} \ No newline at end of file diff --git a/kotlin/bank-account/src/test/kotlin/BankAccountTest.kt b/kotlin/bank-account/src/test/kotlin/BankAccountTest.kt new file mode 100644 index 0000000..dfe5335 --- /dev/null +++ b/kotlin/bank-account/src/test/kotlin/BankAccountTest.kt @@ -0,0 +1,69 @@ +import org.junit.Ignore +import org.junit.Test +import java.util.* +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlinx.coroutines.experimental.* + +class BankAccountTest { + + @Test + fun zeroBalanceWhenOpened() { + val account = BankAccount() + assertEquals(0, account.balance) + } + + @Test + fun sequentialBalanceAdjustments() { + val account = BankAccount() + + account.adjustBalance(1000) + assertEquals(1000, account.balance) + + account.adjustBalance(-958) + assertEquals(42, account.balance) + } + + @Test + fun closedAccountHasNoBalance() { + val account = BankAccount() + account.close() + + assertFailsWith(IllegalStateException::class, { account.balance }) + } + + @Test + fun closedAccountCannotBeAdjusted() { + val account = BankAccount() + account.close() + + assertFailsWith(IllegalStateException::class, { account.adjustBalance(1000) }) + } + + @Test + fun concurrentBalanceAdjustments() { + val threads = 1000 + val iterations = 500 + val random = Random() + + val account = BankAccount() + + val jobs = List(threads) { + launch(CommonPool) { + repeat(iterations) { + account.adjustBalance(1) + delay(random.nextInt(10).toLong()) + account.adjustBalance(-1) + } + } + } + + runBlocking { + jobs.forEach { it.join() } + } + + assertEquals(0, account.balance) + } + +} +