vneuron.com
Interface Oriented Programming - Vneuron
Introduction Writing software is easy, maintaining it is hard. The best method to develop maintainable software is to write a good structured and architectured code. This article talks about some programming patterns related to interfaces that help reduce complexity and develop a better design of the software architecture. Interface Oriented Programming is not a programming paradigm, it is simply object oriented programming, but encouraging the use of interfaces instead of classes in various situations. We start by defining briefly object oriented programming, the key differences between classes and interfaces, and demonstrating that in some situations programming with interfaces offers more flexibility on the long term than programming with classes. I. Object Oriented Programming Object Oriented Programming is a programming paradigm which consists of solving a problem by creating a set objects. Some objects can inherit other ones (a cat **is** an animal) and other objects are composed of others as well (a cat **has** an owner). This programming paradigm was able to solve real world problems and helped to reuse some good portion of source code. Java came out back in 1996 and introduced **interfaces**, a new keyword in the world of object oriented programming. They were invented from the need of separating classes from protocols. By protocol we mean a class that has some certain method or **behavior**. II. Class vs Interface We can define an interface as a particular behavior (or a set of behaviors). In other words, declaring an interface is like saying: > I want something that does `X`, `Y`and `Z`, I don’t care how. A class refers to a more concrete behavior: > I want something that does `X` in this particular way. We may argue that classes share few semantics with interfaces, for example abstract classes, but we will ignore this fact for now. An interface is **abstract**, a class is **concrete**. A class **implements** an interface, meaning it give the prototype a body and shape so it can actually be used. Generally speaking, writing to interfaces allows the code to be generic, reusable across various implementations. Writing to classes kills these features as the code will be bound to this particular implementation. Your job is to write software and at some point you are going to maintain it and be responsible for its evolution. The code will inevitably evolve and the software will get bigger and bigger, in terms of size and functionnalities. Therefore, it is imperative to write generic and bug-free code. A wise use of interfaces can help achieve these requirements. III. Use case Let’s consider the case of a multi-threaded application that connects to a database. Each thread will need to execute a simple query that returns no `ResultSet`. One simple way is passing the `Connection` object to each thread. “`java class MainConnectionClass { /* Our SQL Connection Object */ Connection connectionObj; public Connection getConnectionObj() { return conenctionObj; } } class Consumer implements Runnable { /* Unowned Connection */ private static String QUERY_FORMAT = "SELECT * FROM USER WHERE id = %s;" private Connection connectionObj; public Consumer(Connection connectionObj) { this.connectionObj = connectionObj; } @Override void run() { /* .. */ String cmd = String.format(cmd, "1552"); boolean result = false; try { Statement stmt = connectionObj.createStatement(); log.info("Executing: "+cmd); boolean result = stmt.execute(cmd); } catch (SQLException sqlException) { log.fatal(sqlException); } /* .. */ } } public class App { public static void main(String args[]) { MainConnectionClass mainConn = new MainConnectionClass(); /* .. */ for(int i = 0; i < 100; i++) { Consumer consumer = new Consumer(mainConn.getConnectionObj()); (new Thread(consumer)).start(); } } } ``` This simple code has a lot of issues. First, we cannot define or identify who is the true owner of the `Connection`. It exists everywhere. It may seem obvious because the code was explained, but in other scenarios, the owner and the life cycle of the object is not visible and requires additional inspections to be figured. Additionally, multiple objects will have a reference to the `Connection` and this pattern **will** inevitably lead to side effects. Luckily, in java, the `Connection` object is thread-safe, but some issues may still be raised if, for instance a thread closes the Connection object while other thread have not executed their queries yet. In such case, who is responsible for handling this issue (say reporting the problem or logging the exception) and even better, how can we avoid such complications? Simply, give every object what it needs, and **only what it needs**. If the `Consumer` class needs to execute some queries, make it someone else’s problem. The `Consumer` does not have to handle all the complications of holding references to objects it does not own, messing around with these objects, creating statements and query operations, and handling errors in a very bad fashion. It only needs something that can execute SQL queries, so give it **only** that thing. One good idea would be to create an interface `QueryExecutor` which it does what its names mentions, execute queries. One good prototype would be: “`java interface QueryExecutor { public Boolean executeQuery(String query); } ``` And it can simply be implemented by `MainConnectionClass` as follows: ```java class MainConnectionClass implements QueryExecutor { /* Our SQL Connection Object */ private Connection connectionObj; @Override public Boolean executeQuery(String query) { try { Statement stmt = connectionObj.createStatement(); log.info("Executing: "+cmd); return stmt.execute(cmd); } catch (SQLException sqlException) { log.fatal(sqlException); return false; } } } ``` Our consumer class will require an object that implements `QueryExecutor` so it can execute queries: “`java class Consumer implements Runnable { /* unowned Connection */ private static String QUERY_FORMAT = "SELECT * FROM USER WHERE id = %s;" private QueryExecutor executor; public Consumer(QueryExecutor executor) { this.executor = executor; } @Override void run() { /* .. */ String cmd = String.format(cmd, "1552"); Boolean res = executor.executeQuery(cmd); /* .. */ } } ``` From now on, every created thread takes the `MainConnectionClass` as a parameter, thus getting the only behavior it needs from the class, which is `executeQuery`. Now, it is trivial to understand who is the owner of …