Google

May 21, 2013

Java Coding Interview Questions on decorator and composition design pattern


Q. When would you use a decorator design pattern?
A. The Decorator pattern should be used when:
  •     Object responsibilities and behaviors should be dynamically modifiable
  •     Concrete implementations should be decoupled from responsibilities and behaviors

Q. Can you write a class using the decorator design pattern to print numbers from 1-10, and then decorators that optionally print only even or odd numbers?
A. This can be done by sub classing or via inheritance. But too much sub classing is definitely a bad thing. Composition is more powerful than sub classing as you can get different behaviors via decorating at run time. Here is the code, you will realize the power of object composition and why GoF design patterns favors composition to inheritance.


Step 1: Define the interface class.

package com.arul;

public interface NextNumber
{
    abstract int getNextNumber();
}


Step 2: Define the implementation classes. The class that gets the numbers.

package com.arul;

public class PrintNumbers implements NextNumber
{
    protected int num;
    
    public PrintNumbers(int num)
    {
        this.num = num;
    }
    
    @Override
    public int getNextNumber()
    {
        return ++num; // incremented, assigned, and then returned
    }
    
}

Step 3: The class that gets the odd numbers.

package com.arul;

public class PrintOddNumbers implements NextNumber
{
    
    protected final NextNumber next;
    
    public PrintOddNumbers(NextNumber next)
    {
        if (next instanceof PrintEvenNumbers)
        {
            throw new IllegalArgumentException("Cannot be decorated with " + PrintEvenNumbers.class);
        }
        this.next = next;
        
    }
    
    @Override
    public int getNextNumber()
    {
        int num = -1;
        
        if (next != null)
        {
            
            num = next.getNextNumber();
            //keep getting the next number until it is odd
            while (num % 2 == 0)
            {
                num = next.getNextNumber();
            }
        }
        
        return num;
    }
    
}


Step 4: The class that gets the even numbers

package com.arul;

public class PrintOddNumbers implements NextNumber
{
    
    protected final NextNumber next;
    
    public PrintOddNumbers(NextNumber next)
    {
        if (next instanceof PrintEvenNumbers)
        {
            throw new IllegalArgumentException("Cannot be decorated with " + PrintEvenNumbers.class);
        }
        this.next = next;
        
    }
    
    @Override
    public int getNextNumber()
    {
        int num = -1;
        
        if (next != null)
        {
            
            num = next.getNextNumber();
            //keep getting the next number until it is odd
            while (num % 2 == 0)
            {
                num = next.getNextNumber();
            }
        }
        
        return num;
    }
    
}

Step 5: The class that gets the multiples of 3s

package com.arul;

public class PrintMultipleOfThreeNumbers implements NextNumber
{
    
    protected final NextNumber next;
    
    public PrintMultipleOfThreeNumbers(NextNumber next)
    {
        this.next = next;
    }
    
    @Override
    public int getNextNumber()
    {
        int num = -1;
        
        if (next != null)
        {
            
            num = next.getNextNumber();
            //keep getting the next number until it is odd
            while (num % 3 != 0)
            {
                num = next.getNextNumber();
            }
        }
        
        return num;
    }
    
}






Step 6:  Finally, a  sample file that shows how the above classes can be decorated at run time using object composition to get different outcomes. Additional  implementations of NextNumber  like PrintPrimeNumbers, PrintMultiplesOfSevenPrintFibonacciNumber, etc can be added using the Open-Closed design principle.

package com.arul;

public class TestNumbersWithDecorators
{
    public static void main(String[] args)
    {
        
        //without decorators
        PrintNumbers pn = new PrintNumbers(0);
        for (int i = 0; i < 10; i++)
        {
            System.out.print(pn.getNextNumber() + " "); // print next 10 numbers
        }
        
        System.out.println();
        
        PrintNumbers pn2 = new PrintNumbers(0);
        //print odd numbers with decorators
        PrintOddNumbers pOdd = new PrintOddNumbers(pn2); // decorates pn2
        for (int i = 0; i < 10; i++)
        {
            System.out.print(pOdd.getNextNumber() + " "); //print next 10 odd numbers
        }
        
        System.out.println();
        
        PrintNumbers pn3 = new PrintNumbers(0);
        //print even numbers with decorators
        PrintEvenNumbers pEven = new PrintEvenNumbers(pn3); // decorates pn3
        for (int i = 0; i < 10; i++)
        {
            System.out.print(pEven.getNextNumber() + " "); //print next 10 even numbers
        }
        
        System.out.println("");
        
        PrintNumbers pn4 = new PrintNumbers(0);
        //print odd numbers with decorators
        PrintOddNumbers pOdd2 = new PrintOddNumbers(pn4); // decorates pn4
        //print multiples of 3 with decorators
        PrintMultipleOfThreeNumbers threes = new PrintMultipleOfThreeNumbers(pOdd2); // decorates pOdd2
        for (int i = 0; i < 10; i++)
        {
            System.out.print(threes.getNextNumber() + " "); // print next 10 odd numbers
                                                            // that are multiple of threes
        }
        
        System.out.println("");
        
        PrintNumbers pn5 = new PrintNumbers(0);
        //print even numbers with decorators
        PrintEvenNumbers pEven2 = new PrintEvenNumbers(pn5); // decorates pn5
        //print multiples of 3 with decorators
        PrintMultipleOfThreeNumbers threes2 = new PrintMultipleOfThreeNumbers(pEven2); // decorates pEven2
        
        for (int i = 0; i < 10; i++)
        {
            System.out.print(threes2.getNextNumber() + " ");  // print next 10 even numbers
                                                             // that are multiple of threes
        }
        
        System.out.println("");
        
        PrintNumbers pn6 = new PrintNumbers(0);
        //print multiples of 3 with decorators
        PrintMultipleOfThreeNumbers threes3 = new PrintMultipleOfThreeNumbers(pn6); // decorates pn6
        //print even numbers with decorators
        PrintEvenNumbers pEven3 = new PrintEvenNumbers(threes3); // decorates threes3
        
        for (int i = 0; i < 10; i++)
        {
            System.out.print(pEven3.getNextNumber() + " ");  // print next 10 multiple of threes
                                                            // that are even numbers
        }
        
    }
}

The output of running the above class is

1 2 3 4 5 6 7 8 9 10 
1 3 5 7 9 11 13 15 17 19 
2 4 6 8 10 12 14 16 18 20 
3 9 15 21 27 33 39 45 51 57 
6 12 18 24 30 36 42 48 54 60 
6 12 18 24 30 36 42 48 54 60 


Labels: , ,

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home