Interfacing in Java/Kotlin
What, How & Why Interfacing in Java/Kotlin, by following Practical Example.
Table of contents
If get to know something new by reading my articles, don't forget to endorse me on LinkedIn
Interface - What, Why & How?
What: An interface in Java/Kotlin is a blueprint of a class. It has static constants and abstract methods. The interface in Java/Kotlin is a mechanism to achieve abstraction.
Why:
To achieve abstraction.
To support the functionality of multiple inheritance.
To achieve loose coupling.
Easier to test.
Increase flexibility.
Dependency Injection.
How: An interface is declared by using the interface keyword. It provides total abstraction; means all the methods in an interface are declared with the empty body, and all the fields are public, static and final by default. A class that implements an interface must implement all the methods declared in the interface.
Practical Example
Let's think about a scenario, we have a Book Store System, where we can save a new book record and search for books by following by Author Name only.
data class Book(val id : Int, val name : String, val author : String, val serialNumber : String)
interface BookStore{
fun addNewBook(book: Book): Boolean
fun searchBook(authorName : String): MutableList<Book>
}
We have a model call Book which has some attributes (Id, Name, Author, Serial Number).
We have our Interface called BookStore, which expose some public APIs (or Simply, Objects Behaviour) to the client.
Here comes the benefits of Interfacing. Client can access only the interface, but not the actual implementation of the system. Which provide security benefits and according to good coding approach, client shouldn't know about the underlying implementation of a system.
Now, let's take a look at the implementation of BookStore.
class DefaultBookStore() : BookStore {
companion object {
private val store: MutableList<Book> = mutableListOf()
}
override fun addNewBook(book: Book): Boolean {
if (!store.contains(book)) {
store.add(book)
return true
}
return false
}
override fun searchBook(authorName: String): MutableList<Book> {
val filteredBooks = mutableListOf<Book>()
store.forEach {
if (it.author.contains(authorName, true)) {
filteredBooks.add(it)
}
}
return filteredBooks
}
}
DefaultBookStore
is the impl ofBookStore
.DefaultBookStore
contain a static list (MutableList
), where all books are stored.addNewBook(book: Book): Boolean
is designed to store a new book to the list if it's not stored already in the system and returnBoolean
to verify if book is added or not.searchBook(authorName: String): MutableList<Book>
should return a list ofBook
, where filtered books are searched based on query (Author Name).
But we can can't expose the Implementation of the System, instead we will provide the interface BookStore
to the client.
Now, lets create a Factory
class who is responsible to provide an instance of BookSystem
.
object BookStoreFactory {
private val defaultBookStore: BookStore = DefaultBookStore()
fun getDefaultBookStore(): BookStore {
return defaultBookStore
}
}
BookStoreFactory
expose a public API to get the instance ofBookStore
.getDefaultBookStore(): BookStore
return an instance ofBookStore
, in our case itsDefaultBookStore
where all business logic is defined.
Unit Tests
class BookStoreSystemTest {
private var bookStore: BookStore? = null
@Before
fun setup() {
bookStore = BookStoreFactory.getDefaultBookStore()
}
@Test
fun `get default book store from factory`() {
assert(bookStore != null)
}
@Test
fun `add some books to the system`() {
assert(bookStore?.addNewBook(Book(1, "Test Book 1", "Romman Sabbir", "12")) == true)
assert(bookStore?.addNewBook(Book(2, "Test Book 2", "Test Author", "1243")) == true)
assert(bookStore?.addNewBook(Book(3, "Test Book 3", "Forhad An Naim", "12434")) == true)
assert(bookStore?.addNewBook(Book(4, "Test Book 4", "Romman Sabbir", "12434re")) == true)
}
@Test
fun `search books by author`() {
val searchedBooks = bookStore?.searchBook("romman") ?: mutableListOf()
assert(searchedBooks.isNotEmpty())
}
@After
fun tearDown() {
bookStore = null
}
}
Result:
โ
Tests passed: 3 of 3 tests - 20 ms
Recap
Interfacing helps to provide abstraction to the system.
Interface expose only behaviour not business logic.
Interface helps to implement multiple inheritance.
Easy to test an object behaviour.
Increase code readability and helps to maintain proper codebase.
Happy Coding...