Showing posts with label Java. Show all posts
Showing posts with label Java. Show all posts

Sunday, July 24, 2011

Open Closed Principle

Motivation

A very important consideration when building software is to write code to produce applications which can adapt when new needs or requirements change (.. which for sure they will ) without affecting the rest of the already implemented functionality. Usually, changes in existing code should be minimized since it is assumed that the existing code is already unit tested and that probably it took a long time to get it correct and bug free.

Definition

The open closed principle states that Classes should be open for extension, but closed for modification. The goal of this principle is to allow classes to be easily extended without modifying existing code. Therefore, obtaining designs that are resilient to change and flexible enough to take a new functionality to meet changing requirements.

Example without Open Closed Principle

Let’s say there is a banking application which allows customers to create different types of bank accounts. Below, you can see the class diagram of how this application might look like:

(Click on the image to expand it)

The Account class handles different transactions that a customer might execute depending on his type of account. For example, saving accounts holders allow only withdrawals until the balance is €0.00 but giro accounts provide a credit of €500.00. So the developer writes some code that determines the appropriate type of account and then goes about doing the withdrawals:

   public Account (String name, String type)
{
/* This code is NOT closed for modification
* if new Accounts are introduced or removed
* we have to get into this code and modify it
*
* Bad Times!! :( */

this.type = type;
this.name = name;

if(type.equals("Giro Account"))
withdrawGiroAccount(amount);
else if(type.equals("Saving Account"))
withdrawSavingAccount(amount);
else if(type.equals("Personal Account"))
withdrawPersonalAccount(amount);

}
When you write code like this, you are looking for trouble because you know when it comes time for changes or extensions, you will have to reopen this code and examine what needs to be added or deleted. Often, this kind of code ends up in several parts of the application making maintenance and updates more difficult and error prone.

Clearly, dealing with which concrete class is required is really messing up our Account class and preventing it from being closed for modification. But now that we know that we want the Account class to remain untouched it is time to apply the principle.

Example with Open Closed Principle

The class diagram below shows how this application will be design.

(Click on the image to expand it)

First, we have a client class which will be in charge to create accounts. For this example is just a simple Java class which makes instances of giro, saving or any other type added in the future.

public class AccountTest
{
public static void main(String args[])
{

AccountType giroAccount = new GiroAccount();
AccountType savingAccount = new SavingAccount();

Account account1 = new Account("Cipriano's Account", giroAccount);
Account account2 = new Account("Desiree's Account", savingAccount);

System.out.println("\nTransactions for Cipriano's account");

System.out.println(account1.deposit(1000));
System.out.println(account1.withdraw(4000));
System.out.println(account1.withdraw(3000));
System.out.println(account1.withdraw(1000));

System.out.println("\nTransactions for Desiree's account");
System.out.println(account2.deposit(3000));
System.out.println(account2.withdraw(2000));
System.out.println(account2.withdraw(2000)); }

}
}
We define our Account class, and a constructor which will receive as parameters the owner of the account and the AccountType. Notice that the correct transaction is executed by help of polymorphism since all the new type of accounts will be a subtype of AccountTypes. When we create Account objects in AccountTest we are passing a concrete reference of AccountType (i.e SavingAccount, GiroAccout, etc..) to Account therefore, it will know what kind of transaction execute.
public class Account  {

private AccountType account;
private float amount;

public Account (String name, AccountType type) {
this.account = type;
this.account.setName(name);
}

public String deposit(float amount){
return account.deposit(amount);
}
public String withdraw(float amount){
return account.withdraw(amount);
}
public void setAmount(float amount){
this.amount = amount;
}
public float getAmount(){
return account.getAmount();
}
public String toString(){
return account.getName() + " : " +
account.getAmount() + "€";
}
}
Now is time to define AccountType which will be an abstract superclass of all the new different types of required accounts. It will provide shared common design but it will delegate the withdrawal implementation to concrete classes. It is in here where we are able to create new functionality of accounts without breaking the existing Account implementation. Thus, we will be able to add as many account types with different kind of withdrawal behaviors without even touching the Account class achieving like that the what the Open Closed Principle states. Account will be closed but new types of account might be open to extension providing new implementations of the abstract String withdraw(float amount).
public abstract class AccountType
{
private float amount = 0;
private String name;

public AccountType(){}

public AccountType(String name){
this.name = name;
}

public void setName(String name) {
this.name = name;
}

public String getName(){
return this.name;
}

public void setAmount(float amount) {
this.amount = amount;
}

public float getAmount(){
return amount;
}

public String deposit(float amount){
float current = getAmount();
setAmount(current + amount);

String result = "deposited " + amount + "€" ;
result = result + " for " + getName();
result = result + "\ncurrent balance is: " +
getAmount() + "€";
return result;
}

public abstract String withdraw (float amount);

}

Finally we add our concrete account type classes with their respective withdraw implementations.

public class GiroAccount extends AccountType {

public static final float OVER_DRAFT_LIMIT = 5000f;

public GiroAccount() {
}

public GiroAccount(String name) {
super(name);
}

public String withdraw(float amount) {
float current = getAmount();
float withdrawal;

if (current - amount >= -OVER_DRAFT_LIMIT)
withdrawal = amount;
else
withdrawal = OVER_DRAFT_LIMIT + current;

setAmount(current - withdrawal);

String result = "withdrawn " + withdrawal + "€";
result = result + " from " + getName();
result = result + "\ncurrent balance is: " + getAmount() + "€";

return result;
}

@Override
public String toString() {
return "GiroAccount";
}
}
public class SavingAccount extends AccountType {

public SavingAccount(){}
public SavingAccount(String name){
super(name);
}

@Override
public String withdraw(float amount) {
float current = this.getAmount();
float withdrawal = 0;
String result="";
if(current - amount >= 0){
withdrawal = amount;
setAmount(current - withdrawal);
result = "withdrawn " + withdrawal + "€" ;
result = result + " from " + getName();
result = result + "\ncurrent balance is: " +
getAmount() + "€";
}
else
result = "Insuficient money available";

return result;
}

@Override
public String toString(){
return "SavingAccount";
}
}
Conclusion

The Open Closed Principle is a useful software design consideration when it comes to writing maintainable code and might be used to design applications in which requirements are likely to change. There are many design patterns that allow developers to write extendable code without changing existing implementation. For example the Factory pattern, the Decorator pattern or the Observer pattern.

Be careful when choosing the areas of code that need to be extended; making a flexible design sometimes involves additional complexities and effort. Applying the Open Closed Principle everywhere can lead to complex, hard to understand code.

Wednesday, February 23, 2011

What is a Package?

Packages are namespaces that organize a group of related classes and interfaces. Packages can be thought as folders on your computer system. Many projects might contain html pages in one folder, images in another, scripts, interfaces and so on. Normally, a real world software written in Java contains hundreds or even thousands of Java classes, so it makes sense to keep all these files organized by placing related files into packages. Packages make easier to find related files and use them. As well packages are helpful because they avoid naming conflicts and facilitate control access.

Typical project structure using packages in Eclipse

The previous image depicts a project called Restaurant with 3 different packages: restaurant.api, restaurant.lib and test. And within the packages a set of related classes. One common example of packages can be found in the Java platform itself. In it, classes are included on various packages that bundle classes by function: fundamental classes are in java.lang, classes for reading and writing (I/O) are in java.io and so forth.

Suppose you write a group of classes that represent graphic objects, such as circles, rectangles, lines, and points. You also write an interface, Draggable, that classes implement if they can be dragged with the mouse.

//in the Draggable.java file
package graphics;
public interface Draggable {
. . .
}

//in the Graphic.java file
package graphics;
public abstract class Graphic {
. . .
}

//in the Circle.java file
package graphics;
public class Circle extends Graphic implements Draggable {
. . .
}

//in the Rectangle.java file
package graphics;
public class Rectangle extends Graphic implements Draggable {
. . .
}

//in the Point.java file
package graphics;
public class Point extends Graphic implements Draggable {
. . .
}

//in the Line.java file
package graphics;
public class Line extends Graphic implements Draggable {
. . .
}

Some of the advantages programmers can get by bundling related classes and interfaces in a package include the following :

  • You and other programmers can easily determine that these types(classes & interfaces) are related.

  • You and other programmers know where to find types that can provide graphics-related functions.

  • The names of your types won't conflict with the type names in other packages because the package creates a new namespace.

  • You can allow types within the package to have unrestricted access to one another yet still restrict access for types outside the package.

Thursday, February 17, 2011

What are interfaces?

As it was seen methods (behaviors) in objects define their interaction with the outside world. These method's form the interface which as well can interact with the outside world. Imagine the following scenario: You have been using the previous bike object defined before and you are very familiar with how to speedUp, changeGear, changeCadence, applyBrakes and so on. However, your bike is kind of old and you got a brand new bike. But, it turns out that even when the new bike speeds up, chages gear and applies breaks these methods are named different, so when you try to call the "applyBrakes" method you are used to your new bike simply won't respond because the method name was "bikeBrake", and after that things start getting very ugly.

Interfaces are special contracts that state which operations are performed and how to interact with certain objects. The remote control of your TV is the interface between you and the TV; you just press the turn on button and the TV turns on regardless the brand of the TV, so you don't need to know the details of how to turn on the TV for every kind of vendor; you just know that all TVs have a press turn on button and it will turn the TV on. (Additionally, probably the ACME TV's turn on implementation is different from SUPER TV, but you don't care!).

Software objects also communicate via interfaces. A Java interface describes a set of methods that can be called on an object, to tell the object to perform some task or return some piece of information.

In its most common form, an interface is a group of related methods with empty bodies. A bicycle's behavior, if specified as an interface, might appear as follows:
interface Bicycle {

void changeCadence(int newValue);

void changeGear(int newValue);

void speedUp(int increment);

void applyBrakes(int decrement);
}
To implement this interface, the name of your class would change (to a particular brand of bicycle, for example, such as ACMEBicycle), and you'd use the implements keyword in the class declaration:
class ACMEBicycle implements Bicycle {

// remainder of this class implemented as before

}
Interfaces form a contract between the class and the outside world, and this contract is enforced at build time by the compiler. If your class claims to implement an interface, all methods defined by that interface must appear in its source code before the class will successfully compile.

Interfaces have another very important role in the Java language and is that unlike C++ Java does not support multiple inheritance. However, a class can extend one superclass and implement as many interfaces as needed. This is very useful when you want to share behavior of unrelated objects. This allows objects of unrelated classes to be processed polymorphically, objects of classes that implement the same interface can respond to the same method calls.

This latter benefit of using interfaces will be discussed later, so if it sounds confusing don't bother much for the moment, and think about interfaces as a way of contract between the class and the outside world, and that this contract is enforced at build time by the compiler. If your class claims to implement an interface, all methods defined by that interface must appear in its source code before the class will successfully compile. Remember the example provided, if there was an standard interface between bike manufacturers probably its user would have known how to break because all bikes implementing the interface would have the same methods in this case applyBrakes.

Monday, February 14, 2011

What is inheritance?

Real objects often have a certain amount of common similarities with each other. For example there are many types of Bicycles: mountain bikes, road bikes, tandem bikes and so on. However all of these bicycles share similarities like: current speed, current pedal, cadence, current gear. Yet each also defines additional features that make them different: tandem bicycles have two seats and two sets of handlebars; road bikes have drop handlebars; some mountain bikes have an additional chain ring, giving them a lower gear ratio. But, wouldn't it be great if we could just tell all our bikes to absorb sharing similarities and just worry about the new features the new bike will offer. Well, we can achieve this by using inheritance. (I won't write here about when to use inheritance, best practices, types of inheritance and so on. Hopefully, more on that in future posts, by now just the basic definition)

Object-oriented programming allows classes to inherit commonly used state and behavior from other classes. In this example, Bicycle now becomes the superclass of MountainBike, RoadBike, and TandemBike. In the Java programming language, each class is allowed to have one direct superclass, and each superclass has the potential for an unlimited number of subclasses:



With inheritance, programmers save time during program development by reusing proven and debugged high-quality software. This also increases the likelihood that a system will be implemented effectively.

When creating a class, rather than declaring completely new shared state and behaviors, you can designate that the new class should inherit them of an existing class. The existing class is called the superclass, and the new class is the subclass. (The C++ programming language refers to the superclass as the base class and the subclass as the derived class.) Each subclass can become the superclass for future subclasses.

The syntax for creating a subclass is simple. At the beginning of your class declaration, use the extends keyword, followed by the name of the class to inherit from

class MountainBike extends Bicycle {

//new fields and methods defining a mountain bike would go here

}
In this example this gives MountainBike all the same fields and methods as Bicycle, yet allows its code to focus exclusively on the features that make it unique. This makes code for your subclasses easy to read. However, you must take care to properly document the state and behavior that each superclass defines, since that code will not appear in the source file of each subclass.