Friday, 19 October 2007

Strategy and Decorator Design Patterns in Java

Strategy Pattern:-
Intent:The behaviors of a class should not be inherited, instead they should be encapsulated using interfaces.

Current Problem:
As an example, consider a City class. Two possible behaviors of City are Climate,JobProspects Since Climate,JobProspects implementation behaviors change frequently between cities,a common approach is to implement these behaviors in subclasses. This approach has significant drawbacks: Climate,JobProspects behaviors must be declared in each new Computer model. This may not be a concern when there are only a small number of models, but the work of managing these behaviors increases greatly as the number of models increases, and requires code to be duplicated across models. Additionally, it is not easy to determine the exact nature of the behavior for each model without investigating the code in each.

Solution: Strategy Pattern:The strategy pattern thus uses composition instead of inheritance.This allows better decoupling between the
behavior and the class that uses the behavior. The behavior can be changed without breaking the classes that use it,and the classes can switch between behaviors by changing the specific implementation used without requiring any significant code changes. Behaviors can also be changed at run-time as well as at design-time. For instance, a City object’s JobProspects behavior can be changed from
low() to high() by changing the jobProspects member to: new High();

This gives greater flexibility in design and is in harmony with OCP (Open Closed Principle) that states classes should be open for extension but closed for modification.

Hence in a strategy pattern algorithms can be selected/swapped on-the-fly at runtime.
The strategy pattern allows the algorithms vary independently from clients using them.

Consider using the Strategy design pattern if you have one of the following situations:(Design Patterns For Dummies:Steve Holzner)
1) You have volatile code that you can separate out of your application for easy maintenance.
2) You want to avoid muddling how you handle a task by having to split implementation code over several inherited classes.
3) You want to change the algorithm you use for a task at runtime.

Decorator Pattern:-
Intent: We need a design pattern that allows new/additional behaviour to be added to an existing method of an object dynamically.

Current Problem:
In some object-oriented programming languages, classes cannot be created at runtime, and it is typically not possible to predict what combinations of extensions will be needed at design time. This would mean that a new class would have to be made for every possible combination. By contrast, decorators are objects,
created at runtime, and can be combined on a per-use basis.

Decorators are alternatives to subclassing. Subclassing adds behaviour at compile time whereas decorators provide a new behaviour at runtime.

Java stream classes are decorators

We might take a look at a basic file-reading object,an InputStream object — but there’s no buffering happening here. So we might wrap an InputStream object inside a FilterInputStream object, and then wrap
that in a BufferedInputStream object.The final wrapper, BufferedInputStream,will give us the buffering you want.

When we refer to the JavaAPI for FilterInputStream
We read:
A FilterInputStream contains some other input stream, which it uses as its basic source of data,possibly transforming the data along the way or providing additional functionality. The class FilterInputStream itself simply overrides all methods of InputStream with versions that pass all requests to the contained input stream.
Subclasses of FilterInputStream may further override some of these methods and may also provide additional methods and fields.

Below Example shown an implementation for Strategy and Decorator Pattern.


interface ProgrammerStrategy {
public String getTypeOfProgrammer();
}

abstract class Software implements ProgrammerStrategy {
public abstract String getTypeOfProgrammer();
}

class Java extends Software {
public String getTypeOfProgrammer() {
return "Java Programmer";
   }
}

abstract class JavaDecorator extends Java {
public abstract String getTypeOfProgrammer();
}

class J2eeProgrammerWrapper extends JavaDecorator {
public Java language;
public J2eeProgrammerWrapper(Software java) {
language=(Java)java;
}
public String getTypeOfProgrammer() {
return language.getTypeOfProgrammer() + "with J2ee Skills";
   }
}

class HardWare implements ProgrammerStrategy {
public String getTypeOfProgrammer() {
return "Programmer with Hardware Skills";
   }
}

public class Programmer {
public ProgrammerStrategy programmerStrategy;
public ProgrammerStrategy getProgrammerStrategy() {
return programmerStrategy;
}
public void setProgrammerStrategy(ProgrammerStrategy programmerStrategy) {
this.programmerStrategy = programmerStrategy;
   }
}

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

Programmer nosw=new Programmer();

// Now a graduate has a strategy who joined IT
// and wants to learn Java.Good decision :)
Software sw=new Java();
nosw.setProgrammerStrategy(sw);

// So Now programmer is satisfied with core java skills.
System.out.println(nosw.getProgrammerStrategy().getTypeOfProgrammer());

// Now Programmer wants to decorate his IT skills and
// wants to expertise in J2ee Skills also.
J2eeProgrammerWrapper jwd=new J2eeProgrammerWrapper(sw);
nosw.setProgrammerStrategy(jwd);

// Now Programmer is infact a J2ee Skilled Programmer
System.out.println(nosw.getProgrammerStrategy().getTypeOfProgrammer());

   }
}




Hope this explanation helps.

No comments: