Monday, January 18, 2010

Day 10

Today was all about optimizing the paint method for the TextInputPanel. I spent awhile trying out a few different strategies, picking and choosing things that worked and that I liked, and finally decided on one. It ended up being a combination of a Command Pattern, and another strategy that is similar to a Facade Pattern, but is lacking in the logic.

I made a TextPanelPainter interface with an abstract paint(Graphics2D g2) method. The subclasses would be a variety of single purpose painters like the TextPanelCursorPainter, or SelectionPainter, which would use an instance of the TextModel to change some data and draw the appropriate graphics. This is the Command Pattern portion.

In order to avoid having to create a new instance of say the CursorPainter every time the cursor had to be redrawn, I created a PainterStore class. The PainterStore would, upon initialization, make and save an instance of all the TextPanelPainters, as well as contain getters for each of them. This way, everytime I needed a CursorPainter, I would ask my PainterStore instance for one. This would be a Facade, if I gave the store all the logic, asked it for the painter I need, and then it spat out the appropriate painter based on the situation. I may switch to the Facade Pattern if I find an easy way to implement it.

The TextInputPanel would have a single painter reference, and anytime the panel was marked as dirty, it would called paint() on the painter. The painter would contain a reference to the paint job needed to be performed. For example, if a selection was being made and the user was holding shift and pressing an arrow key, then a SelectionPainter would get placed in the painter reference. When the key was finished being processed and the panel marked dirty, Limelight would then tell the panel to repaint, at which point the painter would call paint() and the SelectionPainter's paint method would update the selection instead of having to repaint absolutely everything.

Certain modification will be needed because when a region gets marked as dirty, it gets cleared and has to be completely repainted. The concept should still work though.

Design Patterns:

*Facade Pattern: The Facade Pattern can be used to create a single unified interface to a batch of classes that the client wishes to use.
This is useful when there are two separate groups of classes which need to interact, but you want to avoid having a web of dependancies. Instead of trying to mix and match the classes of the two groups, you can instead create a Facade which can sum up one of the two batches in a single easy to use interface.
This is done by creating a Facade class which know about all the classes of one of the two groups. Next, several methods are added to the Facade which, using all of the classes of the one group, sum up the functionality of the full subsystem. The second group can then call these methods on the Facade and use the full functionality of the subsystem, without ever having to know about any of the subsystem's classes.
An example of this would be if I had a textbox that needed to be constantly repainted for various reasons and there were a flurry of painter classes which could achieve this functionality. If I didn't want the textbox to have to decide which of the many painters I wanted it to use, but instead wanted the textbox to just call a few paint methods from one class, I could use a Facade. I would create a PainterFacade, that used all of the painter classes to create a few simple paint methods, or even just one paint method, for the text box. This paint method would then look at the current text box state, and decide which of the painters was needed, and what it had to do.

State/Objects for States Pattern: The State Pattern can be used to give a class a variety of different behaviors using the same methods, dependent on the state.
This can be useful if you have a situation in which you want all the operations for a task to occur in one class, but the operations depend on the state of an external source. If for example you have a robotic dog that can take it self outside for a walk for a... oil change I suppose. This robotic dog has some commands that say 'go outside', 'change oil', and 'come inside'. These commands depend on the weather or the planet the robodog is on. Thus in order to retain the same commands for different states, the robodog must implement the State Pattern, so that when it tries to moon walk it doesn't bring an umbrella instead of the Billy boots.

To implement this, you must create some class for the Context, an interface or abstract class for the State, along with the need state subclasses, and have the Context hold a reference to the State. Each of the State subclasses will implement the same set of methods in different ways, for the Context to use. The Context can then call the same method on its State reference, and depending on which state it holds at a given time, it will get different results.

Say our robodog has the some State class with the 'go outside' method implemented differently for sunny days and rainy days. It also has some Context class which holds a daily routine for the robodog. The Context says that at 2 pm each day, robodog must 'go outside'. The Context holds it State reference, and the state changes automatically based on the weather. This way, come 2 pm when the Context tells robodog to 'go outside' by calling the appropriate method on the State reference, robodog will know to bring an umbrella for a rainy state and sunglasses for a sunny state.

No comments:

Post a Comment