CSC/ECE 517 Fall 2011/ch4 4h kp

From Expertiza_Wiki
Jump to navigation Jump to search

Introduction

This wiki article discusses about some of the commonly used & easy to understand Desgin patterns[1] in the software industry. Specifically, we will be studying about the Singleton, Adapter, Command & Strategy patterns.

Design Pattern

Definition

Some of the common definitions available online are as follows.

  • "A design pattern is a repeatable solution to a software engineering problem. Unlike most program-specific solutions, design patterns are used in many programs. Design patterns are not considered finished product; rather, they are templates that can be applied to multiple situations and can be improved over time, making a very robust software engineering tool. Because development speed is increased when using a proven prototype, developers using design pattern templates can improve coding efficiency and final product readability." -Techopedia <ref>Techopedia explains Design Pattern</ref>

In a layman's term, Design Patterns are

  • descriptions of what experienced designers know.
  • hints for choosing classes & methods
  • higher order abstractions for program organization
  • used to weigh design tradeoffs
  • used to avoid limitations of implementation language. <ref>Design Patterns in Dynamic Languages - Peter Norvig</ref>

Examples

There are various types of Design Patterns, viz creational patterns, structural patterns, and behavioral patterns, each of which consists of numerous examples. But that is not the scope of our discussion. So, let us limit ourselves to the following 4 pattern examples.

  1. Singleton pattern (type of Creational pattern).
  2. Adapter pattern (type Structural pattern).
  3. Command pattern (type of Behavioral pattern).
  4. Strategy pattern (type of Behavioral pattern).

Case Study

1. SINGLETON PATTERN

Definition

Singleton is a design pattern which imposes a restriction on the class to instantiate exactly one object of that class.

Implementation

  • In Java, We can create a thread-safe & lazy version of Singleton as follows. The comments in the code help you understand the Lazy & thread-safe aspect of it. Notice that the constructor is "private". So external classes, cannot instantiate the object of class "Singleton" directly. The only way to get an instance of this class is by using the getInstance() method.
public class Singleton {
        private static Singleton instance;
 
        private Singleton() { //Notice that the constructor is private.
                //Do nothing. Initialize the object only when the first time getInstance() is called.
        }
 
        public static synchronized Singleton getInstance() { 

        //The keyword "synchronized" makes it thread safe so that two threads invoking getInstance()  
        //at the same time cannot create two instances when a context switch happens so as to 
        //facilitate this scenario. In general this can happen even with 'n' threads invoking getInstance().
        //"synchronized" keyword is used to eliminate such scenarios.

                if (null == instance) {
                        instance = new Singleton();
                }
                return instance;
        }
}


  • In Ruby, we can achieve singleton classes by mixing-in the 'Singleton' module as follows. Here, "SingletonClass" is the name of the singleton class that is being created.
require 'singleton'
class SingletonClass
    include Singleton
    ...
end

When should we use Singleton ?

There is a lot of criticism to the use of singleton pattern. So, how do we decide whether an object should be singleton or not ? For an object to be considered as a singleton object, they must satisfy three requirements:

  1. controls concurrent access to a shared resource.
  2. access to the resource will be requested from multiple, disparate parts of the system.
  3. there can be only one object. <ref>When should we use Singleton ?</ref>

Applications

  1. You might want to have only one Window Manager to manage all your open windows in your system.
  2. Logging, where it provides a global logging access point in all the application components without being necessary to create an object each time a logging operations is performed. <ref>Logger Classes</ref>

Advantages

  • Singletons are often preferred to global variables as they don't pollute the global namespace with unnecessary variables & also can be instantiated only when needed unlike global variables which always consume resources.

Drawbacks

  • Introduces a global state into the application.
  • Can make unit testing classes in isolation difficult.

Checkout the this video from Misko Hevery which explains in detail when the usage of singletons is not exercised.<ref>Why Singletons are bad</ref>

2. ADAPTER PATTERN

“If it walks like a duck and quacks like a duck, then it might be a turkey wrapped with a Duck Adapter.”
Definition

The Adapter Pattern converts the interface of a class into another interface the clients expect. Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces. - Head First Design Patterns <ref>[Freeman, Elisabeth, Bert Bates, and Kathy Sierra. Head First Design Patterns. Web.]</ref>

Overview
  • Adapter pattern involves wrapping objects to make their interfaces look like something they are not. This enables us to adapt a design expecting one interface to a class that implements a different interface.
  • Real World Adapters - The name “Adapter Design Pattern” is derived from the fact that the design pattern exactly mirror the behavior of a real world adapter, but in an Object Oriented Manner. Consider a normal use-case of an Adapter in real world. If you own a laptop manufactured in the US and want to connect it to a wall-socket plug in Europe, you will not be able to do so. The wall socket plug in Europe exposes an AC outlet and the US laptop charger is not designed to work with that directly. So the Adapter sits between the plug of your laptop and the European wall-socket outlet. Its job is to adapt the European outlet so that you can plug your laptop to it and receive power.
  • On the similar lines, consider that you have an existing software system and you want to make it work against some new third-party vendor class library. Now this new vendor might have designed their library such that the interface exposed by it is different that the previous vendor library against which you have the working software system. Changing your existing code to match this new vendor interface might prove to be a cumbersome and high-cost operation. Also if you decide to change the library sometime again the future, you would have to do the same changes again. Enter Adapter Design Pattern.
  • The Adapter Design Pattern acts as the middleman by receiving requests from the client and converting them into requests that make sense to the vendor classes.

Pictorially, this can be depicted as follows -

Example usage of Adapter Pattern
  • Third Party Libraries: Wrappers are used to adopt 3rd parties libraries and frameworks - most of the applications using third party libraries use adapters as a middle layer between the application and the 3rd party library to decouple the application from the library. If another library has to be used only an adapter for the new library is required without having to change the application code.
  • Wrapper Functions in Java: in java there is wrapper functions for primatives. For example the Integer class is a wrapper class for the primitive int
  • An example would be where a square class can be wrapped so that it can be used as a rectangle also.
  • Transformation of format of Dates: "Transforming the format of dates. YYYY-MM-DD to MM/DD/YYYY or DD/MM/YYYY"
Conceptual Work-out of the Adapter Pattern
  • Suppose the object that is going to be making the requests is called as “Client”. The Client is implemented expecting some interface.
  • Let “Adaptee” be an object that can perform the actions that the Client is going to request. However, the interface implemented by the “Adaptee”, that is the methods or even method signatures exposed by the Adaptee differ than what the Client expects.
  • Now, to make Client and Adaptee work together, we use the Adapter Design Pattern. This Adapter will need to implement the interface expected by the Client. Let us call this interface as the “target interface.” So the methods exposed by the Adapter interface conform to what the Client expects. The Adapter will also hold an instance of the “Adaptee.” This will ensure that Adapter can call the methods of this instance of Adaptee. Thus, the classic implementation of Adapter Pattern is using the Object-Oriented concept of “Composition”

Pictorially, it can be viewed as follows -

Coding Example - Adapter Pattern in Java
  • We will use the standard Duck-Turkey example. Suppose, there is an interface “Duck” which exposes methods “quack” and “fly”. Let us assume that there is an interface “Turkey”, exposing “gobble” and “fly”. Let us also assume that the “fly” supported by Turkey is not the same as the “fly” supported by the Duck. (Turkey can not fly as high as a duck can or something on those lines.)
public interface Duck {
       public void quack();
       public void fly();
}
public interface Turkey {
       public void gobble();
       public void fly();
}
  • Suppose, there is a ‘MallardDuck’ class that implements Duck Interface.
public class MallardDuck implements Duck {
   public void quack() {
       System.out.println(“Quack\n”);
   }
   public void fly() {
      System.out.println(“Fly\n”);
   }
}
  • Now, suppose enough number of Objects of “MallardDuck” are not present and therefore we want some Turkeys to be adapted to support Duck Interface. This means that a Client will fire method invocations expecting a Duck Interface. Hence, ‘Duck’ is our target interface. As explained in the previous section, the Adapter would need to implement the target interface. This should internally call Turkey’s methods. An instance of Turkey will be passed to this Adapter.
public class TurkeyAdapter implements Duck {
    Turkey turkey;
    public TurkeyAdapter(Turkey turkey) {
         this.turkey = turkey;
    }
    /* quack method calls the gobble method of the Turkey*/
    public void quack() {
        turkey.gobble();
    }
    public void fly() {
       turkey.fly();
    }
}
  • Testing that Adapter works: Suppose, there is a function testDuck which calls the “quack” and “fly” methods of the “Duck” passed to it. Following code creates a WildTurkey object. Passes that object to an instance of TurkeyAdapter class specified above. The corresponding TurkeyAdapter class is then passed to the testDuck method.
static void testDuck(Duck duck) {
   duck.quack();
   duck.fly();
}
WildTurkey turkey = new WildTurkey();
Duck turkeyAdapter = new TurkeyAdapter(turkey);
testDuck(turkeyAdapter);
  • In this way, adapter pattern allows us to use a client with an incompatible interface by creating an adapter that does the conversion. This results in decoupling the client from the implemented interface.
  • However, if we expect the interface to change over time, the adapter encapsulates that changes so that the client doesn’t have to be modified each time it needs to operate against a different interface.
  • This is possible because the Adapter pattern binds the client to an interface and not an implementation. We could use several adapters each converting a different backend set of classes as long as they adhere to the same target interface.
Coding Example - Adapter Pattern in Ruby
  • Let us elaborate on the same example that was used in the lecture. Suppose we have classes SquarePeg, RoundPeg and RoundHole. RoundHole class supports a method peg_fits? which expects a “peg” object as an input. The method determines if the peg fits in it or not based on if the radius of the peg passed as an argument is smaller than the radius of the RoundHole. Now since SquarePeg does not have a radius, we will need an Adapter that converts the “width” attribute of SqurePeg into radius and then checks if that SquarePeg can fit into the RoundHole or not.
class SquarePeg
  attr_reader :width
  def initialize(width)
 	@width = width
  end
end
class RoundPeg
  attr_reader :radius
  def initialize(radius)
 	@radius = radius
  end
end
class RoundHole 
  attr_reader :radius
  def initialize(r)
     @radius = r
  end
  def peg_fits?(peg)
     peg.radius <= radius
 end
end

Here is the Adapter class:

class SquarePegAdapter
  def initialize(square_peg)
     @peg = square_peg
  end	
  def radius
     Math.sqrt(((@peg.width/2) ** 2)*2)
  end
end
hole = RoundHole.new(4.0)
4.upto(7) do |i|

peg = SquarePegAdapter.new(SquarePeg.new(i.to_f)) if hole.peg_fits?( peg )

   	puts "peg #{peg} fits in hole #{hole}"

else

   	puts "peg #{peg} does not fit in hole #{hole}"

end end

Object Adapters vs. Class Adapters
  • All the examples of Adapter that we have seen so far are the Object Adapters.
  • Implementing Class Adapter is tricky since it requires multiple inheritance support in the programming language.
  • In case of Object Adapters, we use “Composition” i.e. the Adapter holds an instance of Adaptee. In the case of Class Adapters, we use “Inheritance” i.e. the Adapter class is inherited from both Target class and the Adaptee class.
  • The Class Adapter is thus committed to a specific adaptee class. But the hidden advantage behind this is that it does not require to re-implement the entire Adaptee class. There is also the fact that a Class Adapter can just override the methods of Adaptee is it wants to.
  • Therefore, Class Adapters are not as flexible as Object Adapters.

3. COMMAND PATTERN

"Command Pattern equals Encapsulating Method Invocations"

Definition

"By encapsulating method invocation, we can crystallize pieces of computation, so that the object invoking the computation doesn't need to worry about how things are done. It just uses the crystallized method to get things done" - Head First Design Patterns <ref>[Freeman, Elisabeth, Bert Bates, and Kathy Sierra. Head First Design Patterns. Web.]</ref>

Overview

The command pattern allows one to decouple the requester of an action from the object that actually performs the action.

This decoupling is achieved by introducing a command object which encapsulates the request to do something on an object i.e.the action to be taken or the methods to be invoked and exposes these via a single API. So the requester will only need to invoke this well exposed and simple API of the command object and the implementation of this API will contain calls to the methods of the object on which actual actions are to be done. <ref>Design Patterns in Java</ref> It achieves two things - One, the requester object code can remain simple. It does not need to take into consideration the details about how the action is going to be performed, it will only need to worry about the API exposed by the Command Object corresponding to the object whose actions are to be invoked. And two, the object on which the actions are invoked need not worry about who invoked them. The only "route" via which they would have been invoked is via the Command object specific to this object. <ref>Command Design Pattern</ref>

Example Usage of Command Pattern

  • A calculator with unlimited number of undo’s and redo’s
  • "Thread pools” - A typical, general-purpose thread pool class might have a public addTask() method that adds a work item to an internal queue of tasks waiting to be done. It maintains a pool of threads that execute commands from the queue. <ref>Command Pattern</ref>
  • Say we have designed an image editor and user can have the option of opening file from various ways like menu, tool bar, double click on a file in the explorer. The solution is the command pattern where the FileOpen command is associated in the viewer itself and when the command executes the file is shown in the viewer.

<ref>Design Patterns-codeproject</ref>

Conceptual work-out of the Command Pattern

  • Let us assume that there is an object "Client" which needs to invoke some action of Object "Receiver".
  • Let us also assume that this "action" is a cumulative computation and it might involve invoking multiple methods of Receiver object.

In absence of Command Pattern, "Client" will invoke "Receiver's" methods via code statements like receiver.method1(), receeiver.method2() etc. This would mean that Client has to know exactly which methods and in which sequence are required to be called for the specified action to take place. Thus, it implies a higher level of coupling and hence a dependency between Client and Receiver objects. However, using a Command object specific to the Receiver object will help in decoupling this dependency.

  • The Command object will only provide a single method, say "execute()" and this method will encapsulate all the method invocations of Receiver object. In this way, the methods of Receiver object for executing an action and the Receiver object itself are bound together in the Command object. <ref>http://www.alexmcferron.com/command.html</ref>
  • The Client object will then create the Command Object and pass it to an intermediate "Invoker" object where it will get stored.
  • At some point in the future, the Invoker object will invoke the "execute()" method of the Command Object passed to it, which in turn will culminate in calling all the methods of Receiver Object that are required to be called for the original specified action to take place.

Pictorially, this can be visualized as follows -

<ref>http://www.allapplabs.com/java_design_patterns/command_pattern.html</ref>

Coding example: Command Pattern in Java

This example is inspired from the example given in Head First Design Patterns

Suppose there is a "RemoteControl" that operates lights and other such devices in a house. The remote might have on and off buttons for every light and pressing those buttons might only invoke "on" and "off" actions on those lights. However what if the same Remote is to be used for controlling a GarageDoor? GarageDoor will not have on and off methods but "up" and "down" methods. One way around this would be including "if" statements in RemoteControl code that checks if the device being controlled is a GarageDoor, then invoke "up" instead of "on". However, this is not a robust method. If you decide to support a new device in the future, more "else if" statements will get added. The RemoteControl logic will become complex and it will be closely coupled with all the devices. This does not exhibit good Object Oriented design. However, using Command Pattern will ease out the problem. We can have one Command Object per device and specifically for each action on each device.

This command object's execute() method will encapsulate calls to "on" or "off". This achieves two things. One, the RemoteControl code can now be simple. It will only invoke "execute()" method of whichever device is being controlled. Also adding support of new devices in the future becomes easy.

Java provides an Interface called "Command". It supports only one method, "execute()" The Command Interface can be follows

public interface Command {
   public void execute();
};

Now, there will be a GarageDoorOpen command object that should implement the Command Interface.

public class GarageDoorOpenCommand implements Command {
   GarageDoor garagedoor;
   public GarageDoorOpenCommand(GarageDoor garagedoor) {
      this.garagedoor = garagedoor;
   }
   /* execute method encapsultes the call to "up" */
   public void execute() {
      garagedoor.up();
   }
}

Now remains the client and Invoker classes. Our Invoker class should only call execute method on whatever Command Object is passed to it. Also, the Client object should create a Command object, pass it to Invoker object.

public class RemoteControlInvoker {
    Command cmd;
    public void setCommand(Command cmd) {
       this.cmd = cmd;
    }
    public void buttonPressed() {
       cmd.execute();
    }
    /* The Invoker object will only call execute on the command object to which its local   
       Command object was set to. 
    */
}
public class RemoteControl {
    public static void main(String[] args) {
        RemoteControlInvoker rmtInvoker = new RemoteControlInvoker();
        GarageDoor garageDoor = new GarageDoor();
        GarageDoorOpenCommand gdOpenCmd = new GarageDoorOpenCommand(garageDoor);
        rmtInvoker.setCommand(gdOpenCmd);   //Pass the created Command Object to Invoker
        rmtInvoker.buttonPressed();         //Let the Invoker invoke action on the Receiver GarageDoor
    }
}

In this way, the Command object encapsulates a request by binding together a set of actions on a specific receiver. It does this by packaging the actions and the receiver and exposing just one method - execute(). From the outside, no other objects really know what actions get performed on a receiver, they just know that by calling "execute()", their request will be processed.

Coding example: Command Pattern in Ruby

In Ruby, because of the presence of "Closures", implementing a Command Pattern is a fairly straightforward. <ref>Design Patterns in Ruby</ref>

Formally, a closure is basically a function or a method (or even a block, in the context of Ruby) that has the following twp properties:

  • It is a first-class function, meaning it can be passed around like an object or as a value parameter to other functions/methods.blocks
  • It saves the lexical environment, meaning it captures the variables within scope at its creation and maintains them even if they later go out of scope.

<ref>Wiki Chapter On Closures</ref>

The “Proc” class in Ruby supports a method “call” by which the block of code saved in the Proc class can be “called” and executed. In this scenario, the Proc class (which might be an output of the ‘lambda’ function) can be considered as an equivalent of the “invoker” class and the block of code passed to it to be the “Command” object.

However, using Procs for implementing Command Pattern is not applicable always. Whenever it is required to pass commands between address spaces or save commands to a file and later load them into a Ruby program. Proc objects can not be stored or restored using marshal module. Therefore, for following two reasons, using Proc objects is not enough for Command Pattern -

  • When command objects need to be Persistent
  • When command objects need to be sent to a remote objects.

<ref>http://blog.ashwinraghav.com/2011/03/14/design-patterns-in-ruby-composite-iterator-commandpart-2/</ref>

In such cases, one would have to explicitly implement Command objects via “Command” class.

Below is an example of Command Pattern in Ruby using an explicit class and not using the Proc class. In this example, “CheckMemory” extends Command class and the Installer class supports “install”, “uninstall” methods. Installer class need not worry about the instance of class passed to the “add_command” method. <ref>Ruby Design Patterns</ref>

class Command
   def initialize(description, proc1, proc2)
      @description = description
      @function =  proc1
      @inverse = proc2
   end
   def execute
      @function.call unless @function.nil?
   end
   def unexecute
      @inverse.call unless @inverse.nil?
   end
end
class CheckMemory < Command ##ireversible
   def initialize
      proc1 = Proc.new do
        puts "Checking Memory..."
      end
      proc2 = Proc.new do
        puts "Mem Check need not be reversed"
      end
      super("Checking memory avalability", proc1, proc2)
   end
end
class Installer
   def initialize
      @commands = []
   end
   def add_command command
      @commands << command
   end
   def install
      @commands.each {|c| c.execute}
   end
   def uninstall
      @commands.reverse.each do |command|
         command.unexecute
      end
   end
end
installer = Installer.new
installer.add_command(CheckMemory.new)
installer.add_command(CreateFile.new)
installer.installinstaller.uninstall

Advanced Command Pattern Features

Supporting Undo operation

Providing "undo" functionality to a software is an important feature addition. Many softwares like editors need to keep track of previous actions performed and then let user undo some or all of them on demand. The Command Pattern lends itself in a very nice manner for implementing such functionality. <ref>http://stackoverflow.com/questions/2214874/implementing-the-command-pattern</ref>

When Commands support undo, they have an undo() method that mirros the execute() method. Whatever execute() last did, undo() reverses it. In the above code example, the Command Interface would need to have a method undo and the GarageDoorOpenCommand class will need to implement it. It can just have a call to "down" method on the GarageDoor. In more general sense, we would need to have a Command object that always points to the last Command that was executed. Whenever an undo action needs to be done, the undo() method of object to which this command object is pointing to would be called. <ref>Undo By a Command</ref>

The "NoCommand" Object

The NoCommand object is an example of null object. A null object is useful when there is no meaningful object to return and the responsibility of handling null objects is not adviced. <ref>http://sourcemaking.com/design_patterns/command</ref>

The "Meta" Command Pattern

Meta Command Pattern allows us to create macros of Command objects so that multiple actions can be executed via a single call. This means that the execute() method of a MetaCommand Object will involve a series of calls of "execute()" methods of other Command Objects. <ref>http://www.voelter.de/data/pub/metacommand.pdf</ref>

4. STRATEGY PATTERN

Definition

"In computer programming, the strategy pattern is a design pattern, whereby algorithms can be selected at runtime. Formally speaking, the strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it." - Wikipedia <ref>Stratey Pattern Wikipedia</ref>

Example code for Strategy pattern

Here we will see how strategy pattern can be used to sort a given list of real numbers using either BubbleSort() or QuickSort().

Below is an interface to describe the individual algorithms.

public interface SortingInterface {
      public void sort(double[] list);
}


Now implement both the Quicksort & Bubblesort strategies.

public class QuickSort implements SortingInterface {
      //QuickSort will implement the sort() with the quicksort version.
      public void sort(double[] list) {
             //Do quick sort here
      }
}


public class BubbleSort implements SortingInterface {
      
      //BubbleSort will implement the sort() with the bubblesort version.
      public void sort(double[] list) {


            //Do bubble sort here  
      }
}


Now define a Strategy context that maintains a reference to a Strategy object(Bubblesort or Quicksort) and forwards client requests to that strategy.

public class Context {
private SortingInterface algorithm = null;

public void sortGivenList(double[] list) {
       algorithm.sort(list);
}

//Getter method for algorithm
public SortingInterface getAlgorithm() {
       return algorithm;
}

//Setter method for algorithm
public void setAlgorithm(SortingInterface algorithm) {
       this.algorithm = algorithm;
}

}


Finally, the client picks one of the available strategies(Quicksort or Bubblesort) in the context and sorts the List according to that strategy as shown below.

public class Client {

      public static void main(String[] args) {
      
      //Here is the List
      double[] list = {1,2.4,7.9,3.2,1.2,0.2,10.2,22.5,19.6,14,12,16,17};

      //Now define the Context
      Context context = new Context();

      //Now pick your strategy i.e. Quicksort or Bubblesort
      context.setAlgorithm(new BubbleSort());

      ///Now, sort it using the strategy you picked
      context.sortGivenList(list);
      
 }

Applications

Differences between Command and Strategy Pattern

  • A Command Pattern encapsulates a single action/algorithm whereas the Strategy pattern provides us with a number of actions/algorithms which can be selected based on a number of factors.
  • A Command Pattern object has a single method but with a generic signature whereas Strategy pattern may have as many different methods provided.

Design Patterns at a Glance

Design Patterns Singleton Adapter Command Strategy
Purpose To ensure a class has only one instance, and also to provide a global point of access to it To convert the interface of a class into another interface the clients expects To encapsulate method invocations so as to decouple caller from the implementation details To perform a bunch of different things to do, based on the situation/context
Advantages Sane usage of global namespace by avoiding unnecessary global variables and providing on-demand (lazy) instantiations. It lets classes work together that could not otherwise because of incompatible interfaces. It allows one to decouple the requester of an action from the object that actually performs the action. It lets the algorithms vary independently from clients that use those.
Disadvantages Introduces global state into system and complicates unit testing. Adapter has to implement the entire target interface. Selective functionality implementation is not an option. Having Command objects specific to each action ends up cluttering the design, especially in the context of MVC architectures <ref>Disadvantage of Command Pattern</ref> Increases the number of objects and all algorithms use the same interface. <ref> Disadvantages of Strategy Pattern</ref>

Conclusion

  • The objective of the Singleton pattern as defined in Design Patterns is to "ensure a class has only one instance, and also to provide a global point of access to it".<ref>Why or Why not Singleton</ref>
  • The Adapter Pattern converts the interface of a class into another interface the clients expects. This decouples the client and adaptee and allows them to work together.
  • Command Pattern encapsulates method invocations, by which pieces of computation are crystallized, hence the object invoking the computation doesn't need to worry about how things are done.
  • Strategy pattern should be used when you have a bunch of different things to do, based on the situation/context.

References

<references/>


External Resources

  • Gamma, Erich, Richard Helm, Ralph Johnson, and John Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Web.
  • Effective Java 2nd Edition By Joshua Bloch