There is already enough literature on patterns all around, so I will not be repeating the same. Also this article will assume a certain level of prior knowledge of pattern in terms of how they work. The focus will be more on the intent of pattern and usage of pattern in real life scenarios here. It's important to understand the intent of pattern as this basically governs the need of pattern. Many patterns look similar but the intent makes the solution different. For example, Adapter and Decorator can be seen as kind of Proxies where the final implementation is hidden from the caller. However the intent will differentiate the subtle differences in the solution.
Another aspect to be considered while looking into pattern is to understand the moving and fixed part of application. This is a rather important way to look into application while designing or refactoring. A clear idea of fixed and moving part would help in isolating those pieces from each other in such a way that the changes in the moving part does not cascades the changes to the other part of system.
Context is an important aspect in understanding patterns. Context is the ecosystem in which the objects work. In a switch case statement, the context is simply the case passed. In managed environment, context is defined by the containers.
Patterns are solution to certain class of problem. Traditionally patterns has been classified in three broad categories:
- Creational
- Structural
- Behavioral
Creational
- Factory - Factory is about hiding the object construction. A way to create object in the application, wherever the object is needed is to call the new operator. However if the way the object creation changes because of a new subclass or because of introduction of new argument, would result in cascading the changes to the whole application. So the moving part here is the creation of object. So let's hide it into a class, and this class is called Factory. Whenever the object is needed call a method of the factory and it will return the created object back. Factory is quite prevalent in frameworks. Dependency Injection containers including Spring are build around this notion of factory.
- Abstract Factory - Abstract factory is about bringing one more level of indirection. This is useful when a family of objects need to be created as per the context. The factory itself is abstracted out. The usage of actual implementation of factory decides the family of objects that will get created. The caller still works on the interfaces and behaviour defined in them. For example doing a JDBC connection to a database, the Connection, Statement objects etc are a set of related objects which are governed by the database we are talking too. The factory that will work down below will depend on the driver for the database. The whole family of objects can be changed by just changing the driver class.
- Singleton - Singleton can be seen as a special type of factory. The intent here is that only one object of a particular type should be allowed to created. Servlet object are singleton and are managed by the servlet container. With Singleton take care of state as the state in a singleton is shared among the callers. A slight variation to it is to fix the number of objects of a certain type, e.g. a pool of connections to a database.
- Prototype Pattern - Prototype is like copy constructor (those coming from C++ background). The simple job of prototype is to clone itself. For example if a caller needs a copy of object, it would require a complete understanding of the object. In place of that, encapsulate the copying/cloning logic in the object itself and than ask the object to give back the clone. In future if the copying logic changes, it will still be encapsulated inside the object.
- Builder - Builder is used when an object is created in multiple steps. The actual object created will be different based on context but the steps remain same. A way to handle this is that the caller should call the facade (also known as director) and the facade captures the logic of object creation. The actual object to be created is governed by the need of caller or from the context.
Structural
- Proxy - I will start with Proxy as some other patterns can be considered as special case of proxy. Proxy acts as placeholder to the actual implementation. The need of proxy arises because the actual implementation may not be available immediately or it's just too costly to build the actual implementation unless it is not needed or the framework provide proxies to hide the complex underlying details (smells like facade here). Proxies are very common for remote calls.
- Adapter - Adapters can again be considered as special case of proxy. The adapter sits between the caller and actual implementation by providing an interface to caller which caller understands and calling the actual implementation in it. It's simply a signature matching by the adapter for the caller.
- Decorator- Decorator can be considered another special case of proxy, where extra functionality is added to the actual implementation. Pretty much similar to adapter. In adapter the intent is to provide an interface which caller can understand. In Decorator the interface is there which caller understands but requires extra functionality to be attached.
- Facade - Facade I would again like to put into proxy kind of pattern. Here the complexity of dealing with interactions with multiple objects is hidden behind a facade. The facade acts a proxy to those set of method calls.
- Composite - To build the structure in such a way that the caller can operate on a set of objects.
- Bridge - To decouple the contract to caller from the implementation. For example we might want to build a stack. So to the outside world we will provide the contract in terms of push and pop. However on the implementation side we might use a LinkedList or an Array to build it.
- Flyweight - This is to reduce the number of objects. These are similar to tables with master data in the database. The common, constant and repeating pieces of an object state are capture in another class and than only one object of that can referred by multiple objects.
Behavioral
- Chain of responsibility - Think of it like a list of objects and the request is passed to each object in sequence till one of them handles. Another good example is an exception which is thrown up in the call stack till someone catches it. If non catches than JVM does the job.
- Command - Make an object out of request parameters and pass it.
- Interpreter - Used for handling language grammer and interpreting it.
- Iterator - A plain iterator as we understand which can iterate over a collection.
- Mediator - Putting one more layer of indirection between interacting object so they do not talk to each directly.
- Memento - To maintain the state of an object at a particular point in time. Can be used in building undo operation. A better way to do undo in java is probably serializing the object at an undo defined point.
- Observer Pattern - Typical callback mechanism. Register the callback and it will be called when an event occurs. Listeners in Servlets are observers.
- State - I would consider this more as a smarter facade and the logic of if else is moved to different classes.
- Strategy - I would say, a specific case of State. In State we have one more level of indirection.
- Template - A little different variation to Strategy. Only thing is that we have to something common as pre and post to our strategy. So common pre and post processing are captured in the base class and the call in the middle going to the particular implementation.
- Visitor Pattern - Frankly this looks to be a real pattern. It solves the problem of double dispatching in Java in a nice way. When we have an object hierarchy and a set of functions to operate on the object, than Visitor solves it.
No comments:
Post a Comment