P-Patterns!
From Andrey
Revision as of 20:20, 21 October 2011 Andrey (Talk | contribs) Smart Button ← Previous diff |
Current revision Andrey (Talk | contribs) |
||
Line 1: | Line 1: | ||
- | Some stuff that I came up with. | + | == P-Patterns == |
- | == Object Role == | + | Some design patterns I came up with. |
- | Object role vs. Object class | + | == Smart Button == |
- | == Golden Bridge == | + | Suppose you bought a futuristic DVD player - a slick black box with a single tiny hole for the microphone. It does amazing job of recognizing voice commands. To open the drive bay you would say 'Open the drive'. To pause, you would say 'Do pause', and so on. You liked it in the beginning, but unfortunately, you have to always consult the product manual, because you have to memorize exact commands. For example, one day you might say 'Pause' instead of 'Do pause', and it wouldn't react. Finally, you start wanting a more traditional interface - the one with the buttons, so to operate it you wouldn't have to remember commands. With several buttons, you have complete control over the device and do not have to consult the manual! |
- | Utilizing the Bridge design pattern in order to separate model from application-specific behaviors. | + | The design pattern is: |
- | == X class == | + | Expose a generic object in the interface in order to simplify class use. |
- | Divide model classes into pure and application-specific classes, in the model, use only pure classes, but in application, instantiate only application-specific classes. | + | This is the case for exposing an object in the interface. I know that there is opposition for doing so, and even a law ([http://www.ccs.neu.edu/home/lieber/LoD.html Law of Demeter]). However, I choose to say that exposing an object that belongs to a generic, well-known class (in the above case, a button) in the interface provides for simpler, more intuitive designs. Compare this to hiding everything under a monolith interface with a laundry list of methods. |
- | == Smart Button == | + | Not being entirely opposed to the Law of Demeter, however I often felt that it stems from assumption that we couldn't fully control an object after exposing it in the interface. While it is true in some OO languages, there are designs and approaches that provide for such control ([[Overriding_in_Owner_Class |Example]]). In the presence of such mechanisms, the Law of Demeter may be less crucial. |
- | ::Suppose I bought this DVD player which was a slick black box with a tiny hole of a microphone. It did an amazing job of hearing voice commands and recognizing them. For example, to open drive bay I would say 'Open drive'. To make a pause, I would say 'Do pause' and so on. I like it in the beginning but there was one thing. I had to always consult the product manual because I kept forgetting commands. For example, I would say 'Make pause' instead of 'Do pause', and it wouldn't do anything, because it wouldn't understand. Finally, I started craving for a normal DVD player. The one that would have buttons and a remote control, so to operate it I wouldn't have to recall commands. In the end, I took the DVD player back to store and exchanged it for a conventional one. With a several buttons, I had a complete control and trashed that page with fine-print command printout, wrapped in a transluscent sleeve. | + | On the other hand, I'm definetely not advocating for breaking encapsulation and exposing inner objects in class interface. In our example with a DVD player, it only would make sense to expose some objects (buttons, indicators) while keeping others - the one that really do the job - encapsulated. Therefore, more precisely, we want to only expose very generic objects providing for intuitive control, while keeping implementation-specific parts still hidden inside the class. |
- | Expose a generic object in the interface in order to simplify class use. | + | == Postponed Validation == |
- | This is my case for exposing an object in interface. I know that there is opposition for doing so, and even a law ([http://www.ccs.neu.edu/home/lieber/LoD.html Law of Demeter]). However, I dare to say that exposure of an object of a generic, well-known class in interface provides for simpler, more intuitive designs that hiding everything under monolite interface facade with numerous methods. If the list of class's methods reads like that fine printed command reference of that DVD player, I would like to return it back to store. | + | Let's say we have a setProperty() method which changes the value of an object field. One typically validates method arguments within in the body of the method; and sometimes you need more extensive validations. Extra validations may be taxing on performance, especially when the method is called multiple times. In the Postponed Validation pattern, you perform extra validations in the time of a commit. If a validation rule is broken, perform a rollback. Postponing validations until commit lets you ensure data integrity while maintaining high performance. |
- | I'm not entirely opposed to the Law of Demeter, however I always felt that it stems from the assumption that exposing an object results in loss of control on what actions are performed on the exposed object. While it is true in current OO languages, there may be designs and approaches that provide for such control ([[Overriding_in_Owner_Class |Example]]). In the presence of such mechanisms, obeying the Law of Demeter may be less critical. | + | Postpone object validation until a checkpoint/commit, so that validations do not hinder performance. |
- | I'm also not entirely for always trying to export an object in interface. For example, in the case of DVD player it makes sense to export some (buttons, light indicators) while keeping other under the hood (transformers, electric motors, microprocessors). Therefore, more precisly, we want to export some wery general objects (button controls, status strings), while keeping other parts hidden. | + | One way to implement postponed validation is '''Detached Validation'''. Create a flag in your class to switch validations on and off, and perform a full validation each time the flag is turned on. While in the 'detached' state (flag is off), skip validations thus improving performance. Enforce each object to be back to its non-detached (flag on) state before a commit/checkpoint. |
- | == Postponed Validation == | + | == Static resolution == |
+ | |||
+ | Hide a class behind a static interface to cut on code verbosity and improve readability. As an alternative to creating a Singleton and retrieving it through getInstance() method, create an extra class that implements same interface through static methods, and provide one instance of your Singleton as a work horse for that class. The client code which looked like | ||
+ | |||
+ | Logging logging = Logging.getInstance(); | ||
+ | logging.error("my message"); | ||
+ | |||
+ | becomes this: | ||
- | Postpone object validation until checkpoint/commit so that validations do not hinder system performance. | + | Log.error("my message"); |
- | Example: say you have a setProperty() method which changes the value of an object field. You can validate the method arguments right in the the body of the method; but sometimes you also need to do a longer-type validation, e.g. if your object is a compount object. These validations may be sometimes taxing in processing-intensive scenarios, so what you can do instead is move some validations to the time of a commit, and if any validation breaks, rollback transaction. This lets you maintain data integrity without sacrificing high performance. | + | where Log is a static interface now serving as a facade for the Logging class. Bonus: you get a better English capitalization this way! |
- | == Detached Validation == | + | == Pure Business Objects == |
- | Provide each object with an alternate 'detached' state. In detached state, skip any validations improving performance. Validate object when it changes state from detached to normal (attached). Enforce that each object is in normal (attached) state on commit/checkpoint. | + | Divide model classes into pure business objects and their application-specific implementations related through inheritance. In the client code, only deal with pure business object classes. In instantiating/factory/injection code, only instantiate their application-specific implementations. Utilize Bridge design pattern to decouple inheritance graphs of the two hierarchies, thus separating the model from application-specific behavior. |
Current revision
Contents |
P-Patterns
Some design patterns I came up with.
Smart Button
Suppose you bought a futuristic DVD player - a slick black box with a single tiny hole for the microphone. It does amazing job of recognizing voice commands. To open the drive bay you would say 'Open the drive'. To pause, you would say 'Do pause', and so on. You liked it in the beginning, but unfortunately, you have to always consult the product manual, because you have to memorize exact commands. For example, one day you might say 'Pause' instead of 'Do pause', and it wouldn't react. Finally, you start wanting a more traditional interface - the one with the buttons, so to operate it you wouldn't have to remember commands. With several buttons, you have complete control over the device and do not have to consult the manual!
The design pattern is:
Expose a generic object in the interface in order to simplify class use.
This is the case for exposing an object in the interface. I know that there is opposition for doing so, and even a law (Law of Demeter). However, I choose to say that exposing an object that belongs to a generic, well-known class (in the above case, a button) in the interface provides for simpler, more intuitive designs. Compare this to hiding everything under a monolith interface with a laundry list of methods.
Not being entirely opposed to the Law of Demeter, however I often felt that it stems from assumption that we couldn't fully control an object after exposing it in the interface. While it is true in some OO languages, there are designs and approaches that provide for such control (Example). In the presence of such mechanisms, the Law of Demeter may be less crucial.
On the other hand, I'm definetely not advocating for breaking encapsulation and exposing inner objects in class interface. In our example with a DVD player, it only would make sense to expose some objects (buttons, indicators) while keeping others - the one that really do the job - encapsulated. Therefore, more precisely, we want to only expose very generic objects providing for intuitive control, while keeping implementation-specific parts still hidden inside the class.
Postponed Validation
Let's say we have a setProperty() method which changes the value of an object field. One typically validates method arguments within in the body of the method; and sometimes you need more extensive validations. Extra validations may be taxing on performance, especially when the method is called multiple times. In the Postponed Validation pattern, you perform extra validations in the time of a commit. If a validation rule is broken, perform a rollback. Postponing validations until commit lets you ensure data integrity while maintaining high performance.
Postpone object validation until a checkpoint/commit, so that validations do not hinder performance.
One way to implement postponed validation is Detached Validation. Create a flag in your class to switch validations on and off, and perform a full validation each time the flag is turned on. While in the 'detached' state (flag is off), skip validations thus improving performance. Enforce each object to be back to its non-detached (flag on) state before a commit/checkpoint.
Static resolution
Hide a class behind a static interface to cut on code verbosity and improve readability. As an alternative to creating a Singleton and retrieving it through getInstance() method, create an extra class that implements same interface through static methods, and provide one instance of your Singleton as a work horse for that class. The client code which looked like
Logging logging = Logging.getInstance(); logging.error("my message");
becomes this:
Log.error("my message");
where Log is a static interface now serving as a facade for the Logging class. Bonus: you get a better English capitalization this way!
Pure Business Objects
Divide model classes into pure business objects and their application-specific implementations related through inheritance. In the client code, only deal with pure business object classes. In instantiating/factory/injection code, only instantiate their application-specific implementations. Utilize Bridge design pattern to decouple inheritance graphs of the two hierarchies, thus separating the model from application-specific behavior.